22
33import numpy as np
44import pytest
5- from scipy .sparse import random as sparse_random
5+ import scipy .sparse
66
77import sparseqr
88
@@ -19,23 +19,23 @@ class TestComplexQR:
1919
2020 def test_complex_qr_unitary_q (self ):
2121 """Test that Q is unitary for complex matrix (Q @ Q^H = I)."""
22- A = sparse_random (m = 120 , n = 100 , density = 0.1 , dtype = np .complex128 , random_state = 42 )
22+ A = scipy . sparse . random (m = 120 , n = 100 , density = 0.1 , dtype = np .complex128 , random_state = 42 )
2323 Q , R , P , rank = sparseqr .qr (A )
2424
2525 QQH = (Q @ adj (Q )).toarray ()
2626 np .testing .assert_allclose (QQH , np .eye (Q .shape [0 ]), atol = self .ATOL )
2727
2828 def test_complex_qr_upper_triangular_r (self ):
2929 """Test that R is upper triangular for complex matrix."""
30- A = sparse_random (m = 120 , n = 100 , density = 0.1 , dtype = np .complex128 , random_state = 42 )
30+ A = scipy . sparse . random (m = 120 , n = 100 , density = 0.1 , dtype = np .complex128 , random_state = 42 )
3131 Q , R , P , rank = sparseqr .qr (A )
3232
3333 lower_triangle = np .tril (R .toarray (), k = - 1 )
3434 np .testing .assert_allclose (lower_triangle , 0 , atol = self .ATOL )
3535
3636 def test_complex_qr_reconstruction (self ):
3737 """Test that A can be reconstructed from QR decomposition: A = (QR) @ P^T."""
38- A = sparse_random (m = 120 , n = 100 , density = 0.1 , dtype = np .complex128 , random_state = 42 )
38+ A = scipy . sparse . random (m = 120 , n = 100 , density = 0.1 , dtype = np .complex128 , random_state = 42 )
3939 Q , R , P , rank = sparseqr .qr (A )
4040
4141 P_matrix = sparseqr .permutation_vector_to_matrix (P )
@@ -46,13 +46,60 @@ def test_complex_qr_reconstruction(self):
4646 def test_complex_qr_various_shapes (self , shape ):
4747 """Test complex QR on square, tall, and wide matrices."""
4848 m , n = shape
49- A = sparse_random (m = m , n = n , density = 0.1 , dtype = np .complex128 , random_state = 42 )
49+ A = scipy . sparse . random (m = m , n = n , density = 0.1 , dtype = np .complex128 , random_state = 42 )
5050 Q , R , P , rank = sparseqr .qr (A )
5151
5252 P_matrix = sparseqr .permutation_vector_to_matrix (P )
5353 A_reconstructed = (Q @ R ) @ P_matrix .T
5454 np .testing .assert_allclose (A_reconstructed .toarray (), A .toarray (), atol = self .ATOL )
5555
5656
57+ class TestComplexSolve :
58+ """Tests for solve function with complex matrices."""
59+
60+ ATOL = 1e-10
61+
62+ def test_complex_solve_exact_system (self ):
63+ """Test solving an exact complex system where Ax=b has exact solution."""
64+ A = scipy .sparse .random (m = 50 , n = 50 , density = 0.2 , dtype = np .complex128 , random_state = 42 )
65+ # Add diagonal to make it well-conditioned
66+ A = A + 5 * scipy .sparse .eye (50 , dtype = np .complex128 )
67+ x_true = np .random .RandomState (42 ).random (50 ) + 1j * np .random .RandomState (43 ).random (50 )
68+ b = A @ x_true
69+
70+ x = sparseqr .solve (A , b , tolerance = 0 )
71+
72+ assert x is not None , "solve() returned None"
73+ np .testing .assert_allclose (x , x_true , atol = self .ATOL )
74+
75+ def test_complex_solve_overdetermined_least_squares (self ):
76+ """Test least-squares solution for overdetermined complex system."""
77+ # 100 equations, 50 unknowns
78+ A = scipy .sparse .random (m = 100 , n = 50 , density = 0.2 , dtype = np .complex128 , random_state = 42 )
79+ x_true = np .random .RandomState (42 ).random (50 ) + 1j * np .random .RandomState (43 ).random (50 )
80+ b = A @ x_true
81+
82+ x = sparseqr .solve (A , b , tolerance = 0 )
83+
84+ assert x is not None , "solve() returned None"
85+ assert x .shape == (50 ,), f"Solution shape wrong: { x .shape } "
86+ # For an exact system (b in range of A), solution should match
87+ np .testing .assert_allclose (x , x_true , atol = self .ATOL )
88+
89+ def test_complex_solve_multiple_rhs (self ):
90+ """Test solving AX=B with multiple complex RHS vectors."""
91+ A = scipy .sparse .random (m = 50 , n = 50 , density = 0.2 , dtype = np .complex128 , random_state = 42 )
92+ A = A + 5 * scipy .sparse .eye (50 , dtype = np .complex128 )
93+ X_true = (np .random .RandomState (42 ).random ((50 , 3 )) +
94+ 1j * np .random .RandomState (43 ).random ((50 , 3 )))
95+ B = A @ X_true
96+
97+ X = sparseqr .solve (A , B , tolerance = 0 )
98+
99+ assert X is not None , "solve() returned None"
100+ assert X .shape == (50 , 3 ), f"Solution shape wrong: { X .shape } "
101+ np .testing .assert_allclose (X , X_true , atol = self .ATOL )
102+
103+
57104if __name__ == '__main__' :
58105 pytest .main ([__file__ , '-v' ])
0 commit comments