Sweep states and subsystems#

pyphi.sweep runs one IIT computation across many states and candidate subsystems in a single call, and collects every result into one tidy long-format DataFrame. It saves you from writing the nested loops, building each System by hand, and stitching the results back together. (To vary the substrate’s parameters — its connection weights — rather than its state or the candidate system, see Explore substrate parameter landscapes.)

import pyphi

pyphi.config.progress_bars = False

A first sweep#

sweep takes a substrate and at least one axis to vary. The states argument is required. Pass "all" to enumerate every state of the substrate:

substrate = pyphi.examples.iit4_2023_fig1a_substrate()

result = pyphi.sweep(substrate, states="all")
result.df.round(6)
phi normalized_phi is_irreducible partition_margin cause_state_margin effect_state_margin effectively_tied substrate formalism subset
state
(0, 0, 0) 0.067358 0.033679 True 0.044650 1.182755 0.903435 False 0 IIT_4_0_2026 (0, 1, 2)
(1, 0, 0) 0.133873 0.066937 True 0.026941 0.003492 0.030059 False 0 IIT_4_0_2026 (0, 1, 2)
(0, 1, 0) 0.000000 0.000000 False NaN 0.203006 0.335151 False 0 IIT_4_0_2026 (0, 1, 2)
(1, 1, 0) 0.000000 0.000000 False NaN 0.407902 0.137647 False 0 IIT_4_0_2026 (0, 1, 2)
(0, 0, 1) 0.000000 0.000000 False NaN 0.407902 0.137647 False 0 IIT_4_0_2026 (0, 1, 2)
(1, 0, 1) 0.000000 0.000000 False NaN 0.203006 0.335151 False 0 IIT_4_0_2026 (0, 1, 2)
(0, 1, 1) 0.133873 0.066937 True 0.026941 0.003492 0.030059 False 0 IIT_4_0_2026 (0, 1, 2)
(1, 1, 1) 0.067358 0.033679 True 0.044650 1.182755 0.903435 False 0 IIT_4_0_2026 (0, 1, 2)

Each row is one system-level integrated information analysis (an SIA). The index holds the axis that varied — here, the state — and the columns hold the extracted quantities: phi, normalized_phi, whether the system is irreducible, and the selection margins (partition_margin, cause_state_margin, effect_state_margin, effectively_tied — see Control tie-breaking). The axes that did not vary appear as constant context columns (formalism, subset).

States that cannot be reached from any previous state have no defined repertoire, so their \(\Phi\) is undefined. When you enumerate an axis with "all", those cells are dropped rather than raised, and the dropped cells are recorded on the result. Our probabilistic substrate reaches every state, so nothing was dropped here:

result.skipped
[]

With a deterministic substrate you can see this happen — two of the eight states of the three-gate basic network are unreachable:

pyphi.sweep(pyphi.examples.basic_substrate(), states="all").skipped
[(0, 'IIT_4_0_2026', (0, 1, 2), (0, 1, 0)),
 (0, 'IIT_4_0_2026', (0, 1, 2), (0, 1, 1))]

The result object#

sweep returns a SweepResult with three fields:

  • df — the tidy table (also available as result.to_pandas()).

  • results — the raw result objects, aligned one-to-one with the rows of df, so you can reach into any cell for detail that is not in the table.

  • skipped — the (formalism, subset, state) cells dropped as unreachable.

type(result.results[0]).__name__
'SystemIrreducibilityAnalysis'

Sweeping over subsystems#

The subsets argument chooses which subsets of nodes to treat as the candidate system. Pass "all" for the non-empty powerset, "full" (the default) for the whole substrate, or an explicit list of node-index tuples. Here we hold the state fixed and vary the subsystem:

pyphi.sweep(substrate, states=(0, 1, 1), subsets="all").df.round(6)
phi normalized_phi is_irreducible partition_margin cause_state_margin effect_state_margin effectively_tied substrate formalism state
subset
(0,) 0.038891 0.038891 True NaN 0.077118 0.107132 False 0 IIT_4_0_2026 (0, 1, 1)
(1,) 0.001606 0.001606 True NaN 0.202298 0.004903 False 0 IIT_4_0_2026 (0, 1, 1)
(2,) 0.212220 0.212220 True NaN 0.374172 0.497867 False 0 IIT_4_0_2026 (0, 1, 1)
(0, 1) 0.040497 0.040497 True 0.213401 0.760812 1.097995 False 0 IIT_4_0_2026 (0, 1, 1)
(0, 2) 0.000000 0.000000 False NaN NaN NaN False 0 IIT_4_0_2026 (0, 1, 1)
(1, 2) 0.000000 0.000000 False NaN NaN NaN False 0 IIT_4_0_2026 (0, 1, 1)
(0, 1, 2) 0.133873 0.066937 True 0.026941 0.003492 0.030059 False 0 IIT_4_0_2026 (0, 1, 1)

When more than one axis varies at once, the index becomes a MultiIndex with one level per varying axis. The formalisms= argument adds one more axis, for computing the same cells under earlier versions of IIT; see Reproduce results from earlier versions of IIT.

Choosing what to compute#

By default each cell computes an SIA. Pass compute="ces" to compute the full cause-effect structure instead. The extracted columns change to match: the number of distinctions and the summed relation \(\varphi\).

pyphi.sweep(substrate, states="all", compute="ces").df.round(6)
phi n_distinctions sum_phi_r substrate formalism subset
state
(0, 0, 0) 0.067358 2 0.070801 0 IIT_4_0_2026 (0, 1, 2)
(1, 0, 0) 0.133873 6 2.917676 0 IIT_4_0_2026 (0, 1, 2)
(0, 1, 0) 0.000000 4 0.908204 0 IIT_4_0_2026 (0, 1, 2)
(1, 1, 0) 0.000000 2 0.214175 0 IIT_4_0_2026 (0, 1, 2)
(0, 0, 1) 0.000000 2 0.214175 0 IIT_4_0_2026 (0, 1, 2)
(1, 0, 1) 0.000000 4 0.908204 0 IIT_4_0_2026 (0, 1, 2)
(0, 1, 1) 0.133873 6 2.917676 0 IIT_4_0_2026 (0, 1, 2)
(1, 1, 1) 0.067358 2 0.070801 0 IIT_4_0_2026 (0, 1, 2)

compute also accepts any callable taking a System. The callable’s return value is stored in result.results for every cell; reach into that list to work with whatever it returns.

Find near-tied cells#

The margin columns make it a one-liner to find the cells whose selections were effectively tied at the configured precision — the results whose reported partitions or specified states are sensitive to tie-breaking rules — none, for this asymmetric substrate:

tied = result.df[result.df.effectively_tied.astype(bool)]
tied[["phi", "partition_margin", "cause_state_margin", "effect_state_margin"]].round(6)
phi partition_margin cause_state_margin effect_state_margin
state

Cells computed under IIT 3.0, which does not report margins, have None in these columns.

Running in parallel#

Set parallel=True to spread the cells across worker processes. Each cell is a whole SIA or CES computation, so this parallelizes at the level of the sweep rather than inside any single computation. Passing None (the default) follows config.infrastructure.parallel.

pyphi.sweep(substrate, states="all", parallel=True).df.round(6)["phi"]
state
(0, 0, 0)    0.067358
(1, 0, 0)    0.133873
(0, 1, 0)    0.000000
(1, 1, 0)    0.000000
(0, 0, 1)    0.000000
(1, 0, 1)    0.000000
(0, 1, 1)    0.133873
(1, 1, 1)    0.067358
Name: phi, dtype: float64

The results are returned in the same order whether or not the sweep runs in parallel, so the table is identical either way.

Reproducibility#

Pass a seed to stamp it into every result’s provenance record, so a saved sweep records the seed that produced it alongside the numbers:

seeded = pyphi.sweep(substrate, states="all", seed=42)
seeded.df.shape
(8, 10)