A complete worked example#

Download this page as a Jupyter notebook Open in Colab

This page follows the worked example of the IIT 4.0 paper (Albantakis et al. 2023) from start to finish, reproducing its published numbers: Figure 1 — is a set of units a complex, and how irreducible is it (\(\varphi_s\))? Figure 2 — what distinctions compose its cause-effect structure? Figure 4 — how do those distinctions bind into relations? All three use the same small network: pyphi.examples.iit4_2023_fig1a_substrate().

The paper’s \(\varphi_s\) values were computed before the system’s intrinsic information became part of the definition of \(\varphi_s\), so this page pins the version of IIT the paper used (see Reproduce results from earlier versions of IIT).

import pyphi
from pyphi.conf import presets

pyphi.config.progress_bars = False
# Reproduce the paper's published values under the version of IIT it used.
pyphi.config.iit = presets.iit4_2023["iit"]

The substrate: three logistic units#

Fig 1A defines three units \(A\), \(B\), \(C\). Each unit’s probability of turning ON is a logistic function (slope \(k = 4\)) of its weighted inputs, with the inputs read as \(\pm 1\) (paper Eq. 60). \(A\) and \(B\) excite each other strongly (\(\pm 0.7\)), \(C\) inhibits \(B\) (\(-0.8\)), and each unit weakly affects itself.

substrate = pyphi.examples.iit4_2023_fig1a_substrate()
substrate
Substrate3 units · A, B, C
UnitsA, B, C
State spacebinary
Connectivity
ABC
A111
B11·
C·11
TPM
stateABC
(0,0,0)0.1192030.768525 0.167982
(1,0,0)0.0265970.998887 0.5
(0,1,0)0.9734030.401312 0.167982
(1,1,0)0.8807970.994514 0.5
(0,0,1)0.1192030.0054863 0.5
(1,0,1)0.0265970.598688 0.832018
(0,1,1)0.9734030.001112540.5
(1,1,1)0.8807970.231475 0.832018

The paper analyzes the state written “aBC”: \(A\) off, \(B\) and \(C\) on. (Lowercase marks an OFF unit.)

state = (0, 1, 1)

Figure 1: integration and exclusion#

Fig 1E asks which candidate systems exist. We compute \(\varphi_s\) for the three candidates the paper compares — the single unit \(\{A\}\), the pair \(\{A, B\}\) (“aB”), and the whole substrate:

for subset in [(0,), (0, 1), (0, 1, 2)]:
    analysis = pyphi.analyze(substrate, state, subset=subset)
    print(subset, round(analysis.phi, 4))
(0,) 0.0396
(0, 1) 0.1719
(0, 1, 2) 0.1339

These reproduce the paper’s Fig 1E values: \(0.04\), \(0.17\), and \(0.13\). The pair aB beats both its subset and its superset — and in fact every candidate that overlaps it — so aB is a complex. The exhaustive competition over all candidates returns aB and one other, non-overlapping complex:

for complex_ in substrate.complexes(state):
    print(complex_.node_indices, round(float(complex_.phi), 4))
(2,) 0.2122
(0, 1) 0.1719

The single unit \(\{C\}\) has the globally maximal \(\varphi_s\) here; since it does not overlap aB, both exist. The paper presents aB as a complex: maximal among the candidates that share its units.

Where does aB’s \(\varphi_s\) come from? Fig 1D splits it by temporal direction. Integration is measured separately over the system’s causes and its effects, and the system is only as integrated as its weaker direction:

aB = pyphi.analyze(substrate, state, subset=(0, 1))
print("φ_c =", round(float(aB.sia.cause.phi), 4))
print("φ_e =", round(float(aB.sia.effect.phi), 4))
print("φ_s =", round(aB.phi, 4))
φ_c = 0.2446
φ_e = 0.1719
φ_s = 0.1719

\(\varphi_c = 0.24\) and \(\varphi_e = 0.17\), the paper’s published split; the effect side is the weaker one, so \(\varphi_s = \varphi_e\). The partition responsible — the minimum partition — is on the analysis as aB.sia.partition.

Figure 2: the distinctions#

With the complex fixed, the composition postulate unfolds what exists within it. Every subset of aB’s units — every mechanism — is tested for an irreducible cause and effect. The irreducible ones are the complex’s distinctions, collected in the cause-effect structure:

ces = aB.ces
for d in ces.distinctions:
    print(
        f"{d.mechanism_label:>3}  φ_d = {float(d.phi):.4f}  "
        f"cause {d.cause_purview}  effect {d.effect_purview}"
    )
  a  φ_d = 0.3327  cause (1,)  effect (1,)
  B  φ_d = 0.3236  cause (0,)  effect (0, 1)
 aB  φ_d = 0.0714  cause (1,)  effect (0, 1)

Three distinctions, matching Fig 2: the first-order mechanisms \(a\) (\(\varphi_d = 0.33\)) and \(B\) (\(0.32\)), and the second-order mechanism \(aB\) (\(0.07\)). Each specifies the purviews shown — the units its cause and effect power is about. A single distinction prints its full detail, including the repertoires (the probability distributions it specifies over its purviews):

ces.distinctions[1]
Distinction
MechanismB
φd0.323594
Cause
Purviewa
φ0.323594
Specified state(1,)
Effect
PurviewaB
φ0.861095
Specified state(1, 0)

The whole set converts to a table with to_pandas, handy for sorting, filtering, or exporting:

ces.to_pandas()
phi mechanism_state cause_purview cause_state effect_purview effect_state
mechanism
(A,) 0.332728 (0,) (B,) (0,) (B,) (0,)
(B,) 0.323594 (1,) (A,) (1,) (A, B) (1, 0)
(A, B) 0.071440 (0, 1) (B,) (0,) (A, B) (1, 0)

Figure 4: the relations#

Distinctions whose purviews overlap congruently — same units, same specified state — bind together into relations. Fig 4 works out the relation between the distinctions \(a\) and \(aB\), which overlap over unit \(b\). By default PyPhi computes the relation set analytically — a closed-form summary that answers aggregate queries without building relation objects. To examine individual relations, enumerate them explicitly with materialize():

relations = ces.relations.materialize()
relation = next(
    r for r in relations
    if {tuple(m) for m in r.mechanisms} == {(0,), (0, 1)}
)
print("φ_r =", round(float(relation.phi), 4))
print("faces:", relation.num_faces)
φ_r = 0.0357
faces: 9

\(\varphi_r = 0.036\) with all \(9\) faces, the paper’s Fig 4 relation (quoted there as \(0.035\), from the rounded \(\varphi_d(aB) = 0.07\) divided over the two-unit purview union). The structure has seven relations in all — including self-relations, where a single distinction’s own cause and effect purviews overlap:

for r in relations:
    mechs = [tuple(m) for m in r.mechanisms]
    print(f"φ_r = {float(r.phi):.4f}  mechanisms {mechs}")
φ_r = 0.0357  mechanisms [(0, 1), (0,)]
φ_r = 0.0357  mechanisms [(0, 1)]
φ_r = 0.1618  mechanisms [(0,), (1,)]
φ_r = 0.3327  mechanisms [(0,)]
φ_r = 0.0714  mechanisms [(0, 1), (1,)]
φ_r = 0.0357  mechanisms [(0, 1), (1,), (0,)]
φ_r = 0.1618  mechanisms [(1,)]

The Φ-structure, summed#

Distinctions and relations together are the complex’s \(\Phi\)-structure, and their summed \(\varphi\) is the structure integrated information \(\Phi\):

print("Σ φ_d =", round(float(ces.sum_phi_distinctions), 4))
print("Σ φ_r =", round(float(ces.sum_phi_relations), 4))
print("Φ     =", round(float(ces.big_phi), 4))
Σ φ_d = 0.7278
Σ φ_r = 0.8349
Φ     = 1.5627

Note that \(\Phi\) (the structure’s total, \(1.56\) here) is a different quantity from \(\varphi_s\) (the system’s irreducibility over its minimum partition, \(0.17\) here). Both are reported on the analysis: aB.phi is \(\varphi_s\); aB.ces.big_phi is \(\Phi\).

Summary#

For the paper’s Fig 1A network in state aBC:

  • \(\varphi_s = 0.04 / 0.17 / 0.13\) for \(\{A\}\) / aB / aBC (Fig 1E), with \(\varphi_c = 0.24\), \(\varphi_e = 0.17\) for aB (Fig 1D);

  • aB as a complex, alongside the non-overlapping complex \(\{C\}\);

  • aB’s three distinctions with \(\varphi_d = 0.33, 0.32, 0.07\) and their purviews (Fig 2);

  • the relation \(r(\{a, aB\})\) with \(\varphi_r = 0.035\) and 9 faces (Fig 4);

  • the summed structure, \(\Phi = 1.56\).

Where to go next#

  • Theory — the same pipeline, quantity by quantity, with the full map from paper symbols to code.

  • The intrinsic-information requirement — how the system’s intrinsic information enters \(\varphi_s\), and why a deterministic network has \(\varphi_s = 0\).

  • The IIT 4.0 demo notebook — the paper’s own supplementary notebook, going deeper into the algorithm.