""" Tests for matrix operations. """ import pytest import numpy as np from src.models.matrices.matrix_ops import MatrixOps, MatrixLoader, MatrixInitializer class TestMatrixOps: """Test matrix operation utilities.""" def test_normalize_columns(self, sample_matrix_2d): """Test column normalization.""" normalized = MatrixOps.normalize_columns(sample_matrix_2d) # Check each column sums to 1 assert np.allclose(normalized.sum(axis=0), 1.0) # Check non-negativity preserved assert np.all(normalized >= 0) def test_normalize_rows(self, sample_matrix_2d): """Test row normalization.""" normalized = MatrixOps.normalize_rows(sample_matrix_2d) # Check each row sums to 1 assert np.allclose(normalized.sum(axis=1), 1.0) # Check non-negativity preserved assert np.all(normalized >= 0) def test_ensure_probability_distribution(self): """Test probability distribution enforcement.""" # Test with negative values matrix = np.array([[-1, 0.5], [2, 0.5]]) prob_dist = MatrixOps.ensure_probability_distribution(matrix) assert np.all(prob_dist >= 0) assert np.allclose(prob_dist.sum(axis=0), 1.0) def test_compute_entropy(self, sample_belief_vector): """Test entropy computation.""" entropy = MatrixOps.compute_entropy(sample_belief_vector) # Entropy should be non-negative assert entropy >= 0 # Test with deterministic distribution deterministic = np.array([1.0, 0.0, 0.0]) assert MatrixOps.compute_entropy(deterministic) == 0 def test_compute_kl_divergence(self): """Test KL divergence computation.""" P = np.array([0.5, 0.5]) Q = np.array([0.9, 0.1]) kl = MatrixOps.compute_kl_divergence(P, Q) # KL divergence should be non-negative assert kl >= 0 # KL divergence should be zero for identical distributions assert MatrixOps.compute_kl_divergence(P, P) == 0 def test_softmax(self): """Test softmax computation.""" x = np.array([1.0, 2.0, 3.0]) probs = MatrixOps.softmax(x) # Check output is probability distribution assert np.allclose(probs.sum(), 1.0) assert np.all(probs >= 0) # Check ordering preserved assert np.all(np.diff(probs) > 0) class TestMatrixLoader: """Test matrix loading utilities.""" def test_load_spec(self, sample_markdown_spec): """Test loading matrix specification from markdown.""" spec = MatrixLoader.load_spec(sample_markdown_spec) assert spec['type'] == 'matrix_spec' assert spec['dimensions']['rows'] == 3 assert 'sum(cols) == 1.0' in spec['shape_constraints'] def test_load_matrix(self, sample_matrix_data): """Test loading matrix data from file.""" matrix = MatrixLoader.load_matrix(sample_matrix_data) assert matrix.shape == (3, 3) assert np.allclose(matrix.sum(axis=0), 1.0) def test_validate_matrix(self, sample_matrix_2d, sample_matrix_spec): """Test matrix validation against specification.""" assert MatrixLoader.validate_matrix(sample_matrix_2d, sample_matrix_spec) # Test invalid matrix invalid_matrix = np.array([[1.1, -0.1], [0.2, 1.2]]) assert not MatrixLoader.validate_matrix(invalid_matrix, sample_matrix_spec) class TestMatrixInitializer: """Test matrix initialization utilities.""" def test_random_stochastic(self): """Test random stochastic matrix initialization.""" shape = (3, 3) matrix = MatrixInitializer.random_stochastic(shape) # Check dimensions assert matrix.shape == shape # Check stochastic properties assert np.allclose(matrix.sum(axis=0), 1.0) assert np.all(matrix >= 0) def test_identity_based(self): """Test identity-based matrix initialization.""" shape = (3, 3) strength = 0.9 matrix = MatrixInitializer.identity_based(shape, strength) # Check dimensions assert matrix.shape == shape # Check diagonal dominance assert np.all(np.diag(matrix) > 0.5) # Check stochastic properties assert np.allclose(matrix.sum(axis=0), 1.0) assert np.all(matrix >= 0) def test_uniform(self): """Test uniform matrix initialization.""" shape = (3, 3) matrix = MatrixInitializer.uniform(shape) # Check dimensions assert matrix.shape == shape # Check uniformity assert np.allclose(matrix, 1.0/9.0) # Check stochastic properties assert np.allclose(matrix.sum(), 1.0) assert np.all(matrix >= 0) @pytest.mark.parametrize("shape,strength", [ ((2, 2), 0.9), ((3, 3), 0.8), ((4, 4), 0.7) ]) def test_identity_based_parametrized(shape, strength): """Parametrized tests for identity-based initialization.""" matrix = MatrixInitializer.identity_based(shape, strength) assert matrix.shape == shape assert np.all(np.diag(matrix) >= strength * 0.9) # Allow for normalization effects assert np.allclose(matrix.sum(axis=0), 1.0) @pytest.mark.parametrize("distribution,expected_entropy", [ (np.array([1.0, 0.0]), 0.0), # Deterministic (np.array([0.5, 0.5]), np.log(2)), # Maximum entropy for 2 states ]) def test_entropy_special_cases(distribution, expected_entropy): """Test entropy computation for special cases.""" computed_entropy = MatrixOps.compute_entropy(distribution) assert np.allclose(computed_entropy, expected_entropy)