wmayner / pyphi

A toolbox for integrated information theory.
https://journals.plos.org/ploscompbiol/article?id=10.1371/journal.pcbi.1006343
Other
374 stars 97 forks source link

Initial support for Implicit TPMs #105

Open isacdaavid opened 1 year ago

isacdaavid commented 1 year ago

This might probably require further prettifying, and I have just started writing tests for the new code, documentation, etc. That said, it's mature enough to ask for review and suggestions. Test results are on par with the 4.0 branch, the examples I run are working. Also, I want to get a sense of merge conflicts.

This is what the code is supposed to be doing (also, a mini guide for users):

Example using the 2nd system in fig. 7C in the IIT 4.0 paper:

import pyphi
import numpy as np

node_labels = ("A", "B", "C")

connectivity_matrix = np.array([
    [1, 1, 0,],
    [0, 1, 1,],
    [1, 1, 1,],
])

explicit_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],
])

implicit_tpm = [
    np.array(
        [[[[0., 1.],
            [1., 0.]]],
         [[[1., 0.],
            [0., 1.]]]]
    ),
    np.array(
         [[[[1., 0.],
            [1., 0.]],
           [[0., 1.],
            [1., 0.]]],
          [[[0., 1.],
            [0., 1.]],
           [[0., 1.],
            [1., 0.]]]]
    ),
    np.array(
        [[[[1., 0.],
            [1., 0.]],
           [[0., 1.],
            [0., 1.]]]]
    )
]
>>> network = pyphi.Network(implicit_tpm, node_labels=node_labels, state_space=(("OFF", "ON"),) * 3)
>>> network
Network(
ImplicitTPM((A, B, C)),
cm=[[1 1 0]
 [0 1 1]
 [1 1 1]],
node_labels=NodeLabels(('A', 'B', 'C')),
state_space={'B': ['OFF', 'ON'], 'A': ['OFF', 'ON'], 'C': ['OFF', 'ON']}
)

# P(A_{t+1} | A=0, B=0, C=0)

>>> network.tpm[0, 0, 0]
ImplicitTPM((A, B, C))

# That result returned indexed nodes, behind the scenes. To prove it we can repeat it and then inspect node A: 

>>> network.tpm[0, 0, 0].nodes[0].tpm
ExplicitTPM(
[0. 1.]
)

# That means P(A_{t+1}=OFF | A=0, B=0, C=0) = 0, and P(A_{t+1}=ON | A=0, B=0, C=0) = 1.

# Using state space labels, that would be the same as:

>>> network.tpm[{"B": "OFF", "A": "OFF", "C": "OFF"}].nodes[0].tpm
ExplicitTPM(
[0. 1.]
)

# A different example. P(A_{t+1}=1). This can be achieved by indexing the last dimension, called "Pr".

network.tpm[{"Pr": "ON"}].nodes[0].tpm
ExplicitTPM(
[[[1. 0.]]
 [[0. 1.]]]
)