|
| 1 | +# Migrating pre-2.0 PyPhi code to 2.0 |
| 2 | + |
| 3 | +PyPhi 2.0 is a breaking release. **There are no deprecation shims:** every |
| 4 | +pre-2.0 name below is gone, not aliased, so old code raises `ImportError` or |
| 5 | +`AttributeError` until it is updated. When helping someone port their code, |
| 6 | +change every occurrence — a partial rename will not run. |
| 7 | + |
| 8 | +(The full human-readable guide is `docs/migration/migration-2.0.md`; this is |
| 9 | +the condensed agent-facing copy. Keep the two in sync.) |
| 10 | + |
| 11 | +## 1. Renames at a glance |
| 12 | + |
| 13 | +| Old (pre-2.0) | New (2.0) | |
| 14 | +| --- | --- | |
| 15 | +| `pyphi.Network` | `pyphi.Substrate` | |
| 16 | +| `pyphi.Subsystem` | `pyphi.System` | |
| 17 | +| `pyphi.network_generator` | `pyphi.substrate_generator` | |
| 18 | +| `pyphi.compute.big_phi(...)` / `pyphi.compute.ces(...)` | `pyphi.analyze(...)` | |
| 19 | +| `from pyphi import new_big_phi` | `from pyphi.formalism import iit4` | |
| 20 | +| `pyphi.new_big_phi.phi_structure` | `pyphi.formalism.iit4.ces` | |
| 21 | +| `pyphi.new_big_phi.sia` | `pyphi.formalism.iit4.sia` | |
| 22 | +| `pyphi.metrics` | `pyphi.measures` | |
| 23 | +| `pyphi.models.Concept` | `pyphi.models.Distinction` (`Concept` still works as an alias) | |
| 24 | +| `subsystem.cause_tpm` / `effect_tpm` | `system.cause_marginal` / `effect_marginal` (plus `proper_*` variants) | |
| 25 | +| `pyphi.jsonify` | `pyphi.serialize` / `pyphi.save` / `pyphi.load` | |
| 26 | +| `pyphi.utils.eq` / `is_positive` / `is_nonpositive` | `pyphi.numerics.eq` / `is_positive` / `is_nonpositive` | |
| 27 | +| `pyphi.config.IIT_VERSION` | `pyphi.config.formalism.iit.version` | |
| 28 | + |
| 29 | +Two traps in that table: |
| 30 | + |
| 31 | +- **The result-type names were swapped.** The old `CauseEffectStructure` is now |
| 32 | + `pyphi.models.Distinctions`; the old `PhiStructure` is now |
| 33 | + `pyphi.models.CauseEffectStructure`. The same words point at different |
| 34 | + objects, so an unported import can succeed and still be wrong. |
| 35 | +- **`big_phi` changed kind.** The top-level `pyphi.compute.big_phi(subsystem)` |
| 36 | + *function* is gone. `big_phi` now names the **Φ** property on a cause-effect |
| 37 | + structure (`ces.big_phi`). Do not translate the old function call into a |
| 38 | + property access blindly — the modern entry point is `pyphi.analyze`. |
| 39 | + |
| 40 | +`cause_marginal` / `effect_marginal` are the causal marginals of IIT 4.0. The |
| 41 | +old `cause_tpm` / `effect_tpm` names were a misnomer: the value was never a |
| 42 | +transition probability matrix but a distribution over cause/effect states. |
| 43 | + |
| 44 | +## 2. Building and analyzing |
| 45 | + |
| 46 | +The two core objects are renamed, and the whole `compute` module is replaced by |
| 47 | +one entry point, `pyphi.analyze`. |
| 48 | + |
| 49 | +Before: |
| 50 | + |
| 51 | +```python |
| 52 | +import pyphi |
| 53 | + |
| 54 | +network = pyphi.Network(tpm, cm) |
| 55 | +subsystem = pyphi.Subsystem(network, state, nodes) |
| 56 | +phi = pyphi.compute.big_phi(subsystem) |
| 57 | +ces = pyphi.compute.ces(subsystem) |
| 58 | +``` |
| 59 | + |
| 60 | +After: |
| 61 | + |
| 62 | +```python |
| 63 | +import pyphi |
| 64 | + |
| 65 | +substrate = pyphi.Substrate(tpm, cm=cm) |
| 66 | +analysis = pyphi.analyze(substrate, state) |
| 67 | + |
| 68 | +phi = analysis.phi # system integrated information, φ_s |
| 69 | +ces = analysis.ces # the Φ-structure |
| 70 | +``` |
| 71 | + |
| 72 | +`pyphi.analyze` returns an `Analysis` with `.phi`, `.ces`, `.sia` (the system |
| 73 | +irreducibility analysis), and `.system` (the complex). To analyze a specific |
| 74 | +subset instead of searching for the complex, construct a `System` directly: |
| 75 | +`pyphi.System(substrate, state, node_indices=(0, 1, 2))`. Code that imported the |
| 76 | +low-level IIT 4.0 functions from `new_big_phi` now imports `sia` and `ces` from |
| 77 | +`pyphi.formalism.iit4`. |
| 78 | + |
| 79 | +## 3. Choosing a formalism |
| 80 | + |
| 81 | +In 1.x a single `IIT_VERSION` toggle selected the formalism, defaulting to IIT |
| 82 | +3.0. In 2.0 the formalism is chosen per call, and **the default is now IIT 4.0 |
| 83 | +(2026)** with the intrinsic-information requirement. |
| 84 | + |
| 85 | +Before: |
| 86 | + |
| 87 | +```python |
| 88 | +pyphi.config.IIT_VERSION = 3.0 # global toggle, default 3.0 |
| 89 | +``` |
| 90 | + |
| 91 | +After: |
| 92 | + |
| 93 | +```python |
| 94 | +# per call — the reliable way; it sets the compatible measures for you |
| 95 | +analysis = pyphi.analyze(substrate, state, formalism="IIT_3_0") |
| 96 | + |
| 97 | +# or via configuration |
| 98 | +pyphi.config.formalism.iit.version # "IIT_4_0_2026" by default |
| 99 | +``` |
| 100 | + |
| 101 | +The available formalisms are `"IIT_3_0"`, `"IIT_4_0_2023"`, and |
| 102 | +`"IIT_4_0_2026"`. |
| 103 | + |
| 104 | +## 4. Configuration |
| 105 | + |
| 106 | +The configuration file moved from a flat format to a layered nested one. A |
| 107 | +legacy flat `pyphi_config.yml` is **rejected** on load with an error pointing |
| 108 | +each old key to its new location. |
| 109 | + |
| 110 | +Before (`pyphi_config.yml`): |
| 111 | + |
| 112 | +```yaml |
| 113 | +PRECISION: 6 |
| 114 | +PARALLEL: true |
| 115 | +``` |
| 116 | +
|
| 117 | +After (`pyphi_config.yml`): |
| 118 | + |
| 119 | +```yaml |
| 120 | +numerics: |
| 121 | + precision: 6 |
| 122 | +infrastructure: |
| 123 | + parallel: true |
| 124 | +``` |
| 125 | + |
| 126 | +The three top-level layers are `formalism` (with the sub-namespaces `iit` and |
| 127 | +`actual_causation`), `infrastructure` (parallelism, caching, logging), and |
| 128 | +`numerics` (precision). At runtime, read a value from its layer |
| 129 | +(`pyphi.config.numerics.precision`); a top-level write such as |
| 130 | +`pyphi.config.precision = 6` is routed to the correct layer automatically. |
| 131 | + |
| 132 | +## 5. Saving and loading results |
| 133 | + |
| 134 | +The custom `pyphi.jsonify` layer (and the per-class `to_json` / `from_json` |
| 135 | +hooks) is gone. Results are saved and loaded with a typed `msgspec` serializer. |
| 136 | + |
| 137 | +Before: |
| 138 | + |
| 139 | +```python |
| 140 | +import pyphi.jsonify |
| 141 | +
|
| 142 | +data = pyphi.jsonify.jsonify(result) |
| 143 | +``` |
| 144 | + |
| 145 | +After: |
| 146 | + |
| 147 | +```python |
| 148 | +ces = analysis.ces |
| 149 | +
|
| 150 | +pyphi.save(ces, "ces.json") # or ces.save("ces.json") |
| 151 | +ces = pyphi.load("ces.json") # or CauseEffectStructure.load("ces.json") |
| 152 | +``` |
| 153 | + |
| 154 | +The format is inferred from the extension: `.json`, `.mpk` (msgpack), and a |
| 155 | +transparent `.gz` layer for either. This is a **format break with no converter** |
| 156 | +— data written by the old `jsonify` cannot be loaded and must be recomputed. |
| 157 | + |
| 158 | +## 6. Comparing φ tolerantly |
| 159 | + |
| 160 | +The φ, Φ, and α values on results are plain Python floats, so `==` and `<` |
| 161 | +between them are **exact** floating-point comparisons: two values that differ |
| 162 | +only by summation noise below `config.numerics.precision` compare as unequal. |
| 163 | +Use the scalar predicates in `pyphi.numerics` for tolerant comparison: |
| 164 | + |
| 165 | +```python |
| 166 | +from pyphi import numerics |
| 167 | +
|
| 168 | +numerics.eq(a.phi, b.phi) # tolerant equality at config.numerics.precision |
| 169 | +numerics.is_zero(a.phi) # tolerant test against 0 |
| 170 | +numerics.is_positive(a.phi) |
| 171 | +``` |
| 172 | + |
| 173 | +The precision-aware helpers `eq`, `is_positive`, and `is_nonpositive` moved from |
| 174 | +`pyphi.utils` to `pyphi.numerics`, joined by `is_zero`, `positive_mask`, and |
| 175 | +`round_to_precision`. |
| 176 | + |
| 177 | +## 7. Changed default: the φ_s = 0 surprise |
| 178 | + |
| 179 | +Because the default formalism changed from IIT 3.0 to IIT 4.0 (2026), the same |
| 180 | +substrate and state give a **different result** than a 1.x default run unless |
| 181 | +`formalism="IIT_3_0"` is requested. |
| 182 | + |
| 183 | +The consequence that most often surprises people migrating: under the 2026 |
| 184 | +default, **deterministic networks compute φ_s = 0.** The classic examples |
| 185 | +(`xor`, `basic`, the cellular-automaton rules) are all deterministic, so |
| 186 | +analyses ported from 1.x or from the literature will show 0 where papers print |
| 187 | +nonzero values. This is the intended behavior of the 2026 intrinsic-information |
| 188 | +requirement, not a bug. To reproduce old numbers: |
| 189 | + |
| 190 | +- IIT 3.0 numbers → `formalism="IIT_3_0"` |
| 191 | +- IIT 4.0 (2023) system φ, without the requirement → `formalism="IIT_4_0_2023"` |
| 192 | + |
| 193 | +See the `gotchas` and `theory` references for what the requirement is. |
| 194 | + |
| 195 | +## 8. Checklist for migrating a file |
| 196 | + |
| 197 | +1. Rename `Network`→`Substrate`, `Subsystem`→`System`, and the other names in |
| 198 | + the table above; fix every `import`. |
| 199 | +2. Replace `pyphi.compute.*` calls with `pyphi.analyze(substrate, state)` and |
| 200 | + read `.phi` / `.ces` / `.sia` off the result. |
| 201 | +3. Convert any `pyphi_config.yml` and `pyphi.config.*` writes to the layered |
| 202 | + form. |
| 203 | +4. Replace `pyphi.jsonify` save/load with `pyphi.save` / `pyphi.load`; recompute |
| 204 | + any data stored in the old format. |
| 205 | +5. If the code must reproduce pre-2.0 numbers, pass `formalism="IIT_3_0"` (or |
| 206 | + `"IIT_4_0_2023"`) — otherwise it silently runs under the new default. |
| 207 | +6. Flag every place where the result changed value, not just the API. |
0 commit comments