Skip to content

Commit d95a26f

Browse files
wmaynerclaude
andcommitted
Add a 1.x→2.0 migration guide to the MCP server
Surface a migration guide through the MCP server so an assistant can port pre-2.0 PyPhi code to 2.0. 2.0 ships no deprecation shims, and the authoritative guide (docs/migration/migration-2.0.md) is not packaged into the wheel, so agents talking to an installed server could not reach it. Add a condensed, self-contained agent-facing copy as the `migration` reference topic (auto-exposed via get_iit_reference and the pyphi://theory/migration resource) and a `migrate_code` prompt that kicks off a rewrite. The topic carries the full old→new rename table — including the import-level renames and the CauseEffectStructure/PhiStructure name swap that the docs table omits — plus before/after snippets and the deterministic-gives-φ_s=0 default trap. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B9znTVJeUQedYWAnzXjirV
1 parent 1634983 commit d95a26f

7 files changed

Lines changed: 256 additions & 1 deletion

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
The MCP server ships a `migration` reference topic (readable via the
2+
`get_iit_reference` tool and the `pyphi://theory/migration` resource) and a
3+
`migrate_code` prompt, so an agent can port pre-2.0 PyPhi code to 2.0.

docs/migration/migration-2.0.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Tononi, 2026), with IIT 4.0 (2023, Albantakis et al.) and IIT 3.0 retained
66
through configuration. There are no deprecation shims: code written against pre-2.0
77
PyPhi must be updated to run.
88

9+
The PyPhi MCP server carries a condensed, agent-facing copy of this guide as its
10+
`migration` reference topic (`pyphi/mcp/content/migration.md`), so an assistant
11+
can port code without the docs build. Keep the two in sync when the API changes.
12+
913
This guide documents the changes a pre-2.0 user hits, organized by topic. Each
1014
topic is tagged with who it affects:
1115

pyphi/mcp/content.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@
4141
"How to turn a description of some units into a valid transition "
4242
"probability matrix.",
4343
),
44+
"migration": (
45+
"migration.md",
46+
"Migrating pre-2.0 PyPhi code to 2.0: the renames, config, and "
47+
"default-formalism changes, with before/after snippets.",
48+
),
4449
}
4550

4651

pyphi/mcp/content/migration.md

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
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.

pyphi/mcp/content/primer.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ reference.
3737
`pip install pyphi[visualize]`): `kind="ces"` the Φ-structure,
3838
`"repertoires"` the cause/effect repertoires, `"connectivity"` the causal
3939
graph, `"tpm"` the transition probability matrix.
40-
- `get_iit_reference(topic)` — the grounded theory reference.
40+
- `get_iit_reference(topic)` — the grounded theory reference. Porting code
41+
written against an older PyPhi? Read `get_iit_reference("migration")` first:
42+
2.0 renamed the core objects and has no compatibility shims.
4143

4244
## Two things to keep straight from the start
4345

@@ -52,3 +54,4 @@ reference.
5254
- `explain_result` — narrate an analysis result in plain language.
5355
- `build_system_walkthrough` — turn a description of some units into a valid
5456
transition probability matrix.
57+
- `migrate_code` — rewrite pre-2.0 PyPhi code for 2.0.

pyphi/mcp/prompts.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,30 @@ def explain_result(result_ref: str) -> str:
4646
"Avoid jargon where a plain word is just as precise."
4747
)
4848

49+
@mcp.prompt()
50+
def migrate_code(code: str) -> str:
51+
"""Rewrite pre-2.0 PyPhi code for PyPhi 2.0.
52+
53+
Parameters
54+
----------
55+
code : str
56+
The pre-2.0 PyPhi code to port (e.g. code using ``pyphi.Network``,
57+
``pyphi.Subsystem``, ``pyphi.compute.*``, or a flat config).
58+
"""
59+
return (
60+
"Rewrite this PyPhi code for version 2.0:\n\n"
61+
f"{code}\n\n"
62+
"First read the 'migration' and 'gotchas' reference topics with "
63+
"get_iit_reference. There are no deprecation shims, so every "
64+
"pre-2.0 name must be changed. Apply the renames, replace "
65+
"pyphi.compute.* with pyphi.analyze, update any config and jsonify "
66+
"usage, and preserve the original behavior — if the code relied on "
67+
'the old IIT 3.0 default, pass formalism="IIT_3_0" so the numbers '
68+
"still match. Show the rewritten code and note any change that "
69+
"alters computed values (especially that deterministic systems give "
70+
"φ_s = 0 under the new 2026 default)."
71+
)
72+
4973
@mcp.prompt()
5074
def build_system_walkthrough(description: str) -> str:
5175
"""Turn a description of some units into a valid substrate.

test/mcp/test_server.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,20 @@ def test_reference_topics_load():
153153
"gotchas",
154154
"interpreting-results",
155155
"building-systems",
156+
"migration",
156157
}
157158
for topic in topics:
158159
assert len(content.load(topic)) > 100
159160

160161

162+
def test_migration_topic_covers_the_renames():
163+
doc = content.load("migration")
164+
# The load-bearing facts an agent needs, guarding against a stub file.
165+
assert "no deprecation shims" in doc
166+
for name in ("Substrate", "System", "analyze", "IIT_3_0"):
167+
assert name in doc
168+
169+
161170
@pytest.mark.skipif(not HAS_EMD, reason="IIT 3.0 needs the emd extra")
162171
def test_analyze_iit3_differs_from_iit4(basic_handle):
163172
v3 = srv.analyze(basic_handle, BASIC_STATE, formalism="IIT_3_0", compute="sia")

0 commit comments

Comments
 (0)