import numpy as np
import pytest
import scirs2
class TestBasicOperations:
def test_determinant(self):
a = np.array([[4.0, 2.0], [2.0, 3.0]])
det = scirs2.det_py(a)
assert abs(det - 8.0) < 1e-10
def test_determinant_singular(self):
a = np.array([[1.0, 2.0], [2.0, 4.0]])
det = scirs2.det_py(a)
assert abs(det) < 1e-10
def test_inverse(self):
a = np.array([[4.0, 2.0], [2.0, 3.0]])
a_inv = scirs2.inv_py(a)
identity = a @ a_inv
assert abs(identity[0, 0] - 1.0) < 1e-10
assert abs(identity[1, 1] - 1.0) < 1e-10
assert abs(identity[0, 1]) < 1e-10
assert abs(identity[1, 0]) < 1e-10
def test_trace(self):
a = np.array([[4.0, 2.0], [2.0, 3.0]])
tr = scirs2.trace_py(a)
assert abs(tr - 7.0) < 1e-10
class TestDecompositions:
def test_lu_decomposition(self):
a = np.array([[4.0, 3.0], [6.0, 3.0]])
result = scirs2.lu_py(a)
assert "p" in result
assert "l" in result
assert "u" in result
p = result["p"]
l = result["l"]
u = result["u"]
pa = p @ a
lu = l @ u
assert np.allclose(pa, lu, atol=1e-10)
def test_qr_decomposition(self):
a = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
result = scirs2.qr_py(a)
assert "q" in result
assert "r" in result
q = result["q"]
r = result["r"]
qr = q @ r
assert np.allclose(a, qr, atol=1e-10)
def test_svd_decomposition(self):
a = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
result = scirs2.svd_py(a)
assert "u" in result
assert "s" in result
assert "vt" in result
s = result["s"]
assert all(sv > 0 for sv in s)
assert all(s[i] >= s[i+1] for i in range(len(s)-1))
def test_cholesky_decomposition(self):
a = np.array([[4.0, 2.0], [2.0, 3.0]])
l = scirs2.cholesky_py(a)
reconstructed = l @ l.T
assert np.allclose(a, reconstructed, atol=1e-10)
def test_eigenvalues(self):
a = np.array([[2.0, 1.0], [1.0, 2.0]])
result = scirs2.eig_py(a)
assert "eigenvalues_real" in result
assert "eigenvalues_imag" in result
eig_real = sorted(result["eigenvalues_real"])
assert abs(eig_real[0] - 1.0) < 1e-10
assert abs(eig_real[1] - 3.0) < 1e-10
def test_symmetric_eigenvalues(self):
a = np.array([[4.0, 2.0], [2.0, 3.0]])
result = scirs2.eigh_py(a)
assert "eigenvalues" in result
assert "eigenvectors" in result
assert len(result["eigenvalues"]) == 2
class TestSolvers:
def test_solve(self):
a = np.array([[3.0, 1.0], [1.0, 2.0]])
b = np.array([9.0, 8.0])
x = scirs2.solve_py(a, b)
ax = a @ x
assert np.allclose(ax, b, atol=1e-10)
def test_lstsq(self):
a = np.array([[1.0, 1.0], [1.0, 2.0], [1.0, 3.0]])
b = np.array([1.0, 2.0, 2.0])
result = scirs2.lstsq_py(a, b)
assert "solution" in result
assert "rank" in result
assert result["rank"] == 2
class TestNorms:
def test_matrix_norm_frobenius(self):
a = np.array([[3.0, 4.0], [0.0, 0.0]])
norm = scirs2.matrix_norm_py(a, "fro")
assert abs(norm - 5.0) < 1e-10
def test_vector_norm_l2(self):
x = np.array([3.0, 4.0])
norm = scirs2.vector_norm_py(x, 2)
assert abs(norm - 5.0) < 1e-10
def test_vector_norm_l1(self):
x = np.array([3.0, -4.0])
norm = scirs2.vector_norm_py(x, 1)
assert abs(norm - 7.0) < 1e-10
def test_condition_number(self):
a = np.array([[1.0, 0.0], [0.0, 1.0]])
cond = scirs2.cond_py(a)
assert abs(cond - 1.0) < 1e-10
def test_matrix_rank(self):
a = np.array([[1.0, 2.0], [2.0, 4.0]]) rank = scirs2.matrix_rank_py(a)
assert rank == 1
class TestPseudoinverse:
def test_pinv_square_invertible(self):
a = np.array([[4.0, 2.0], [2.0, 3.0]])
a_pinv = scirs2.pinv_py(a)
a_inv = scirs2.inv_py(a)
assert np.allclose(a_pinv, a_inv, atol=1e-10)
result = a @ a_pinv @ a
assert np.allclose(result, a, atol=1e-10)
def test_pinv_singular(self):
a = np.array([[1.0, 2.0], [2.0, 4.0]]) a_pinv = scirs2.pinv_py(a)
result = a @ a_pinv @ a
assert np.allclose(result, a, atol=1e-10)
def test_pinv_rectangular_tall(self):
a = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]) a_pinv = scirs2.pinv_py(a)
assert a_pinv.shape == (2, 3)
result = a @ a_pinv @ a
assert np.allclose(result, a, atol=1e-10)
def test_pinv_rectangular_wide(self):
a = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]) a_pinv = scirs2.pinv_py(a)
assert a_pinv.shape == (3, 2)
result = a @ a_pinv @ a
assert np.allclose(result, a, atol=1e-10)
def test_pinv_diagonal(self):
a = np.array([[2.0, 0.0], [0.0, 3.0]])
a_pinv = scirs2.pinv_py(a)
expected = np.array([[0.5, 0.0], [0.0, 1.0/3.0]])
assert np.allclose(a_pinv, expected, atol=1e-10)
def test_pinv_rcond_parameter(self):
a = np.array([[3.0, 0.0], [0.0, 0.25]])
a_pinv_default = scirs2.pinv_py(a)
a_pinv_custom = scirs2.pinv_py(a, rcond=0.01)
assert a_pinv_default.shape == (2, 2)
assert a_pinv_custom.shape == (2, 2)
result = a @ a_pinv_default @ a
assert np.allclose(result, a, atol=1e-8)
def test_pinv_orthogonal_projector(self):
a = np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]])
a_pinv = scirs2.pinv_py(a)
p = a_pinv @ a
p_squared = p @ p
assert np.allclose(p_squared, p, atol=1e-10)
assert np.allclose(p, p.T, atol=1e-10)
if __name__ == "__main__":
pytest.main([__file__, "-v"])