The IIT 4.0 demo notebook#
This notebook is the supplement to the IIT 4.0 paper (Albantakis et al. 2023). It walks through a full IIT 4.0 analysis in PyPhi and reproduces the paper’s numbers on the paper’s own example systems.
The paper’s \(\varphi_s\) values were computed before the system’s intrinsic information became part of the definition of \(\varphi_s\), so the notebook pins the version of IIT the paper used (see Reproduce results from earlier versions of IIT).
Part 1 works through a System Irreducibility Analysis one IIT postulate at a time (intrinsicality, information, integration (\(\varphi_s\)), exclusion, and composition) on the deterministic three-node A/B/C system from Figure 8C. Part 2 reproduces Figures 1, 2, and 4 on the nondeterministic Ising network used in the main text, going deeper into the algorithmic computation of complexes, distinctions, and relations.
The outputs shown here are stored with the notebook. For the theory behind each step, see the Theory section; for a shorter hands-on tour, see A complete worked example.
PyPhi is a software toolbox that provides the reference implementation of Integrated Information Theory (IIT).
This notebook accompanies the IIT 4.0 paper, available here, and is written for PyPhi 2.0.
Installing PyPhi#
First, we use pip to install PyPhi and its dependencies:
# Colab: install PyPhi first. Skip this cell in a local environment that has it.
!python -m pip install -U git+https://github.com/wmayner/pyphi.git@main
import pyphi
import numpy as np
from pyphi.measures.distribution import (
resolve_mechanism_measure,
resolve_system_measure,
)
pyphi.config.progress_bars = False
pyphi.config.parallel = False
# This notebook deliberately evaluates every system partition, so we turn off
# the short-circuit that stops as soon as a system is found to be reducible.
pyphi.config.shortcircuit_sia = False
# Reproduce the paper's values under the version of IIT it used.
import warnings
from pyphi.conf import presets
with warnings.catch_warnings():
warnings.simplefilter("ignore") # advisory config-change notices
pyphi.config.iit = presets.iit4_2023["iit"]
# The high-level entry points (`pyphi.analyze`, `System.sia`, ...) resolve the
# measures for intrinsic information and system integration from config
# automatically. A few cells below call the lower-level machinery directly,
# which in PyPhi 2.0 takes these measures as explicit arguments; we resolve
# them here from the current configuration so those cells can pass them in.
specification_measure = resolve_mechanism_measure(
pyphi.config.formalism.iit.specification_measure
)
system_measure = resolve_system_measure(
pyphi.config.formalism.iit.system_phi_measure
)
<function pyphi.measures.distribution.generalized_intrinsic_difference(forward_repertoire: 'ArrayLike', partitioned_forward_repertoire: 'ArrayLike', selectivity_repertoire: 'ArrayLike', state: 'State | None' = None) -> 'Repertoire | float'>
Defining a substrate#
The first part of the tutorial will be based on the second deterministic system from Figure 8C of the IIT 4.0 paper: “Functionally equivalent networks with different Φ-structures”.
Background conditions (nodes “I” and “O”) have already been fixed to “I”=ON, “O”=ON in the hypothetical 5-node Transition Probability Matrix (TPM). Therefore, we will start from a TPM which only contains nodes “A”, “B”, “C”.
node_labels = ("A", "B", "C")
# TPM in state-by-node format (see `help(pyphi.Substrate)` for information on TPM
# representations).
tpm = np.array(
[
[1, 0, 0],
[0, 1, 0],
[1, 1, 1],
[0, 1, 1],
[0, 0, 0],
[1, 1, 0],
[0, 0, 1],
[1, 0, 1],
]
)
# The network's adjacency matrix. In its absence, PyPhi will assume all-to-all
# connectivity. PyPhi uses the "from-to" convention: a "1" in entry (i,j) means
# there is a directed edge from unit i to unit j.
# NOTE: If the adjacency matrix indicates a connection between units when there
# is no causal connection according to the TPM, the analysis will correctly
# determine that; however, if the matrix specifies the *lack* of a connection,
# PyPhi will assume no causal influence and short-circuit the analysis, even if
# the TPM implies that there is.
connectivity_matrix = np.array([
[1, 1, 0,], # A->A, A->B
[0, 1, 1,], # B->B, B->C
[1, 1, 1,], # C->A, C->B, C->C
])
array([[1, 1, 0],
[0, 1, 1],
[1, 1, 1]])
We begin by instantiating a Substrate object. This represents the “universal”
physical substrate, which serves as the operational starting point to assess the
causal powers and intrinsic properties of a system within it.
See the theory: Substrate and system.
substrate = pyphi.Substrate(tpm, cm=connectivity_matrix, node_labels=node_labels)
substrate
| Units | A, B, C |
|---|---|
| State space | binary |
| A | B | C | |
|---|---|---|---|
| A | 1 | 1 | · |
| B | · | 1 | 1 |
| C | 1 | 1 | 1 |
| state | A | B | C |
|---|---|---|---|
| (0,0,0) | 1.0 | 0.0 | 0.0 |
| (1,0,0) | 0.0 | 1.0 | 0.0 |
| (0,1,0) | 1.0 | 1.0 | 1.0 |
| (1,1,0) | 0.0 | 1.0 | 1.0 |
| (0,0,1) | 0.0 | 0.0 | 0.0 |
| (1,0,1) | 1.0 | 1.0 | 0.0 |
| (0,1,1) | 0.0 | 0.0 | 1.0 |
| (1,1,1) | 1.0 | 0.0 | 1.0 |
The whole analysis in one call#
Before unfolding the analysis one postulate at a time, it is worth seeing the
easy path. Given a substrate and a state of interest, a single call to
pyphi.analyze runs the entire IIT 4.0 pipeline: it identifies the complex,
computes its system integrated information (\(φ_s\)), and unfolds the full
cause-effect structure of distinctions and relations that sums to big \(Φ\).
We analyze the substrate in state (1, 0, 0) (A=ON, B=OFF, C=OFF; “Abc” in the
paper’s notation, where lowercase letters denote units in the OFF state).
state = (1, 0, 0)
analysis = pyphi.analyze(substrate, state)
analysis
| Φ | 3.74674 |
|---|---|
| φs | 2.0 |
| Distinctions | 4 |
| Σφd | 1.79248 |
| Relations | 10 |
| Σφr | 1.95426 |
| Formalism | IIT_4_0_2023 |
| Mechanism | φd | Cause purview | Effect purview |
|---|---|---|---|
| A | 0.438722 | Ac | b |
| b | 0.5 | Ab | c |
| c | 0.438722 | b | b |
| Ab | 0.415037 | Abc | bc |
| Units | A,B,C |
|---|---|
| Current state | (1, 0, 0) |
| Specified cause state | (0, 0, 0) |
| Specified effect state | (0, 1, 0) |
| MIP | 3 parts: {A,B,C} |
| ii(s) | 0.0 |
The resulting Analysis object bundles the whole result together: the System
Irreducibility Analysis at analysis.sia, its \(φ_s\) at analysis.phi, and the
cause-effect structure at analysis.ces (with analysis.ces.distinctions,
analysis.ces.relations, and analysis.ces.big_phi). The rest of Part 1
rebuilds this same result step by step, one IIT postulate at a time.
System Irreducibility Analysis: identifying complexes#
Intrinsicality: Define a candidate system#
Now we instantiate a System object over a candidate set of units, evaluated
in the state chosen above. Here the candidate system is the whole substrate,
“Abc”. A System is built from a substrate, a state, and a tuple of node
indices; any units of the substrate left out of that tuple act as fixed
background conditions, and the TPM is conditioned on their state.
See the theory: Substrate and system.
# The candidate system spans all three units of the substrate.
system = pyphi.System(substrate, state, substrate.node_indices)
system
| Units | A, B, C |
|---|---|
| State | A=1, B=0, C=0 |
| Substrate | 3 units |
| A | B | C | |
|---|---|---|---|
| A | 1 | 1 | · |
| B | · | 1 | 1 |
| C | 1 | 1 | 1 |
| state | A | B | C |
|---|---|---|---|
| (0,0,0) | 1.0 | 0.0 | 0.0 |
| (1,0,0) | 0.0 | 1.0 | 0.0 |
| (0,1,0) | 1.0 | 1.0 | 1.0 |
| (1,1,0) | 0.0 | 1.0 | 1.0 |
| (0,0,1) | 0.0 | 0.0 | 0.0 |
| (1,0,1) | 1.0 | 1.0 | 0.0 |
| (0,1,1) | 0.0 | 0.0 | 1.0 |
| (1,1,1) | 1.0 | 0.0 | 1.0 |
| state | A | B | C |
|---|---|---|---|
| (0,0,0) | 1.0 | 0.0 | 0.0 |
| (1,0,0) | 0.0 | 1.0 | 0.0 |
| (0,1,0) | 1.0 | 1.0 | 1.0 |
| (1,1,0) | 0.0 | 1.0 | 1.0 |
| (0,0,1) | 0.0 | 0.0 | 0.0 |
| (1,0,1) | 1.0 | 1.0 | 0.0 |
| (0,1,1) | 0.0 | 0.0 | 1.0 |
| (1,1,1) | 1.0 | 0.0 | 1.0 |
From the System Irreducibility Analysis (SIA), we can see that the
candidate system in state (1, 0, 0) exists (it has both cause and effect power, since \(\textrm{ii}_c > 0\) and \(\textrm{ii}_e > 0\)) in a way that is intrinsic (upon the system
Abc itself), informative (it picks out specific previous and next states), and
integrated (\(φ_s\) > 0 for the minimum partition).
sia = system.sia()
sia
| φs | 2.0 |
|---|---|
| Normalized φs | 0.4 |
| System | A,B,C |
| Current state | (1, 0, 0) |
| Specified state | (0, 0, 0) |
|---|---|
| Intrinsic information | 3.0 |
| Intrinsic differentiation | 0.0 |
| Specified state | (0, 1, 0) |
|---|---|
| Intrinsic information | 3.0 |
| Intrinsic differentiation | 0.0 |
| Partition | 3 parts: {A,B,C} |
|---|---|
| Tied MIPs | 0 |
| A | B | C | |
|---|---|---|---|
| A | · | · | ✕ |
| B | ✕ | · | ✕ |
| C | ✕ | ✕ | · |
The minimum partition (MIP) is shown below. It cuts the candidate system into three parts; the ✕ marks indicate the connections that are severed. Note that system partitions are directed, so the matrix may be asymmetric.
See the theory: System integrated information.
sia.partition
3 parts: {A,B,C}| Connections cut | 5 |
|---|
| A | B | C | |
|---|---|---|---|
| A | · | · | ✕ |
| B | ✕ | · | ✕ |
| C | ✕ | ✕ | · |
We can access properties which hold the corresponding system integrated information (\(φ_s\), its irreducibility), and a normalized system integrated information (\(φ_s\) divided by the maximum \(φ_s\) that could be obtained for a system of three nodes).
print(f"φ_s = {sia.phi}")
print(f"φ_s_norm = {sia.normalized_phi}")
φ_s = 2.0
φ_s_norm = 0.4
By the principle of minimal existence, \(φ_s\) must be the minimum between the \(φ\)
of the maximally irreducible cause and the maximally irreducible effect within
the candidate system itself. For this particular subsystem at the
current particular state, cause and effect are equally irreducible.
print(sia.cause.phi)
print(sia.effect.phi)
2.0
2.0
Information: Maximal cause-effect state#
See the theory: System integrated information.
Let’s look more closely at the selected effect state:
sia.effect
| φ | 2.0 |
|---|---|
| Normalized φ | 0.4 |
| Direction | EFFECT |
| Mechanism | [A,B,C] |
| Purview | [A,B,C] |
| Specified state | (0, 1, 0) |
| Intrinsic information | 3.0 |
| Selectivity | 1.0 |
| Forward probability | 1.0 |
|---|---|
| Partitioned forward probability | 0.25 |
| Partition | 3 parts: {A,B,C} |
|---|
| State ties | 0 |
|---|---|
| Partition ties | 0 |
Recall that \(φ_s\) is evaluated for a specific cause or effect state, which is defined as the state that maximizes intrinsic information (\(\textrm{ii}\)). We can inspect this state:
sia.effect.specified_state
| Direction | EFFECT |
|---|---|
| Purview | aBc |
| Specified state | (0, 1, 0) |
| Intrinsic information | 3.0 |
Let’s go deeper into PyPhi’s API to recompute the specified effect state:
maximal_effect_state = system.intrinsic_information(
direction=pyphi.Direction.EFFECT,
mechanism=system.node_indices,
purview=system.node_indices,
specification_measure=specification_measure,
)
maximal_effect_state
| Direction | EFFECT |
|---|---|
| Purview | aBc |
| Specified state | (0, 1, 0) |
| Intrinsic information | 3.0 |
Integration: System integrated information (\(φ_s\))#
See the theory: System integrated information.
Let’s use PyPhi to recompute sia.effect and \(φ_s\). We will use the fact
that we already know the MIP to avoid going through all partitions.
# NOTE: the full set of system partitions may be manually computed by invoking:
system_partitions = pyphi.partition.system_partitions(
system.node_indices, node_labels=system.node_labels
)
<generator object _directed_set_partitions at 0x10cc9ae30>
We can now compute the integration value with respect to a direction, sia.partition, and the maximal_effect_state we
had already obtained.
maximal_cause_effect_states = pyphi.formalism.iit4.system_intrinsic_information(
system, specification_measure=specification_measure
)
pyphi.formalism.iit4.integration_value(
pyphi.Direction.EFFECT,
system,
sia.partition,
maximal_cause_effect_states,
system_measure=system_measure,
)
| φ | 2.0 |
|---|---|
| Normalized φ | 0.4 |
| Direction | EFFECT |
| Mechanism | [A,B,C] |
| Purview | [A,B,C] |
| Specified state | (0, 1, 0) |
| Intrinsic information | 3.0 |
| Selectivity | 1.0 |
| Forward probability | 1.0 |
|---|---|
| Partitioned forward probability | 0.25 |
| Partition | 3 parts: {A,B,C} |
|---|
| State ties | 0 |
|---|---|
| Partition ties | 0 |
Exclusion: Identifying the first complex#
See the theory: System integrated information.
We know the whole Abc is irreducible, but is it maximally irreducible? We will go through all its subsets, computing system irreducibility analyses for all of them.
Indeed, in this case Abc wins over all its subsets (note that in general, the largest subset is not necessarily the maximally irreducible one).
for candidate_nodes in pyphi.utils.powerset(node_labels, nonempty=True):
node_indices = tuple(
substrate.node_labels.index(label) for label in candidate_nodes
)
try:
system = pyphi.System(substrate, state, node_indices)
sia = system.sia()
print(f"φ_{candidate_nodes} = {sia.phi}")
except pyphi.exceptions.StateUnreachableError:
print(f"φ_{candidate_nodes} = 0.0")
φ_('A',) = 1.0
φ_('B',) = 0.0
φ_('C',) = 0.0
φ_('A', 'B') = 0.0
φ_('A', 'C') = 0.0
φ_('B', 'C') = 0.0
φ_('A', 'B', 'C') = 2.0
Composition: Unfolding Φ-structure#
See the theory: Distinctions and relations.
Now that the maximal complex or physical substrate of consciousness has been identified, we can “unfold” the cause-effect structure to examine the compositional structure of the distinctions it specifies and the relations among them.
The maximal complex here is the whole substrate, so we rebuild that System
explicitly and keep referring to it as the candidate system.
system = pyphi.System(substrate, state, substrate.node_indices)
| Units | A, B, C |
|---|---|
| State | A=1, B=0, C=0 |
| Substrate | 3 units |
| A | B | C | |
|---|---|---|---|
| A | 1 | 1 | · |
| B | · | 1 | 1 |
| C | 1 | 1 | 1 |
| state | A | B | C |
|---|---|---|---|
| (0,0,0) | 1.0 | 0.0 | 0.0 |
| (1,0,0) | 0.0 | 1.0 | 0.0 |
| (0,1,0) | 1.0 | 1.0 | 1.0 |
| (1,1,0) | 0.0 | 1.0 | 1.0 |
| (0,0,1) | 0.0 | 0.0 | 0.0 |
| (1,0,1) | 1.0 | 1.0 | 0.0 |
| (0,1,1) | 0.0 | 0.0 | 1.0 |
| (1,1,1) | 1.0 | 0.0 | 1.0 |
| state | A | B | C |
|---|---|---|---|
| (0,0,0) | 1.0 | 0.0 | 0.0 |
| (1,0,0) | 0.0 | 1.0 | 0.0 |
| (0,1,0) | 1.0 | 1.0 | 1.0 |
| (1,1,0) | 0.0 | 1.0 | 1.0 |
| (0,0,1) | 0.0 | 0.0 | 0.0 |
| (1,0,1) | 1.0 | 1.0 | 0.0 |
| (0,1,1) | 0.0 | 0.0 | 1.0 |
| (1,1,1) | 1.0 | 0.0 | 1.0 |
Distinctions#
Distinctions are parts of the complex that specify maximally-irreducible cause and effect states over other parts of the complex. For example, does mechanism “A” specify an irreducible distinction? Yes, it does (it has cause ac and effect B):
mechanism = (0,)
distinction = system.distinction(mechanism)
# NOTE: Mechanism partitions are represented as fractions, with mechanism parts
# in the numerator and purview parts in the denominator.
distinction
| Mechanism | A |
|---|---|
| φd | 0.438722 |
| Purview | Ac |
|---|---|
| φ | 0.5 |
| Specified state | (0, 0) |
| Purview | b |
|---|---|
| φ | 0.438722 |
| Specified state | (1,) |
Does the higher-order mechanism “Ab” specify an irreducible distinction? It does:
mechanism = (0, 1)
distinction = system.distinction(mechanism)
distinction
| Mechanism | Ab |
|---|---|
| φd | 0.415037 |
| Purview | Abc |
|---|---|
| φ | 0.5 |
| Specified state | (0, 0, 0) |
| Purview | b |
|---|---|
| φ | 0.415037 |
| Specified state | (1,) |
What about mechanism “Ac”? This mechanism does not specify a distinction, because it is reducible on the cause side:
mechanism = (0, 2)
distinction = system.distinction(mechanism)
distinction
| Mechanism | Ac |
|---|---|
| φd | 0.0 |
| Purview | bc |
|---|---|
| φ | 0.0 |
| Specified state | (0, 0) |
| Purview | ∅ |
|---|---|
| φ | 0.0 |
| Specified state | None |
Relations#
See the theory: Distinctions and relations.
Causal relations capture how the causes and/or effects of a set of distinctions overlap with each other.
We can obtain all 4 irreducible distinctions (mechanisms “A”, “b”, “c”, “Ab”), and then use them to find causal relations among them.
# Unfold all the irreducible distinctions in the complex.
distinctions = system.all_distinctions()
| Distinctions | 4 |
|---|---|
| Σφd | 1.79248 |
| Mechanism | φd | Cause purview | Effect purview |
|---|---|---|---|
| A | 0.438722 | Ac | b |
| b | 0.5 | Ab | c |
| c | 0.438722 | b | b |
| Ab | 0.415037 | Abc | b |
NOTE: Before computing relations, it is important to filter out distinctions that are incongruent with the state specified by the entire candidate system (this is done automatically by pyphi.analyze, whose cause-effect structure is discussed below).
distinctions = distinctions.resolve_congruence(sia.system_state)
| Distinctions | 4 |
|---|---|
| Σφd | 1.79248 |
| Mechanism | φd | Cause purview | Effect purview |
|---|---|---|---|
| A | 0.438722 | Ac | b |
| b | 0.5 | Ab | c |
| c | 0.438722 | b | b |
| Ab | 0.415037 | Abc | bc |
Now we can compute all relations among these distinctions:
relations = pyphi.relations.relations(distinctions)
relations
| Relations | 10 |
|---|---|
| Σφr | 1.95426 |
The output functions like a set object containing all the relations. Each relation in turn also functions like a set that contains the distinctions that are related. Additionally, relations have
A
relation.phivalue.relation.faces: A set of relation faces, each corresponding to an overlap among the purviews of some causes and effectsrelation.purview: The union of the face purviews (or, equivalently, the intersection of the distinction cause/effect purview unions).
Let’s examine the relation between the first two distinctions.
relation = pyphi.relations.relation(distinctions[:2])
relation
| φr | 0.292481 |
|---|---|
| Purview | [a, c] |
| Degree | 2 |
| Faces | 2 |
We can look at the relation’s faces:
relation.faces
frozenset({╭─ RelationFace ─╮
│ Purview [c] │
│ Relata 2 │
╰────────────────╯,
╭─ RelationFace ─╮
│ Purview [a] │
│ Relata 2 │
╰────────────────╯})
As with collections of relations and relations themselves, a relation face also functions like a set. It contains the causes/effects which have a nonempty congruent overlap among their purviews.
This congruent overlap is stored in the purview attribute:
face = next(iter(relation.faces))
face.purview
{c}
Cause-effect structure and big Φ#
See the theory: The Φ-structure.
For convenience, PyPhi can fully unfold the cause-effect structure of a system
in a single call, performing all the method calls described above. When the
system is a maximal complex (as found in the System Irreducibility Analysis
above), its cause-effect structure is called a Φ-structure. This is exactly the
analysis.ces we obtained from pyphi.analyze at the very start.
ces = pyphi.analyze(substrate, state).ces
ces
| Φ | 3.74674 |
|---|---|
| φs | 2.0 |
| Distinctions | 4 |
| Σφd | 1.79248 |
| Relations | 10 |
| Σφr | 1.95426 |
| Mechanism | φd | Cause purview | Effect purview |
|---|---|---|---|
| A | 0.438722 | Ac | b |
| b | 0.5 | Ab | c |
| c | 0.438722 | b | b |
| Ab | 0.415037 | Abc | bc |
Individual properties and components of the structure remain accessible:
# φ_s
print(f"φ_s = {sia.phi}")
# Big Φ (sum of distinctions' and relations' small φ)
print(f" Φ = {ces.big_phi}")
φ_s = 2.0
Φ = 3.746741665224354
ces.distinctions
| Distinctions | 4 |
|---|---|
| Σφd | 1.79248 |
| Mechanism | φd | Cause purview | Effect purview |
|---|---|---|---|
| A | 0.438722 | Ac | b |
| b | 0.5 | Ab | c |
| c | 0.438722 | b | b |
| Ab | 0.415037 | Abc | bc |
ces.relations
| Relations | 10 |
|---|---|
| Σφr | 1.95426 |
Reproducing figures 1, 2 and 4#
The following sections are intended to reinforce and clarify certain aspects of the analysis by delving deeper into the algorithmic computation of distinctions and relations. For this we will use the nondeterministic substrate employed in Figure 1, Figure 2 and Figure 4 in the main IIT 4.0 text, a small recurrent network with both excitation and inhibition, analyzed using PyPhi to reproduce the results shown there.
See the theory: What IIT 4.0 computes.
We start by defining the substrate and the state of interest:
k = 4
weights = np.array(
[
[-.2, 0.7, 0.2],
[0.7, -.2, 0.0],
[0.0, -.8, 0.2]
]
)
node_labels = ["A", "B", "C"]
substrate = pyphi.substrate_generator.build_substrate(
"ising",
weights,
node_labels=node_labels,
temperature=1 / k,
)
state = (0, 1, 1)
(0, 1, 1)
Figure 1: Identifying complexes#
See the theory: System integrated information.
Let’s jump straight into panel (E) in Fig 1, by performing a combinatorial search over all possible candidate subsystems, applying exclusion to find the first maximally-irreducible complex, then repeating the procedure with the remaining units to find the second maximally-irreducible complex and so on.
As shown in Fig 1, the substrate aBC condenses into complexes C and aB:
# System Irreducibility Analyses
sias = []
for candidate_nodes in pyphi.utils.powerset(node_labels, nonempty=True):
node_indices = tuple(
substrate.node_labels.index(label) for label in candidate_nodes
)
try:
system = pyphi.System(substrate, state, node_indices)
sias.append(system.sia())
except pyphi.exceptions.StateUnreachableError:
pass
sias = sorted(sias, key=lambda sia: sia.phi, reverse=True)
print(sias)
[╭─ SystemIrreducibilityAnalysis ───────╮
│ φ_s 0.21222 │
│ Normalized φ_s 0.21222 │
│ System C │
│ Current state (1,) │
├─ Cause ──────────────────────────────┤
│ Specified state (1,) │
│ Intrinsic information 0.21222 │
│ Intrinsic differentiation 0.663793 │
├─ Effect ─────────────────────────────┤
│ Specified state (1,) │
│ Intrinsic information 0.29106 │
│ Intrinsic differentiation 1.0 │
├─ MIP ────────────────────────────────┤
│ Partition Total │
│ Tied MIPs 0 │
│ C │
│ C ✕ │
╰──────────────────────────────────────╯, ╭─ SystemIrreducibilityAnalysis ─────────────╮
│ φ_s 0.171863 │
│ Normalized φ_s 0.171863 │
│ System A,B │
│ Current state (0, 1) │
├─ Cause ────────────────────────────────────┤
│ Specified state (1, 0) │
│ Intrinsic information 0.843228 │
│ Intrinsic differentiation 0.663964 │
├─ Effect ───────────────────────────────────┤
│ Specified state (1, 0) │
│ Intrinsic information 1.09763 │
│ Intrinsic differentiation 0.0404968 │
├─ MIP ──────────────────────────────────────┤
│ Partition 2 parts: {A,B} │
│ Tied MIPs 0 │
│ A B │
│ A · ✕ │
│ B · · │
╰────────────────────────────────────────────╯, ╭─ SystemIrreducibilityAnalysis ──────────────╮
│ φ_s 0.133873 │
│ Normalized φ_s 0.0669366 │
│ System A,B,C │
│ Current state (0, 1, 1) │
├─ Cause ─────────────────────────────────────┤
│ Specified state (1, 0, 0) │
│ Intrinsic information 0.718749 │
│ Intrinsic differentiation 1.26848 │
├─ Effect ────────────────────────────────────┤
│ Specified state (1, 0, 0) │
│ Intrinsic information 0.841796 │
│ Intrinsic differentiation 1.0405 │
├─ MIP ───────────────────────────────────────┤
│ Partition 2 parts: {AB,C} │
│ Tied MIPs 0 │
│ A B C │
│ A · · · │
│ B · · · │
│ C ✕ ✕ · │
╰─────────────────────────────────────────────╯, ╭─ SystemIrreducibilityAnalysis ────────╮
│ φ_s 0.039591 │
│ Normalized φ_s 0.039591 │
│ System A │
│ Current state (0,) │
├─ Cause ───────────────────────────────┤
│ Specified state (1,) │
│ Intrinsic information 0.039591 │
│ Intrinsic differentiation 0.924838 │
├─ Effect ──────────────────────────────┤
│ Specified state (1,) │
│ Intrinsic information 0.0684421 │
│ Intrinsic differentiation 0.0388909 │
├─ MIP ─────────────────────────────────┤
│ Partition Total │
│ Tied MIPs 0 │
│ A │
│ A ✕ │
╰───────────────────────────────────────╯, ╭─ SystemIrreducibilityAnalysis ─────────╮
│ φ_s 0.00315846 │
│ Normalized φ_s 0.00315846 │
│ System B │
│ Current state (1,) │
├─ Cause ────────────────────────────────┤
│ Specified state (0,) │
│ Intrinsic information 0.108312 │
│ Intrinsic differentiation 0.810094 │
├─ Effect ───────────────────────────────┤
│ Specified state (0,) │
│ Intrinsic information 0.00315846 │
│ Intrinsic differentiation 0.00160594 │
├─ MIP ──────────────────────────────────┤
│ Partition Total │
│ Tied MIPs 0 │
│ B │
│ B ✕ │
╰────────────────────────────────────────╯, ╭─ NullSystemIrreducibilityAnalysis ──────╮
│ φ_s 0.0 │
│ Normalized φ_s 0.0 │
│ System A,C │
│ Current state None │
├─ MIP ───────────────────────────────────┤
│ Partition NullCut((0, 2)) │
│ Tied MIPs 0 │
├─ Reasons ───────────────────────────────┤
│ NO_STRONG_CONNECTIVITY │
╰─────────────────────────────────────────╯, ╭─ NullSystemIrreducibilityAnalysis ──────╮
│ φ_s 0.0 │
│ Normalized φ_s 0.0 │
│ System B,C │
│ Current state None │
├─ MIP ───────────────────────────────────┤
│ Partition NullCut((1, 2)) │
│ Tied MIPs 0 │
├─ Reasons ───────────────────────────────┤
│ NO_STRONG_CONNECTIVITY │
╰─────────────────────────────────────────╯]
Figure 2: Distinctions#
See the theory: Distinctions and relations.
We now want to derive all the irreducible distinctions in complex aB, both first-order and higher-order together with their \(φ_d\) values. A more detailed derivation with intermediate steps (intrinsic information, MIP, maximal cause/effect states) is developed in later sections.
# Select the System Irreducibility Analysis corresponding to complex aB:
sia = sias[1]
nodes = sia.node_indices # Same as nodes = (0, 1)
# Obtain the system.
system = pyphi.System(substrate, state, nodes)
# Unfold all the irreducible distinctions in complex aB.
distinctions = system.all_distinctions()
# Discard potential distinctions which are incongruent with the cause and
# effect specified by the complex as a whole.
distinctions = distinctions.resolve_congruence(sia.system_state)
print(distinctions)
╭─ ResolvedDistinctions ─────────────────────────────────╮
│ Distinctions 3 │
│ Σφ_d 0.727763 │
├─ Distinctions ─────────────────────────────────────────┤
│ Mechanism φ_d Cause purview Effect purview │
│ a 0.332728 B B │
│ B 0.323594 a aB │
│ aB 0.0714404 B aB │
╰────────────────────────────────────────────────────────╯
A more detailed derivation#
# Change this to explore different mechanisms.
mechanism = (0,1)
(0, 1)
Intrinsic information (\(\textrm{ii}\)) and the specified cause/effect state.#
DIRECTION = pyphi.Direction.CAUSE
SYSTEM = system
# Explore all cause purviews.
purviews = SYSTEM.potential_purviews(DIRECTION, mechanism)
for purview in purviews:
# Find specified state for this particular purview: the state for which
# intrinsic information is maximum.
specified_states = SYSTEM.intrinsic_information(
DIRECTION, mechanism, purview,
specification_measure=specification_measure,
).ties
print(specified_states)
(╭─ Specified CAUSE ────────────────╮
│ Direction CAUSE │
│ Purview Ab │
│ Specified state (1, 0) │
│ Intrinsic information 0.843228 │
╰──────────────────────────────────╯,)
(╭─ Specified CAUSE ────────────────╮
│ Direction CAUSE │
│ Purview b │
│ Specified state (0,) │
│ Intrinsic information 0.873686 │
╰──────────────────────────────────╯,)
(╭─ Specified CAUSE ────────────────╮
│ Direction CAUSE │
│ Purview A │
│ Specified state (1,) │
│ Intrinsic information 0.397981 │
╰──────────────────────────────────╯,)
Similarly for the effect side:
DIRECTION = pyphi.Direction.EFFECT
SYSTEM = system
# Explore all effect purviews.
purviews = SYSTEM.potential_purviews(DIRECTION, mechanism)
for purview in purviews:
# Find specified state for this particular purview: the state for which
# intrinsic information is maximum.
specified_states = SYSTEM.intrinsic_information(
DIRECTION, mechanism, purview,
specification_measure=specification_measure,
).ties
print(specified_states)
(╭─ Specified EFFECT ──────────────╮
│ Direction EFFECT │
│ Purview Ab │
│ Specified state (1, 0) │
│ Intrinsic information 1.09763 │
╰─────────────────────────────────╯,)
(╭─ Specified EFFECT ───────────────╮
│ Direction EFFECT │
│ Purview b │
│ Specified state (0,) │
│ Intrinsic information 0.336617 │
╰──────────────────────────────────╯,)
(╭─ Specified EFFECT ───────────────╮
│ Direction EFFECT │
│ Purview A │
│ Specified state (1,) │
│ Intrinsic information 0.935547 │
╰──────────────────────────────────╯,)
Minimum partition (MIP) and integrated information (\(\varphi\))#
DIRECTION = pyphi.Direction.CAUSE
SYSTEM = system
# Explore all cause purviews.
purviews = SYSTEM.potential_purviews(DIRECTION, mechanism)
cause_mips = []
for purview in purviews:
# Explore all mechanism partitions for this particular purview and find MIP:
# the partition for which integrated information is minimum.
# NOTE: this call internally recomputes and uses the specified state.
mip = SYSTEM.find_mip(DIRECTION, mechanism, purview)
print(mip)
cause_mips.append(mip)
╭─ RepertoireIrreducibilityAnalysis ────────────────╮
│ φ 0.138809 │
│ Normalized φ 0.0694043 │
│ Direction CAUSE │
│ Mechanism [A,B] │
│ Purview [A,B] │
│ Specified state (1, 0) │
│ Intrinsic information 0.843228 │
│ Selectivity 0.631142 │
├─ Repertoire ──────────────────────────────────────┤
│ Forward probability 0.802601 │
│ Partitioned forward probability 0.689118 │
├─ MIP ─────────────────────────────────────────────┤
│ Partition A/B × B/A × ∅/∅ │
├─ Ties ────────────────────────────────────────────┤
│ State ties 0 │
│ Partition ties 0 │
╰───────────────────────────────────────────────────╯
╭─ RepertoireIrreducibilityAnalysis ────────────────╮
│ φ 0.217943 │
│ Normalized φ 0.217943 │
│ Direction CAUSE │
│ Mechanism [A,B] │
│ Purview [B] │
│ Specified state (0,) │
│ Intrinsic information 0.873686 │
│ Selectivity 0.947459 │
├─ Repertoire ──────────────────────────────────────┤
│ Forward probability 0.58436 │
│ Partitioned forward probability 0.498236 │
├─ MIP ─────────────────────────────────────────────┤
│ Partition A/B × B/∅ × ∅/∅ │
├─ Ties ────────────────────────────────────────────┤
│ State ties 0 │
│ Partition ties 0 │
╰───────────────────────────────────────────────────╯
╭─ RepertoireIrreducibilityAnalysis ────────────────╮
│ φ 0.0932374 │
│ Normalized φ 0.0932374 │
│ Direction CAUSE │
│ Mechanism [A,B] │
│ Purview [A] │
│ Specified state (1,) │
│ Intrinsic information 0.397981 │
│ Selectivity 0.729711 │
├─ Repertoire ──────────────────────────────────────┤
│ Forward probability 0.406069 │
│ Partitioned forward probability 0.371652 │
├─ MIP ─────────────────────────────────────────────┤
│ Partition A/∅ × B/A × ∅/∅ │
├─ Ties ────────────────────────────────────────────┤
│ State ties 0 │
│ Partition ties 0 │
╰───────────────────────────────────────────────────╯
Similarly for the effect side:
DIRECTION = pyphi.Direction.EFFECT
SYSTEM = system
# Explore all effect purviews.
purviews = SYSTEM.potential_purviews(DIRECTION, mechanism)
effect_mips = []
for purview in purviews:
# Explore all mechanism partitions for this particular purview and find MIP:
# the partition for which integrated information is minimum.
# NOTE: this call internally recomputes and uses the specified state.
mip = SYSTEM.find_mip(DIRECTION, mechanism, purview)
print(mip)
effect_mips.append(mip)
╭─ RepertoireIrreducibilityAnalysis ────────────────╮
│ φ 0.0714404 │
│ Normalized φ 0.0357202 │
│ Direction EFFECT │
│ Mechanism [A,B] │
│ Purview [A,B] │
│ Specified state (1, 0) │
│ Intrinsic information 1.09763 │
│ Selectivity 0.97232 │
├─ Repertoire ──────────────────────────────────────┤
│ Forward probability 0.97232 │
│ Partitioned forward probability 0.924041 │
├─ MIP ─────────────────────────────────────────────┤
│ Partition A/B × B/A × ∅/∅ │
├─ Ties ────────────────────────────────────────────┤
│ State ties 0 │
│ Partition ties 0 │
╰───────────────────────────────────────────────────╯
╭─ RepertoireIrreducibilityAnalysis ────────────────╮
│ φ 0.00315846 │
│ Normalized φ 0.00315846 │
│ Direction EFFECT │
│ Mechanism [A,B] │
│ Purview [B] │
│ Specified state (0,) │
│ Intrinsic information 0.336617 │
│ Selectivity 0.998887 │
├─ Repertoire ──────────────────────────────────────┤
│ Forward probability 0.998887 │
│ Partitioned forward probability 0.996701 │
├─ MIP ─────────────────────────────────────────────┤
│ Partition A/B × B/∅ × ∅/∅ │
├─ Ties ────────────────────────────────────────────┤
│ State ties 0 │
│ Partition ties 0 │
╰───────────────────────────────────────────────────╯
╭─ RepertoireIrreducibilityAnalysis ────────────────╮
│ φ 0.0684421 │
│ Normalized φ 0.0684421 │
│ Direction EFFECT │
│ Mechanism [A,B] │
│ Purview [A] │
│ Specified state (1,) │
│ Intrinsic information 0.935547 │
│ Selectivity 0.973403 │
├─ Repertoire ──────────────────────────────────────┤
│ Forward probability 0.973403 │
│ Partitioned forward probability 0.9271 │
├─ MIP ─────────────────────────────────────────────┤
│ Partition A/∅ × B/A × ∅/∅ │
├─ Ties ────────────────────────────────────────────┤
│ State ties 0 │
│ Partition ties 0 │
╰───────────────────────────────────────────────────╯
Maximal cause/effect purviews and \(φ_d\)#
Finally, select the maximal cause and maximal effect purviews for this mechanism. Remember the distinction’s irreducibility (\(φ_d\)) will be the minimum of the two.
# Find maximal cause and effect.
print(max(cause_mips))
print(max(effect_mips))
╭─ RepertoireIrreducibilityAnalysis ────────────────╮
│ φ 0.217943 │
│ Normalized φ 0.217943 │
│ Direction CAUSE │
│ Mechanism [A,B] │
│ Purview [B] │
│ Specified state (0,) │
│ Intrinsic information 0.873686 │
│ Selectivity 0.947459 │
├─ Repertoire ──────────────────────────────────────┤
│ Forward probability 0.58436 │
│ Partitioned forward probability 0.498236 │
├─ MIP ─────────────────────────────────────────────┤
│ Partition A/B × B/∅ × ∅/∅ │
├─ Ties ────────────────────────────────────────────┤
│ State ties 0 │
│ Partition ties 0 │
╰───────────────────────────────────────────────────╯
╭─ RepertoireIrreducibilityAnalysis ────────────────╮
│ φ 0.0714404 │
│ Normalized φ 0.0357202 │
│ Direction EFFECT │
│ Mechanism [A,B] │
│ Purview [A,B] │
│ Specified state (1, 0) │
│ Intrinsic information 1.09763 │
│ Selectivity 0.97232 │
├─ Repertoire ──────────────────────────────────────┤
│ Forward probability 0.97232 │
│ Partitioned forward probability 0.924041 │
├─ MIP ─────────────────────────────────────────────┤
│ Partition A/B × B/A × ∅/∅ │
├─ Ties ────────────────────────────────────────────┤
│ State ties 0 │
│ Partition ties 0 │
╰───────────────────────────────────────────────────╯
Figure 4: Relations#
See the theory: Distinctions and relations.
All \(2^{3} - 1\) relations among distinctions a, B and AB exist.
As in Fig 4, we will focus on the first-order relation {a, aB}, with overlap over purview b and \(φ_r\) = 0.035
By default the relation set is a closed-form summary; individual relations are enumerated explicitly with materialize():
relations = pyphi.relations.relations(distinctions).materialize()
print(f"Total number of relations: {len(relations)}\n")
# Filter out relations other than the one between {aB, a}
a_aB_relation = [
relation for relation in relations if (relation.mechanisms == {(0, 1), (0,)})
]
print(a_aB_relation)
Total number of relations: 7
[╭─ Relation ──────────╮
│ φ_r 0.0357202 │
│ Purview [b] │
│ Degree 2 │
│ Faces 9 │
╰─────────────────────╯]
A more detailed derivation#
Computing a specific relation#
We start by selecting the relevant distinctions:
# Assuming a certain order, we can slice the object with all distinctions to
# only include first (a) and third (aB) ones.
distinctions_a_aB = distinctions[::2]
distinctions_a_aB
| Distinctions | 2 |
|---|---|
| Σφd | 0.404169 |
| Mechanism | φd | Cause purview | Effect purview |
|---|---|---|---|
| a | 0.332728 | B | B |
| aB | 0.0714404 | B | aB |
From the relevant distinctions, PyPhi then computes the combinations of size greater than 2 that share a congruent purview overlap.
Since we are dealing with only two distinctions, there’s only one such combination of distinctions: the pair {a, aB} itself.
# Materialize the combinations so they can be iterated more than once below
# (the underlying helper returns a one-shot generator).
combinations = list(
pyphi.relations._combinations_with_nonempty_congruent_overlap(
distinctions_a_aB,
min_degree=2,
max_degree=None,
)
)
combinations
[frozenset({0, 1})]
We are ready to reconstruct the relation by calling pyphi.relations.Relation:
for combination in combinations:
relation = pyphi.relations.Relation((distinctions_a_aB[i] for i in combination))
print(relation)
╭─ Relation ──────────╮
│ φ_r 0.0357202 │
│ Purview [b] │
│ Degree 2 │
│ Faces 9 │
╰─────────────────────╯
Computing \(φ_r\)#
Then, the relation’s irreducibility value (\(φ_r\)) is defined as the \(φ_d\)-per-unit ratio for the distinction (relatum) with the minimum such value (distinction_phi_per_unique_purview_unit),
weighted by the size of the overlap (len(relation.purview)),
distinction_phi_per_unique_purview_unit = [
relatum.phi / len(relatum.purview_union) for relatum in relation
]
phi_r = len(relation.purview) * min(distinction_phi_per_unique_purview_unit)
print(f"φ_r = {phi_r}")
φ_r = 0.03572021119668863
What next#
An Analysis carries everything computed above. Two common next steps are
saving a cause-effect structure for later and exporting it to a pandas
DataFrame for inspection or plotting.
# Rebuild the Part 1 substrate (the `tpm` and `connectivity_matrix` from the
# start of the notebook are still in scope) and analyze it.
substrate_abc = pyphi.Substrate(
tpm, cm=connectivity_matrix, node_labels=("A", "B", "C")
)
analysis = pyphi.analyze(substrate_abc, (1, 0, 0))
# Save the cause-effect structure to JSON, then load it back.
# (Save `analysis.ces`, not the `Analysis` wrapper.)
pyphi.save(analysis.ces, "ces.json")
reloaded_ces = pyphi.load("ces.json")
# Export the distinctions to a pandas DataFrame.
analysis.ces.to_pandas()
| phi | mechanism_state | cause_purview | cause_state | effect_purview | effect_state | |
|---|---|---|---|---|---|---|
| mechanism | ||||||
| (A,) | 0.438722 | (1,) | (A, C) | (0, 0) | (B,) | (1,) |
| (B,) | 0.500000 | (0,) | (A, B) | (0, 0) | (C,) | (0,) |
| (C,) | 0.438722 | (0,) | (B,) | (0,) | (B,) | (1,) |
| (A, B) | 0.415037 | (1, 0) | (A, B, C) | (0, 0, 0) | (B, C) | (1, 0) |
For more, see the how-to guides:
PyPhi documentation#
See pyphi.readthedocs.io for further information on PyPhi usage and its API.