from sympy.polys.matrices import DomainMatrix, DM from sympy.polys.domains import ZZ, QQ from sympy import Matrix import pytest FFLU_EXAMPLES = [ ( 'zz_2x3', DM([[1, 2, 3], [4, 5, 6]], ZZ), DM([[1, 0], [0, 1]], ZZ), DM([[1, 0], [4, -3]], ZZ), DM([[1, 0], [0, -3]], ZZ), DM([[1, 2, 3], [0, -3, -6]], ZZ), ), ( 'zz_2x2', DM([[4, 3], [6, 3]], ZZ), DM([[1, 0], [0, 1]], ZZ), DM([[1, 0], [6, -6]], ZZ), DM([[4, 0], [0, -3]], ZZ), DM([[4, 3], [0, -3]], ZZ), ), ( 'zz_3x2', DM([[1, 2], [3, 4], [5, 6]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[1, 0, 0], [3, 1, 0], [5, 2, 1]], ZZ), DM([[1, 0], [0, -2]], ZZ), DM([[1, 2], [0, -2], [0, 0]], ZZ), ), ( 'zz_3x3', DM([[1, 2, 3], [4, 5, 6], [7, 8, 9]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[1, 0, 0], [4, 1, 0], [7, 2, 1]], ZZ), DM([[1, 0, 0], [0, -3, 0], [0, 0, 0]], ZZ), DM([[1, 2, 3], [0, -3, -6], [0, 0, 0]], ZZ), ), ( 'zz_zero', DM([[0, 0, 0], [0, 0, 0], [0, 0, 0]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[0, 0, 0], [0, 0, 0], [0, 0, 0]], ZZ), DM([[0, 0, 0], [0, 0, 0], [0, 0, 0]], ZZ), ), ( 'zz_empty', DM([], ZZ), DM([], ZZ), DM([], ZZ), DM([], ZZ), DM([], ZZ), ), ( 'zz_empty_0x2', DomainMatrix([], (0, 2), ZZ), DomainMatrix([], (0, 0), ZZ), DomainMatrix([], (0, 0), ZZ), DomainMatrix([], (0, 0), ZZ), DomainMatrix([], (0, 2), ZZ) ), ( 'zz_empty_2x0', DomainMatrix([[], []], (2, 0), ZZ), DomainMatrix.eye((2, 2), ZZ), DomainMatrix.eye((2, 2), ZZ), DomainMatrix.eye((2, 2), ZZ), DomainMatrix([[], []], (2, 0), ZZ) ), ( 'zz_negative', DM([[-1, -2], [-3, -4]], ZZ), DM([[1, 0], [0, 1]], ZZ), DM([[-1, 0], [-3, -2]], ZZ), DM([[-1, 0], [0, 2]], ZZ), DM([[-1, -2], [0, -2]], ZZ), ), ( 'zz_mixed_signs', DM([[1, -2], [-3, 4]], ZZ), DM([[1, 0], [0, 1]], ZZ), DM([[1, 0], [-3, 1]], ZZ), DM([[1, 0], [0, -2]], ZZ), DM([[1, -2], [0, -2]], ZZ), ), ( 'zz_upper_triangular', DM([[1, 2, 3], [0, 4, 5], [0, 0, 6]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[1, 0, 0], [0, 4, 0], [0, 0, 24]], ZZ), DM([[1, 0, 0], [0, 4, 0], [0, 0, 96]], ZZ), DM([[1, 2, 3], [0, 4, 5], [0, 0, 24]], ZZ), ), ( 'zz_lower_triangular', DM([[1, 0, 0], [2, 3, 0], [4, 5, 6]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[1, 0, 0], [2, 3, 0], [4, 5, 18]], ZZ), DM([[1, 0, 0], [0, 3, 0], [0, 0, 54]], ZZ), DM([[1, 0, 0], [0, 3, 0], [0, 0, 18]], ZZ), ), ( 'zz_diagonal', DM([[2, 0, 0], [0, 3, 0], [0, 0, 4]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[2, 0, 0], [0, 6, 0], [0, 0, 24]], ZZ), DM([[2, 0, 0], [0, 12, 0], [0, 0, 144]], ZZ), DM([[2, 0, 0], [0, 6, 0], [0, 0, 24]], ZZ) ), ( 'rank_deficient_3x3', DM([[1, 2, 3], [2, 4, 6], [3, 6, 9]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[1, 0, 0], [2, 1, 0], [3, 0, 1]], ZZ), DM([[1, 0, 0], [0, 0, 0], [0, 0, 0]], ZZ), DM([[1, 2, 3], [0, 0, 0], [0, 0, 0]], ZZ), ), ( 'zz_1x1', DM([[5]], ZZ), DM([[1]], ZZ), DM([[5]], ZZ), DM([[5]], ZZ), DM([[5]], ZZ), ), ( 'zz_nx1_2rows', DM([[81], [54]], ZZ), DM([[1, 0], [0, 1]], ZZ), DM([[81, 0], [54, 81]], ZZ), DM([[81, 0], [0, 81]], ZZ), DM([[81], [0]], ZZ), ), ( 'zz_nx2_3rows', DM([[2, 7], [7, 45], [25, 84]], ZZ), DM([[1, 0, 0], [0, 1, 0], [0, 0, 1]], ZZ), DM([[2, 0, 0], [7, 82, 0], [25, 41, 41]], ZZ), DM([[2, 0, 0], [0, 82, 0], [0, 0, 41]], ZZ), DM([[2, 7], [0, 82], [0, 0]], ZZ), ), ( 'zz_1x2', DM([[0, 28]], ZZ), DM([[1]], ZZ), DM([[28]], ZZ), DM([[28]], ZZ), DM([[0, 28]], ZZ) ), ( 'zz_nx3_4rows', DM([[84, 30, 9], [20, 59, 13], [53, 46, 81], [63, 48, 29]], ZZ), DM([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [0, 0, 0, 1]], ZZ), DM([[84, 0, 0, 0], [20, 365904, 0, 0], [53, 303411, 303411, 0], [63, 303411, 303411, 303411]], ZZ), DM([[84, 0, 0, 0], [0, 365904, 0, 0], [0, 0, 1321658316, 0], [0, 0, 0, 303411]], ZZ), DM([[84, 30, 9], [0, 365904, 13], [0, 0, 1321658316], [0, 0, 0]], ZZ), ), ( 'fflu_row_swap', DM([[0, 1, 2], [3, 4, 5], [6, 7, 8]], ZZ), DM([[0, 1, 0], [1, 0, 0], [0, 0, 1]], ZZ), DM([[3, 0, 0], [0, 3, 0], [6, -3, 1]], ZZ), DM([[3, 0, 0], [0, 9, 0], [0, 0, 3]], ZZ), DM([[3, 4, 5], [0, 3, 6], [0, 0, 0]], ZZ) ), ] def _check_fflu(A, P, L, D, U): P_field = P.to_field().to_dense() L_field = L.to_field().to_dense() D_field = D.to_field().to_dense() U_field = U.to_field().to_dense() m, n = A.shape assert P_field.shape == (m, m) assert L_field.shape == (m, m) assert D_field.shape == (m, m) assert U_field.shape == (m, n) assert L_field.is_lower assert D_field.is_diagonal di, d = D.inv_den() assert P.matmul(A).rmul(d) == L.matmul(di).matmul(U) assert U_field.is_upper def _to_DM(A, ans): if isinstance(A, DomainMatrix): return A elif isinstance(A, Matrix): return A.to_DM(ans.domain) return DomainMatrix(A.to_list(), A.shape, A.domain) def _check_fflu_result(result, A, P_ans, L_ans, D_ans, U_ans): P, L, D, U = result P = _to_DM(P, P_ans) L = _to_DM(L, L_ans) D = _to_DM(D, D_ans) U = _to_DM(U, U_ans) A = _to_DM(A, P_ans) m, n = A.shape assert P.shape == (m, m) assert L.shape == (m, m) assert D.shape == (m, m) assert U.shape == (m, n) assert L.is_lower assert D.is_diagonal di, d = D.inv_den() assert P.matmul(A).rmul(d) == L.matmul(di).matmul(U) assert U.is_upper @pytest.mark.parametrize('name, A, P_ans, L_ans, D_ans, U_ans', FFLU_EXAMPLES) def test_dm_dense_fflu(name, A, P_ans, L_ans, D_ans, U_ans): A = A.to_dense() _check_fflu_result(A.fflu(), A, P_ans, L_ans, D_ans, U_ans) @pytest.mark.parametrize('name, A, P_ans, L_ans, D_ans, U_ans', FFLU_EXAMPLES) def test_dm_sparse_fflu(name, A, P_ans, L_ans, D_ans, U_ans): A = A.to_sparse() _check_fflu_result(A.fflu(), A, P_ans, L_ans, D_ans, U_ans) @pytest.mark.parametrize('name, A, P_ans, L_ans, D_ans, U_ans', FFLU_EXAMPLES) def test_ddm_fflu(name, A, P_ans, L_ans, D_ans, U_ans): A = A.to_ddm() _check_fflu_result(A.fflu(), A, P_ans, L_ans, D_ans, U_ans) @pytest.mark.parametrize('name, A, P_ans, L_ans, D_ans, U_ans', FFLU_EXAMPLES) def test_sdm_fflu(name, A, P_ans, L_ans, D_ans, U_ans): A = A.to_sdm() _check_fflu_result(A.fflu(), A, P_ans, L_ans, D_ans, U_ans) @pytest.mark.parametrize('name, A, P_ans, L_ans, D_ans, U_ans', FFLU_EXAMPLES) def test_dfm_fflu(name, A, P_ans, L_ans, D_ans, U_ans): pytest.importorskip('flint') if A.domain not in (ZZ, QQ) and not A.domain.is_FF: pytest.skip("Domain not supported by DFM") A = A.to_dfm() _check_fflu_result(A.fflu(), A, P_ans, L_ans, D_ans, U_ans) def test_fflu_empty_matrix(): A = DomainMatrix([], (0, 0), ZZ) P, L, D, U = A.fflu() assert P.shape == (0, 0) assert L.shape == (0, 0) assert D.shape == (0, 0) assert U.shape == (0, 0) def test_fflu_properties(): A = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(3), ZZ(4)]], (2, 2), ZZ) P, L, D, U = A.fflu() assert P.shape == (2, 2) assert L.shape == (2, 2) assert D.shape == (2, 2) assert U.shape == (2, 2) assert L.is_lower assert U.is_upper assert D.is_diagonal di, d = D.inv_den() assert P.matmul(A).rmul(d) == L.matmul(di).matmul(U) def test_fflu_rank_deficient(): A = DomainMatrix([[ZZ(1), ZZ(2)], [ZZ(2), ZZ(4)]], (2, 2), ZZ) P, L, D, U = A.fflu() assert P.shape == (2, 2) assert L.shape == (2, 2) assert D.shape == (2, 2) assert U.shape == (2, 2) assert U.getitem_sympy(1, 1) == 0