|
| 1 | +"""Regression tests for phase compatibility refactoring. |
| 2 | +
|
| 3 | +Verifies that the unified phase compatibility functions produce identical |
| 4 | +results to the original inline implementations that were replaced in UM |
| 5 | +and THOL operators. |
| 6 | +""" |
| 7 | + |
| 8 | +import math |
| 9 | +import pytest |
| 10 | + |
| 11 | +from tnfr.metrics.phase_compatibility import compute_phase_coupling_strength |
| 12 | +from tnfr.utils.numeric import angle_diff |
| 13 | + |
| 14 | + |
| 15 | +class TestPhaseCompatibilityRegression: |
| 16 | + """Test that refactored code produces identical results to original.""" |
| 17 | + |
| 18 | + def test_thol_propagation_formula_equivalence(self): |
| 19 | + """Verify THOL propagation coupling formula remains identical.""" |
| 20 | + # Original THOL implementation: |
| 21 | + # phase_diff = abs(angle_diff(neighbor_theta, parent_theta)) |
| 22 | + # coupling_strength = 1.0 - (phase_diff / math.pi) |
| 23 | + |
| 24 | + test_cases = [ |
| 25 | + (0.0, 0.0), # Perfect alignment |
| 26 | + (0.0, math.pi/2), # Orthogonal |
| 27 | + (0.0, math.pi), # Antiphase |
| 28 | + (0.1, 0.15), # Small difference |
| 29 | + (0.0, 2*math.pi - 0.1), # Wrap-around |
| 30 | + (math.pi/4, 3*math.pi/4), # 90 degree separation |
| 31 | + ] |
| 32 | + |
| 33 | + for parent_theta, neighbor_theta in test_cases: |
| 34 | + # Original formula |
| 35 | + phase_diff_original = abs(angle_diff(neighbor_theta, parent_theta)) |
| 36 | + coupling_original = 1.0 - (phase_diff_original / math.pi) |
| 37 | + |
| 38 | + # New unified formula |
| 39 | + coupling_unified = compute_phase_coupling_strength(parent_theta, neighbor_theta) |
| 40 | + |
| 41 | + assert abs(coupling_original - coupling_unified) < 1e-10, \ |
| 42 | + f"Mismatch for θ_parent={parent_theta}, θ_neighbor={neighbor_theta}: " \ |
| 43 | + f"original={coupling_original}, unified={coupling_unified}" |
| 44 | + |
| 45 | + def test_thol_capture_signals_formula_equivalence(self): |
| 46 | + """Verify THOL capture_network_signals coupling formula remains identical.""" |
| 47 | + # Original implementation in capture_network_signals: |
| 48 | + # phase_diff = abs(n_theta - node_theta) |
| 49 | + # if phase_diff > math.pi: |
| 50 | + # phase_diff = 2 * math.pi - phase_diff |
| 51 | + # coupling_strength = 1.0 - (phase_diff / math.pi) |
| 52 | + |
| 53 | + test_cases = [ |
| 54 | + (0.0, 0.0), |
| 55 | + (0.0, math.pi/2), |
| 56 | + (0.0, math.pi), |
| 57 | + (0.1, 0.15), |
| 58 | + (0.0, 1.9 * math.pi), # Should wrap to small difference |
| 59 | + (math.pi/6, 5*math.pi/6), # 120 degree separation |
| 60 | + ] |
| 61 | + |
| 62 | + for node_theta, n_theta in test_cases: |
| 63 | + # Original formula (manual normalization) |
| 64 | + phase_diff_original = abs(n_theta - node_theta) |
| 65 | + if phase_diff_original > math.pi: |
| 66 | + phase_diff_original = 2 * math.pi - phase_diff_original |
| 67 | + coupling_original = 1.0 - (phase_diff_original / math.pi) |
| 68 | + |
| 69 | + # New unified formula (uses angle_diff internally) |
| 70 | + coupling_unified = compute_phase_coupling_strength(node_theta, n_theta) |
| 71 | + |
| 72 | + assert abs(coupling_original - coupling_unified) < 1e-10, \ |
| 73 | + f"Mismatch for θ_node={node_theta}, θ_n={n_theta}: " \ |
| 74 | + f"original={coupling_original}, unified={coupling_unified}" |
| 75 | + |
| 76 | + def test_um_phase_alignment_formula_equivalence(self): |
| 77 | + """Verify UM operator phase alignment formula remains identical.""" |
| 78 | + # Original UM implementation for ΔNFR reduction: |
| 79 | + # dphi = abs(angle_diff(neighbor.theta, node.theta)) |
| 80 | + # alignment = 1.0 - dphi / math.pi |
| 81 | + |
| 82 | + test_cases = [ |
| 83 | + (0.0, 0.0), |
| 84 | + (0.0, math.pi/2), |
| 85 | + (0.0, math.pi), |
| 86 | + (0.1, 0.2), |
| 87 | + (math.pi, math.pi + 0.1), |
| 88 | + (0.1, 2*math.pi - 0.1), |
| 89 | + ] |
| 90 | + |
| 91 | + for node_theta, neighbor_theta in test_cases: |
| 92 | + # Original formula |
| 93 | + dphi_original = abs(angle_diff(neighbor_theta, node_theta)) |
| 94 | + alignment_original = 1.0 - dphi_original / math.pi |
| 95 | + |
| 96 | + # New unified formula |
| 97 | + alignment_unified = compute_phase_coupling_strength(node_theta, neighbor_theta) |
| 98 | + |
| 99 | + assert abs(alignment_original - alignment_unified) < 1e-10, \ |
| 100 | + f"Mismatch for θ_node={node_theta}, θ_neighbor={neighbor_theta}: " \ |
| 101 | + f"original={alignment_original}, unified={alignment_unified}" |
| 102 | + |
| 103 | + def test_um_functional_links_formula_equivalence(self): |
| 104 | + """Verify UM functional link formation phase coupling remains identical.""" |
| 105 | + # Original UM implementation for link formation: |
| 106 | + # th_j = j.theta |
| 107 | + # dphi = abs(angle_diff(th_j, th_i)) / math.pi |
| 108 | + # phase_coupling = (1 - dphi) |
| 109 | + |
| 110 | + test_cases = [ |
| 111 | + (0.0, 0.0), |
| 112 | + (0.0, math.pi/2), |
| 113 | + (0.0, math.pi), |
| 114 | + (math.pi/4, 3*math.pi/4), |
| 115 | + (0.05, 0.1), |
| 116 | + (5.0, 5.5), |
| 117 | + ] |
| 118 | + |
| 119 | + for th_i, th_j in test_cases: |
| 120 | + # Original formula |
| 121 | + dphi_original = abs(angle_diff(th_j, th_i)) / math.pi |
| 122 | + phase_coupling_original = 1 - dphi_original |
| 123 | + |
| 124 | + # New unified formula |
| 125 | + phase_coupling_unified = compute_phase_coupling_strength(th_i, th_j) |
| 126 | + |
| 127 | + assert abs(phase_coupling_original - phase_coupling_unified) < 1e-10, \ |
| 128 | + f"Mismatch for θ_i={th_i}, θ_j={th_j}: " \ |
| 129 | + f"original={phase_coupling_original}, unified={phase_coupling_unified}" |
| 130 | + |
| 131 | + def test_comprehensive_equivalence_scan(self): |
| 132 | + """Scan through many angle combinations to ensure complete equivalence.""" |
| 133 | + import random |
| 134 | + random.seed(42) |
| 135 | + |
| 136 | + # Test 100 random angle pairs |
| 137 | + for _ in range(100): |
| 138 | + theta_a = random.uniform(0, 2*math.pi) |
| 139 | + theta_b = random.uniform(0, 2*math.pi) |
| 140 | + |
| 141 | + # Original formula (most general form) |
| 142 | + phase_diff = abs(angle_diff(theta_b, theta_a)) |
| 143 | + coupling_original = 1.0 - (phase_diff / math.pi) |
| 144 | + |
| 145 | + # Unified formula |
| 146 | + coupling_unified = compute_phase_coupling_strength(theta_a, theta_b) |
| 147 | + |
| 148 | + assert abs(coupling_original - coupling_unified) < 1e-10, \ |
| 149 | + f"Random test failed for θ_a={theta_a}, θ_b={theta_b}" |
| 150 | + |
| 151 | + |
| 152 | +class TestBackwardCompatibility: |
| 153 | + """Test that operator behavior is preserved after refactoring.""" |
| 154 | + |
| 155 | + def test_um_compatibility_calculation_unchanged(self): |
| 156 | + """UM compatibility formula should produce same results as before.""" |
| 157 | + # Test the full compatibility calculation used in UM functional links |
| 158 | + # compat = (1 - dphi) * 0.5 + 0.25 * epi_sim + 0.25 * si_sim |
| 159 | + |
| 160 | + th_i, th_j = 0.0, math.pi/4 |
| 161 | + epi_i, epi_j = 0.8, 0.7 |
| 162 | + si_i, si_j = 0.9, 0.85 |
| 163 | + |
| 164 | + # Original calculation |
| 165 | + dphi_original = abs(angle_diff(th_j, th_i)) / math.pi |
| 166 | + epi_sim = 1.0 - abs(epi_i - epi_j) / (abs(epi_i) + abs(epi_j) + 1e-9) |
| 167 | + si_sim = 1.0 - abs(si_i - si_j) |
| 168 | + compat_original = (1 - dphi_original) * 0.5 + 0.25 * epi_sim + 0.25 * si_sim |
| 169 | + |
| 170 | + # New calculation |
| 171 | + phase_coupling_unified = compute_phase_coupling_strength(th_i, th_j) |
| 172 | + compat_unified = phase_coupling_unified * 0.5 + 0.25 * epi_sim + 0.25 * si_sim |
| 173 | + |
| 174 | + assert abs(compat_original - compat_unified) < 1e-10, \ |
| 175 | + f"UM compatibility mismatch: original={compat_original}, unified={compat_unified}" |
| 176 | + |
| 177 | + def test_thol_propagation_threshold_unchanged(self): |
| 178 | + """THOL propagation thresholding should work identically.""" |
| 179 | + # Test that the same neighbors would be selected for propagation |
| 180 | + |
| 181 | + parent_theta = 0.0 |
| 182 | + neighbor_thetas = [0.1, math.pi/3, math.pi/2, 2.0, math.pi, 3.0] |
| 183 | + threshold = 0.5 |
| 184 | + |
| 185 | + # Original logic |
| 186 | + selected_original = [] |
| 187 | + for neighbor_theta in neighbor_thetas: |
| 188 | + phase_diff = abs(angle_diff(neighbor_theta, parent_theta)) |
| 189 | + coupling = 1.0 - (phase_diff / math.pi) |
| 190 | + if coupling >= threshold: |
| 191 | + selected_original.append(neighbor_theta) |
| 192 | + |
| 193 | + # New logic |
| 194 | + selected_unified = [] |
| 195 | + for neighbor_theta in neighbor_thetas: |
| 196 | + coupling = compute_phase_coupling_strength(parent_theta, neighbor_theta) |
| 197 | + if coupling >= threshold: |
| 198 | + selected_unified.append(neighbor_theta) |
| 199 | + |
| 200 | + assert selected_original == selected_unified, \ |
| 201 | + f"THOL propagation selection mismatch: " \ |
| 202 | + f"original={selected_original}, unified={selected_unified}" |
| 203 | + |
| 204 | + |
| 205 | +if __name__ == "__main__": |
| 206 | + pytest.main([__file__, "-v"]) |
0 commit comments