chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# Copyright (c) 2022 PaddlePaddle Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import paddle
|
||||
from paddle.base.data_feeder import check_type
|
||||
from paddle.base.framework import Variable, in_pir_mode
|
||||
|
||||
|
||||
def check_input_type(input, name, op_name):
|
||||
r"""Check whether the input is tensor or variable."""
|
||||
if paddle.in_dynamic_mode():
|
||||
if not isinstance(input, paddle.Tensor):
|
||||
raise ValueError(f"The input: {input} must be tensor.")
|
||||
else:
|
||||
check_type(input, name, (Variable, paddle.pir.Value), op_name)
|
||||
|
||||
|
||||
def check_initial_inverse_hessian_estimate(H0):
|
||||
r"""Check whether the specified initial_inverse_hessian_estimate is symmetric and positive definite.
|
||||
Raise errors when precondition not met.
|
||||
|
||||
Note:
|
||||
In static graph can not raise error directly, so use py_func make raise_func as a op,
|
||||
and use paddle.static.nn.cond to decide if put the op in net.
|
||||
cholesky is the fast way to check positive definition, but in static graph can not catch
|
||||
exception to raise value error, so use eigvals rather than cholesky in static graph.
|
||||
"""
|
||||
is_symmetric = paddle.all(paddle.equal(H0, H0.t()))
|
||||
|
||||
def raise_func():
|
||||
raise ValueError(
|
||||
"The initial_inverse_hessian_estimate should be symmetric and positive definite, but the specified is not."
|
||||
)
|
||||
|
||||
if paddle.in_dynamic_mode():
|
||||
if not is_symmetric:
|
||||
raise_func()
|
||||
try:
|
||||
paddle.linalg.cholesky(H0)
|
||||
except RuntimeError as error:
|
||||
raise_func()
|
||||
elif in_pir_mode():
|
||||
paddle.static.nn.control_flow.Assert(
|
||||
is_symmetric,
|
||||
None,
|
||||
10,
|
||||
name="The initial_inverse_hessian_estimate should be symmetric and positive definite, but the specified is not.",
|
||||
)
|
||||
eigvals = paddle.linalg.eigvals(H0)
|
||||
is_positive = paddle.bitwise_and(
|
||||
paddle.all(eigvals.real() > 0.0), paddle.all(eigvals.imag() == 0.0)
|
||||
)
|
||||
paddle.static.nn.control_flow.Assert(
|
||||
is_positive,
|
||||
None,
|
||||
10,
|
||||
name="The initial_inverse_hessian_estimate should be symmetric and positive definite, but the specified is not.",
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
def create_tmp_var(program, name, dtype, shape):
|
||||
return program.current_block().create_var(
|
||||
name=name, dtype=dtype, shape=shape
|
||||
)
|
||||
|
||||
out_var = create_tmp_var(
|
||||
paddle.static.default_main_program(),
|
||||
name='output',
|
||||
dtype='float32',
|
||||
shape=[-1],
|
||||
)
|
||||
|
||||
def false_fn():
|
||||
paddle.static.nn.py_func(
|
||||
func=raise_func, x=is_symmetric, out=out_var
|
||||
)
|
||||
|
||||
paddle.static.nn.cond(is_symmetric, None, false_fn)
|
||||
# eigvals only support cpu
|
||||
paddle.set_device("cpu")
|
||||
eigvals = paddle.linalg.eigvals(H0)
|
||||
is_positive = paddle.all(eigvals.real() > 0.0) and paddle.all(
|
||||
eigvals.imag() == 0.0
|
||||
)
|
||||
paddle.static.nn.cond(is_positive, None, false_fn)
|
||||
|
||||
|
||||
def _value_and_gradient(f, x, v=None):
|
||||
r"""Compute function value and gradient of f at x.
|
||||
|
||||
Args:
|
||||
f (Callable): the objective function.
|
||||
x (Tensor): the input tensor.
|
||||
Returns:
|
||||
value: a tensor that holds the function value.
|
||||
gradient: a tensor that holds the function gradients.
|
||||
"""
|
||||
# use detach to cut off relation between x and original graph
|
||||
x = x.detach()
|
||||
x.stop_gradient = False
|
||||
value = f(x)
|
||||
if paddle.in_dynamic_mode():
|
||||
# only need to compute first order derivative, and some op dont support high order derivative.
|
||||
gradient = paddle.grad([value], [x], create_graph=False)[0]
|
||||
else:
|
||||
gradient = paddle.static.gradients([value], [x])[0]
|
||||
# use detach to make results real number without grad to avoid assign error
|
||||
return value.detach(), gradient.detach()
|
||||
Reference in New Issue
Block a user