Files
paddlepaddle--paddle/test/legacy_test/test_lu_solve_op.py
T
2026-07-13 12:40:42 +08:00

439 lines
13 KiB
Python

# Copyright (c) 2025 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 sys
import unittest
import numpy as np
import paddle
from paddle import base
sys.path.append("..")
from op_test import OpTest, get_places
def _transpose_last_2dim(x):
"""transpose the last 2 dimension of a tensor"""
x_new_dims = list(range(len(x.shape)))
x_new_dims[-1], x_new_dims[-2] = x_new_dims[-2], x_new_dims[-1]
x = paddle.transpose(x, x_new_dims)
return x
def get_inandout(A_shape, b_shape, trans="N", dtype="float64"):
paddle.disable_static(base.CPUPlace())
np.random.seed(2025)
A = np.random.random(A_shape).astype(dtype)
b = np.random.random(b_shape).astype(dtype)
x_grad_np = np.random.random(b_shape).astype(dtype)
if 'complex' in dtype:
A += 1j * np.random.random(A_shape).astype(dtype)
b += 1j * np.random.random(b_shape).astype(dtype)
x_grad_np += 1j * np.random.random(b_shape).astype(dtype)
x_grad = paddle.to_tensor(x_grad_np)
paddle_A = paddle.to_tensor(A)
lu, pivots = paddle.linalg.lu(paddle_A)
if trans == "N": # Ax = b
out = np.linalg.solve(A, b)
temp_A = np.swapaxes(A, -2, -1)
b_grad = np.linalg.solve(temp_A, x_grad)
_, L, U = paddle.linalg.lu_unpack(lu, pivots, True, False)
U_mH = _transpose_last_2dim(paddle.conj(U))
gR = paddle.linalg.triangular_solve(
U_mH,
paddle.mm(
-x_grad,
_transpose_last_2dim(paddle.conj(paddle.to_tensor(out))),
),
False,
False,
False,
)
gL = paddle.linalg.triangular_solve(
_transpose_last_2dim(paddle.conj(L)),
paddle.mm(gR, U_mH),
True,
False,
True,
)
lu_grad = (paddle.tril(gL, -1) + paddle.triu(gR, 0)).numpy()
elif trans == "T": # A^Tx = b
temp_A = np.swapaxes(A, -2, -1)
out = np.linalg.solve(temp_A, b)
b_grad = np.linalg.solve(A, x_grad)
P, L, U = paddle.linalg.lu_unpack(lu, pivots, True, True)
gR = paddle.mm(-_transpose_last_2dim(P), paddle.to_tensor(out))
gR = paddle.mm(gR, _transpose_last_2dim(paddle.conj(x_grad)))
gR = paddle.mm(gR, P)
L_mH = _transpose_last_2dim(paddle.conj(L))
gR = paddle.linalg.triangular_solve(L_mH, gR, True, True, True)
gU = paddle.linalg.triangular_solve(
_transpose_last_2dim(paddle.conj(U)),
paddle.mm(L_mH, gR),
False,
True,
False,
)
lu_grad = (paddle.tril(gR, -1) + paddle.triu(gU, 0)).numpy()
lu = lu.numpy()
pivots = pivots.numpy()
x_grad = x_grad.numpy()
paddle.enable_static()
return lu, pivots, b, out, x_grad, b_grad, lu_grad
class TestLuSolveOp(OpTest):
def setUp(self):
self.python_api = paddle.linalg.lu_solve
self.op_type = "lu_solve"
self.init_value()
(
self.LU,
self.pivots,
self.b,
self.out,
self.x_grad,
self.b_grad,
self.lu_grad,
) = get_inandout(self.A_shape, self.b_shape, self.trans, self.dtype)
self.inputs = {
'b': self.b,
'lu': self.LU,
'pivots': self.pivots,
}
self.attrs = {'trans': self.trans}
self.outputs = {'out': self.out}
def init_value(self):
self.A_shape = [2, 10, 10]
self.b_shape = [2, 10, 5]
self.trans = "N"
self.dtype = "float64"
def test_check_output(self):
paddle.enable_static()
self.check_output(check_pir=True)
paddle.disable_static()
def test_check_grad(self):
paddle.enable_static()
self.check_grad(
['b', 'lu'],
'out',
no_grad_set=['pivots'],
user_defined_grads=[self.b_grad, self.lu_grad],
user_defined_grad_outputs=[self.x_grad],
check_pir=True,
)
paddle.disable_static()
class TestLuSolveOp1(TestLuSolveOp):
def init_value(self):
self.A_shape = [2, 10, 10]
self.b_shape = [2, 10, 5]
self.trans = "T"
self.dtype = "float64"
class TestLuSolveOp2(TestLuSolveOp):
def init_value(self):
self.A_shape = [2, 2, 10, 10]
self.b_shape = [2, 2, 10, 5]
self.trans = "T"
self.dtype = "float64"
class TestLuSolveOp3(TestLuSolveOp):
def init_value(self):
self.A_shape = [2, 2, 10, 10]
self.b_shape = [2, 2, 10, 5]
self.trans = "N"
self.dtype = "float64"
class TestLuSolveOp4(TestLuSolveOp):
def init_value(self):
self.A_shape = [10, 10]
self.b_shape = [10, 10]
self.trans = "T"
self.dtype = "float64"
class TestLuSolveOp5(TestLuSolveOp):
def init_value(self):
self.A_shape = [10, 10]
self.b_shape = [10, 10]
self.trans = "N"
self.dtype = "float64"
# complex64
@unittest.skipIf(
base.core.is_compiled_with_rocm(), "Skip when compiled by ROCM."
)
class TestLuSolveOp6(TestLuSolveOp):
def init_value(self):
self.A_shape = [10, 10]
self.b_shape = [10, 10]
self.trans = "T"
self.dtype = "complex64"
# complex128
@unittest.skipIf(
base.core.is_compiled_with_rocm(), "Skip when compiled by ROCM."
)
class TestLuSolveOp7(TestLuSolveOp):
def init_value(self):
self.A_shape = [10, 10]
self.b_shape = [10, 10]
self.trans = "T"
self.dtype = "complex128"
class TestLuSolveOpAPI(unittest.TestCase):
def setUp(self):
self.init_value()
(
self.LU,
self.pivots,
self.b,
self.out,
_,
_,
_,
) = get_inandout(self.A_shape, self.b_shape, self.trans, self.dtype)
self.place = get_places()
def init_value(self):
# Ax = b
self.A_shape = [10, 10]
self.b_shape = [10, 5]
self.trans = "N"
self.dtype = "float64"
self.rtol = 1e-05
def test_dygraph(self):
def run(place):
paddle.disable_static(place)
lu = paddle.to_tensor(self.LU)
pivots = paddle.to_tensor(self.pivots)
b = paddle.to_tensor(self.b)
lu_solve_x = paddle.linalg.lu_solve(b, lu, pivots, self.trans)
np.testing.assert_allclose(
lu_solve_x.numpy(), self.out, rtol=self.rtol
)
paddle.enable_static()
for place in self.place:
run(place)
def test_static(self):
def run(place):
paddle.enable_static()
with paddle.static.program_guard(
paddle.static.Program(), paddle.static.Program()
):
b = paddle.static.data(
name='B', shape=self.b.shape, dtype=self.b.dtype
)
lu = paddle.static.data(
name='Lu', shape=self.LU.shape, dtype=self.LU.dtype
)
pivots = paddle.static.data(
name='Pivots',
shape=self.pivots.shape,
dtype=self.pivots.dtype,
)
lu_solve_x = paddle.linalg.lu_solve(b, lu, pivots, self.trans)
exe = base.Executor(place)
fetches = exe.run(
feed={
'B': self.b,
'Lu': self.LU,
'Pivots': self.pivots,
},
fetch_list=[lu_solve_x],
)
np.testing.assert_allclose(fetches[0], self.out, rtol=self.rtol)
paddle.disable_static()
for place in self.place:
run(place)
class TestLuSolveOpAPI2(TestLuSolveOpAPI):
def init_value(self):
# Ax = b
self.A_shape = [1, 10, 10]
self.b_shape = [2, 10, 5]
self.trans = "N"
self.dtype = "float64"
self.rtol = 1e-05
class TestLuSolveOpAPI3(TestLuSolveOpAPI):
def init_value(self):
# A^Tx = b
self.A_shape = [1, 10, 10]
self.b_shape = [2, 10, 5]
self.trans = "T"
self.dtype = "float64"
self.rtol = 1e-05
class TestLuSolveOpAPI4(TestLuSolveOpAPI):
def init_value(self):
# Ax = b
self.A_shape = [1, 10, 10]
self.b_shape = [2, 10, 5]
self.trans = "N"
self.dtype = "float32"
self.rtol = 0.001
class TestLuSolveOpAPI5(TestLuSolveOpAPI):
def init_value(self):
# A^Tx = b
self.A_shape = [1, 10, 10]
self.b_shape = [2, 10, 5]
self.trans = "T"
self.dtype = "float32"
self.rtol = 0.001
class TestLuSolveOpAPI6(TestLuSolveOpAPI):
def init_value(self):
# Ax = b
self.A_shape = [10, 10]
self.b_shape = [10, 5]
self.trans = "N"
self.dtype = "float32"
self.rtol = 0.001
class TestLuSolveOpAPI7(TestLuSolveOpAPI):
def init_value(self):
# A^Tx = b
self.A_shape = [10, 10]
self.b_shape = [10, 5]
self.trans = "T"
self.dtype = "float32"
self.rtol = 0.001
class TestLuSolveOpAPI8(TestLuSolveOpAPI):
def init_value(self):
# A^Tx = b
self.A_shape = [10, 10]
self.b_shape = [10, 5]
self.trans = "T"
self.dtype = "float64"
self.rtol = 1e-05
@unittest.skipIf(
base.core.is_compiled_with_rocm(), "Skip when compiled by ROCM."
)
class TestLuSolveOpAPI9(TestLuSolveOpAPI):
def init_value(self):
# Ax = b
self.A_shape = [10, 10]
self.b_shape = [10, 5]
self.trans = "N"
self.dtype = "complex64"
self.rtol = 0.001
@unittest.skipIf(
base.core.is_compiled_with_rocm(), "Skip when compiled by ROCM."
)
class TestLuSolveOpAPI10(TestLuSolveOpAPI):
def init_value(self):
# Ax = b
self.A_shape = [10, 10]
self.b_shape = [10, 5]
self.trans = "N"
self.dtype = "complex128"
self.rtol = 1e-05
class TestLSolveError(unittest.TestCase):
def test_errors(self):
with paddle.base.dygraph.guard():
# The size of b should gather than 2.
def test_b_size():
b = paddle.randn([3])
lu = paddle.randn([3, 3])
pivots = paddle.randn([3])
paddle.linalg.lu_solve(b, lu, pivots)
self.assertRaises(ValueError, test_b_size)
# The size of lu should gather than 2.
def test_lu_size():
b = paddle.randn([3, 1])
lu = paddle.randn([3])
pivots = paddle.randn([3])
paddle.linalg.lu_solve(b, lu, pivots)
self.assertRaises(ValueError, test_lu_size)
# The size of pivots should gather than 1.
def test_pivots_size():
b = paddle.randn([3, 1])
lu = paddle.randn([3, 3])
pivots = paddle.randn([])
paddle.linalg.lu_solve(b, lu, pivots)
self.assertRaises(ValueError, test_pivots_size)
# b.shape[-2] should equal to lu.shape[-2].
def test_b_lu_shape():
b = paddle.randn([1, 3])
lu = paddle.randn([3, 3])
pivots = paddle.randn([3])
paddle.linalg.lu_solve(b, lu, pivots)
self.assertRaises(ValueError, test_b_lu_shape)
# lu.shape[-1] should equal to pivots.shape[-1].
def test_b_pivots_shape():
b = paddle.randn([3, 1])
lu = paddle.randn([3, 3])
pivots = paddle.randn([2])
paddle.linalg.lu_solve(b, lu, pivots)
self.assertRaises(ValueError, test_b_pivots_shape)
# lu.shape[-2] should equal to lu.shape[-1].
def test_lu_shape():
b = paddle.randn([3, 1])
lu = paddle.randn([3, 2])
pivots = paddle.randn([3])
paddle.linalg.lu_solve(b, lu, pivots)
self.assertRaises(ValueError, test_lu_shape)
if __name__ == "__main__":
unittest.main()