Migrating to PyPhi 2.0#
PyPhi 2.0 is a breaking release. It computes IIT 4.0 (Albantakis et al., 2023; Mayner, Marshall & Tononi, 2026), in which system integrated information includes the system’s intrinsic information. IIT 3.0, and IIT 4.0 as published in 2023, remain available for reproducing earlier results. There are no deprecation shims: code written against pre-2.0 PyPhi must be updated to run.
The PyPhi MCP server carries a condensed, agent-facing copy of this guide as its
migration reference topic, so an assistant can port code without this site.
This guide documents the changes a pre-2.0 user hits, organized by topic. Each topic is tagged with who it affects:
[1.x] — released PyPhi 1.x (the PyPhi-paper / IIT 3.0 era).
[4.0-branch] — the IIT 4.0 feature branch.
[both] — everyone.
Renames at a glance#
Old |
New |
Affects |
|---|---|---|
|
|
[1.x] |
|
|
[1.x] |
|
|
[1.x] |
|
|
[both] |
|
|
[both] |
|
|
[both] |
|
|
[both] |
cause_marginal and effect_marginal (with the proper_cause_marginal /
proper_effect_marginal variants) are the causal marginals of IIT 4.0. The old
cause_tpm / effect_tpm names were a misnomer — the value was never a
transition probability matrix but a distribution over cause/effect states. See
Substrate and system for what they are.
Example networks#
The example functions follow the same vocabulary: every *_network is now
*_substrate and every *_subsystem is *_system. Transitions and bare
matrices (prevention_transition, cond_depend_tpm, cond_independ_tpm,
differentiation_*_tpm) keep their names.
Old |
New |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The IIT 4.0 paper’s networks are new in 2.0 (iit4_2023_fig1a_substrate()
and the other iit4_2023_*, marshall_2023_*, and mayner_2026_*
functions); the examples gallery lists every
registered example.
Configuration options#
Every 1.x option and where it went. Names are lowercase and live under a
layer; set one with pyphi.config.<layer>.<option> or a top-level write such
as pyphi.config.precision = 6, which is routed to its layer. An option whose
name only changed case still works under its uppercase 1.x spelling
(pyphi.config.PRECISION), with a FutureWarning giving the new name. Measure
values are the names in pyphi.measures.
1.x option |
2.0 option |
|---|---|
|
|
|
|
|
|
|
removed: intrinsic differentiation is the surprisal of the specified state and has no measure option |
|
|
|
|
|
|
|
|
|
|
|
removed; the |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
the same names, lowercase, under |
|
|
|
|
|
|
|
the same names under |
|
|
|
removed; results persist through the disk result cache, |
|
removed |
|
|
|
|
|
|
|
the same names under |
|
|
|
|
|
removed |
The quantities#
1.x |
2.0 |
|---|---|
|
|
|
|
|
|
|
unchanged on each concept |
|
|
Building and analyzing#
[1.x] The two core objects are renamed, and the compute module is replaced
by a single entry point, pyphi.analyze.
Before:
import pyphi
network = pyphi.Network(tpm, cm)
subsystem = pyphi.Subsystem(network, state, nodes)
phi = pyphi.compute.phi(subsystem)
ces = pyphi.compute.ces(subsystem)
After:
import pyphi
substrate = pyphi.Substrate(tpm, cm=cm)
analysis = pyphi.analyze(substrate, state)
phi = analysis.phi # system integrated information, φ_s
ces = analysis.ces # the Φ-structure (a CauseEffectStructure)
pyphi.analyze returns an Analysis with .phi, .ces, .sia (the system
irreducibility analysis), and .system (the analyzed system). It analyzes
the given units — the whole substrate by default, or the subset argument —
and does not search for the complex. To find complexes, use
substrate.complexes() or substrate.maximal_complex(); to analyze a
specific subset, pass subset= or construct a System directly:
pyphi.System(substrate, state, node_indices=(0, 1, 2)).
Choosing a formalism#
[both] In 1.x a single IIT_VERSION config toggle selected the formalism,
defaulting to IIT 3.0. In 2.0 the default is IIT 4.0 (with the
intrinsic-information requirement), and an earlier version is selected per call:
Before:
pyphi.config.IIT_VERSION = 3.0 # global toggle, default 3.0
After:
# per call — the reliable way; sets the compatible measures for you
analysis = pyphi.analyze(substrate, state, formalism="IIT_3_0")
# or via configuration
pyphi.config.formalism.iit.version # "IIT_4_0_2026" by default
The available formalisms are "IIT_3_0", "IIT_4_0_2023", and "IIT_4_0_2026".
Because the default changed from IIT 3.0 to IIT 4.0, the same substrate and state
give a different result than a 1.x default run unless you request
formalism="IIT_3_0". See
Reproduce results from earlier versions of IIT
for the differences, and Compute under an earlier version for the three equivalent
ways to select one.
Configuration#
[both] The configuration file moved from a flat format to a layered nested
one. Loading a legacy flat pyphi_config.yml is rejected with a rename map
pointing each old key to its new location.
Before (pyphi_config.yml):
PRECISION: 6
PARALLEL: true
After (pyphi_config.yml):
numerics:
precision: 6
infrastructure:
parallel: true
The three top-level layers are formalism (with the sub-namespaces iit and
actual_causation), infrastructure (parallelism, caching, logging), and
numerics (precision). At runtime, read a value from its layer
(pyphi.config.numerics.precision); a top-level write such as
pyphi.config.precision = 6 is routed to the correct layer automatically.
Saving and loading results#
[both] The custom pyphi.jsonify layer (and the per-class to_json /
from_json hooks) is gone. Results are now saved and loaded with a typed
msgspec-based serializer:
Before:
import pyphi.jsonify
data = pyphi.jsonify.jsonify(result)
After:
ces = analysis.ces
pyphi.save(ces, "ces.json") # or ces.save("ces.json")
ces = pyphi.load("ces.json") # or CauseEffectStructure.load("ces.json")
save / load (and the .save() / .load() methods) apply to every
serializable result type, including the top-level Analysis wrapper — see
Save and load results. The format is inferred from
the extension:
.json, .mpk (msgpack), and a transparent .gz layer for any of them. This
is a format break with no standalone converter: results saved in the old
jsonify format cannot be loaded and must be recomputed.
Precision and φ comparison#
[both] The φ, Φ, and α values on results (.phi, .alpha, and the small-φ
of distinctions) are plain Python floats. A direct == or < between two of
them is an exact floating-point comparison, so two values that differ only
by summation noise below config.numerics.precision compare as unequal. To
compare tolerantly at the configured precision, use the scalar predicates in the
new pyphi.numerics module:
from pyphi import numerics
numerics.eq(a.phi, b.phi) # tolerant equality up to config.numerics.precision
numerics.is_zero(a.phi) # tolerant test against 0
numerics.is_positive(a.phi)
The precision-aware comparison helpers moved from pyphi.utils to
pyphi.numerics: pyphi.utils.eq, is_positive, and is_nonpositive are now
pyphi.numerics.eq / is_positive / is_nonpositive, alongside the new
is_zero, positive_mask, and round_to_precision. Update imports
accordingly.
The PyPhiFloat wrapper type is removed. Values that previously carried metadata
alongside a float (repertoire distances) are now DistanceResult, which is a
float subtype: it compares and arithmetic-combines exactly like the number it
holds, so no unwrapping is needed.
Because tie resolution now clusters candidate values tolerantly, a reported tie set may include members that earlier versions silently dropped when two candidates differed only by sub-precision noise. Selection is deterministic and order-independent.
Changed defaults#
[both] The default changed from IIT 3.0 (1.x) to IIT 4.0, with the
intrinsic-information requirement
(system_phi_measure="INTRINSIC_INFORMATION").
This silently changes computed values relative to a 1.x default run, so a
migration that expects IIT 3.0 numbers must request formalism="IIT_3_0"
explicitly; request formalism="IIT_4_0_2023" for the IIT 4.0 system φ
without that requirement.
A practical consequence: deterministic networks compute \(\varphi_s = 0\) by
default. The classic examples (xor, basic, the cellular-automaton
rules) are all deterministic, so analyses ported from 1.x or from the
literature will show 0 where papers print nonzero values. This is the
expected behavior of the intrinsic-information requirement; pin
formalism="IIT_4_0_2023" to reproduce numbers published with the IIT 4.0
paper in 2023. See
The intrinsic-information requirement.