|
14 | 14 | from pyphi import config |
15 | 15 | from pyphi import new_big_phi |
16 | 16 | from pyphi.new_big_phi import NullSystemIrreducibilityAnalysis |
| 17 | +from pyphi.subsystem import Subsystem |
17 | 18 |
|
| 19 | +from . import example_networks |
18 | 20 | from .conftest import skip_if_no_pyemd |
19 | 21 |
|
20 | 22 |
|
@@ -320,3 +322,67 @@ def test_phi_structure_has_relations(self, example_name): |
320 | 322 | f"but no relations attribute" |
321 | 323 | ) |
322 | 324 | # Relations might be None for some systems, so don't assert it's non-empty |
| 325 | + |
| 326 | + |
| 327 | +class TestPermutationSymmetry: |
| 328 | + """Systems related by node permutation must have identical phi values. |
| 329 | +
|
| 330 | + AND-XOR and XOR-AND have the same all-ones connectivity matrix but swap |
| 331 | + which node gets the AND vs XOR gate. They are related by the node |
| 332 | + permutation π: 0↔1. Under this permutation, state (a,b) in AND-XOR |
| 333 | + maps to (b,a) in XOR-AND. |
| 334 | +
|
| 335 | + All measures must be invariant under this permutation. |
| 336 | + """ |
| 337 | + |
| 338 | + def test_system_intrinsic_information_symmetric(self): |
| 339 | + """Cause/effect intrinsic information must be equal for permuted systems.""" |
| 340 | + sub_ax = Subsystem(example_networks.and_xor_network(), (0, 1)) |
| 341 | + sub_xa = Subsystem(example_networks.xor_and_network(), (1, 0)) |
| 342 | + ss_ax = new_big_phi.system_intrinsic_information(sub_ax) |
| 343 | + ss_xa = new_big_phi.system_intrinsic_information(sub_xa) |
| 344 | + assert float(ss_ax.cause.intrinsic_information) == pytest.approx( |
| 345 | + float(ss_xa.cause.intrinsic_information) |
| 346 | + ) |
| 347 | + assert float(ss_ax.effect.intrinsic_information) == pytest.approx( |
| 348 | + float(ss_xa.effect.intrinsic_information) |
| 349 | + ) |
| 350 | + |
| 351 | + def test_sia_phi_symmetric(self): |
| 352 | + """Overall phi must be equal for permuted systems.""" |
| 353 | + sub_ax = Subsystem(example_networks.and_xor_network(), (0, 1)) |
| 354 | + sub_xa = Subsystem(example_networks.xor_and_network(), (1, 0)) |
| 355 | + sia_ax = new_big_phi.sia(sub_ax) |
| 356 | + sia_xa = new_big_phi.sia(sub_xa) |
| 357 | + assert float(sia_ax.phi) == pytest.approx(float(sia_xa.phi)) |
| 358 | + |
| 359 | + def test_sia_phi_c_symmetric(self): |
| 360 | + """phi_c must be equal for permuted systems. |
| 361 | +
|
| 362 | + This is the specific invariant that was violated by the tied-state |
| 363 | + bug: AND-XOR(0,1) reported phi_c=0.5 while XOR-AND(1,0) reported |
| 364 | + phi_c=0.0, due to arbitrary tie-breaking in the specified cause state. |
| 365 | + """ |
| 366 | + sub_ax = Subsystem(example_networks.and_xor_network(), (0, 1)) |
| 367 | + sub_xa = Subsystem(example_networks.xor_and_network(), (1, 0)) |
| 368 | + sia_ax = new_big_phi.sia(sub_ax) |
| 369 | + sia_xa = new_big_phi.sia(sub_xa) |
| 370 | + phi_c_ax = float(sia_ax.cause.phi) if sia_ax.cause else 0.0 |
| 371 | + phi_c_xa = float(sia_xa.cause.phi) if sia_xa.cause else 0.0 |
| 372 | + assert phi_c_ax == pytest.approx(phi_c_xa), ( |
| 373 | + f"phi_c differs for permuted systems: " |
| 374 | + f"AND-XOR(0,1)={phi_c_ax}, XOR-AND(1,0)={phi_c_xa}" |
| 375 | + ) |
| 376 | + |
| 377 | + def test_sia_phi_e_symmetric(self): |
| 378 | + """phi_e must be equal for permuted systems.""" |
| 379 | + sub_ax = Subsystem(example_networks.and_xor_network(), (0, 1)) |
| 380 | + sub_xa = Subsystem(example_networks.xor_and_network(), (1, 0)) |
| 381 | + sia_ax = new_big_phi.sia(sub_ax) |
| 382 | + sia_xa = new_big_phi.sia(sub_xa) |
| 383 | + phi_e_ax = float(sia_ax.effect.phi) if sia_ax.effect else 0.0 |
| 384 | + phi_e_xa = float(sia_xa.effect.phi) if sia_xa.effect else 0.0 |
| 385 | + assert phi_e_ax == pytest.approx(phi_e_xa), ( |
| 386 | + f"phi_e differs for permuted systems: " |
| 387 | + f"AND-XOR(0,1)={phi_e_ax}, XOR-AND(1,0)={phi_e_xa}" |
| 388 | + ) |
0 commit comments