Transition probability matrix conventions

A Network can be created with a transition probability matrix (TPM) in any of the three forms described below. However, in PyPhi the canonical TPM representation is multidimensional state-by-node form. The TPM will be converted to this form when the Network is built.


Functions for converting TPMs from one form to another are available in the convert module.

State-by-node form

A TPM in state-by-node form is a matrix where the entry \((i,j)\) gives the probability that the \(j^{\textrm{th}}\) node will be ON at time \(t+1\) if the system is in the \(i^{\textrm{th}}\) state at time \(t\).

Multidimensional state-by-node form

A TPM in multidimensional state-by-node form is a state-by-node form that has been reshaped so that it has \(n+1\) dimensions instead of two. The first \(n\) dimensions correspond to each of the \(n\) nodes at time \(t\), while the last dimension corresponds to the probabilities of each node being ON at \(t+1\).

With this form, we can take advantage of NumPy array indexing and use a network state as an index directly:

>>> from pyphi.examples import basic_noisy_selfloop_network
>>> tpm = basic_noisy_selfloop_network().tpm
>>> state = (0, 0, 1)  # A network state is a binary tuple
>>> tpm[state]
array([ 0.919,  0.91 ,  0.756])

This tells us that if the current state is \(N_0 = 0, N_1 = 0, N_2 = 1\), then the for the next state, \(P(N_0 = 1) = 0.919\), \(P(N_1 = 1) = 0.91\) and \(P(N_2 = 1) = 0.756\).


The multidimensional state-by-node form is used throughout PyPhi, regardless of the form that was used to create the Network.

State-by-state form

A TPM in state-by-state form is a matrix where the entry \((i,j)\) gives the probability that the state at time \(t+1\) will be \(j\) if the state at time \(t\) is labeled by \(i\).


When converting a state-by-state TPM to one of the other forms, information may be lost!

This is because the space of possible state-by-state TPMs is larger than the space of state-by-node TPMs (so the conversion cannot be injective). However, if we restrict the state-by-state TPMs to only those that satisfy the conditional independence property, then the mapping becomes bijective.

See Conditional Independence for a more detailed discussion.

Little-endian convention

Even after choosing one of the above representations, there are several ways to write down the TPM.

With both state-by-state and state-by-node TPMs, one is confronted with a choice about which rows correspond to which states. In state-by-state TPMs, this choice must also be made for the columns.

There are two possible choices for the rows. Either the first node changes state every other row:

State at \(t\) \(P(N = ON)\) at \(t+1\)
A, B A B
(0, 0) 0.1 0.2
(1, 0) 0.3 0.4
(0, 1) 0.5 0.6
(1, 1) 0.7 0.8

Or the last node does:

State at \(t\) \(P(N = ON)\) at \(t+1\)
A, B A B
(0, 0) 0.1 0.2
(0, 1) 0.5 0.6
(1, 0) 0.3 0.4
(1, 1) 0.7 0.8

Note that the index \(i\) of a row in a TPM encodes a network state: convert the index to binary, and each bit gives the state of a node. The question is, which node?

Throughout PyPhi, we always choose the first convention—the state of the first node (the one with the lowest index) varies the fastest. So, the least-signficant bit—the one’s place—gives the state of the lowest-index node.

This is analogous to the little-endian convention in organizing computer memory. The other convention, where the highest-index node varies the fastest, is analogous to the big-endian convention (see Endianness).

The rationale for this choice of convention is that the little-endian mapping is stable under changes in the number of nodes, in the sense that the same bit always corresponds to the same node index. The big-endian mapping does not have this property.


Functions to convert states to indices and vice versa, according to either the little-endian or big-endian convention, are available in the convert module.


This applies to only situations where decimal indices are encoding states. Whenever a network state is represented as a list or tuple, we use the only sensible convention: the \(i^{\textrm{th}}\) element gives the state of the \(i^{\textrm{th}}\) node.

Connectivity matrix conventions

Throughout PyPhi, if \(CM\) is a connectivity matrix, then \([CM]_{i,j} = 1\) means that there is a directed edge \((i,j)\) from node \(i\) to node \(j\), and \([CM]_{i,j} = 0\) means there is no edge from \(i\) to \(j\).

For example, this network of four nodes


has the following connectivity matrix:

>>> cm = [[0, 0, 1, 0],
...       [1, 0, 1, 0],
...       [0, 1, 0, 1],
...       [0, 0, 0, 1]]