Skip to content

Commit

Permalink
Merge branch 'release/0.3.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
wmayner committed Feb 5, 2015
2 parents 12cb2a2 + ac14d80 commit e9291d4
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 22 deletions.
2 changes: 1 addition & 1 deletion pyphi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"""

__title__ = 'pyphi'
__version__ = '0.3.2'
__version__ = '0.3.3'
__description__ = 'Python library for computing integrated information.',
__author__ = 'Will Mayner'
__author_email__ = 'wmayner@gmail.com'
Expand Down
3 changes: 2 additions & 1 deletion pyphi/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,8 @@ def _big_mip(cache_key, subsystem):
submatrix_indices = np.ix_(subsystem.node_indices, subsystem.node_indices)
cm = subsystem.network.connectivity_matrix[submatrix_indices]
# Get the number of strongly connected components.
num_components, _ = connected_components(csr_matrix(cm))
num_components, _ = connected_components(csr_matrix(cm),
connection='strong')
if num_components > 1:
return _null_mip(subsystem)
# =========================================================================
Expand Down
3 changes: 2 additions & 1 deletion pyphi/convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import numpy as np
from . import validate
import logging
from .constants import EPSILON


# Create a logger for this module.
Expand Down Expand Up @@ -184,7 +185,7 @@ def state_by_state2state_by_node(tpm):
# i.e., a row of the state-by-node TPM.
# Assign that row to the ith state in the state-by-node TPM.
sbn_tpm[state] = [np.sum(on_probabilities[n][i]) for n in range(N)]
if not np.all(tpm == state_by_node2state_by_state(sbn_tpm)):
if not np.all((tpm - state_by_node2state_by_state(sbn_tpm)) < EPSILON):
logging.warning(
'The TPM is not conditionally independent. See the conditional '
'independence example in the documentation for more information '
Expand Down
13 changes: 7 additions & 6 deletions pyphi/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
from collections import namedtuple, Iterable
import numpy as np

from .constants import DIRECTIONS, PAST, FUTURE
from . import utils, constants, config, convert, json
from . import utils, constants, convert, json

# TODO use properties to avoid data duplication

Expand Down Expand Up @@ -415,17 +414,19 @@ def emd_eq(self, other):
return self.mechanism == other.mechanism and self.eq_repertoires(other)

# TODO Rename to expanded_cause_repertoire, etc
def expand_cause_repertoire(self):
def expand_cause_repertoire(self, new_purview=None):
"""Expands a cause repertoire to be a distribution over an entire
network."""
return self.subsystem.expand_cause_repertoire(self.cause.purview,
self.cause.repertoire)
self.cause.repertoire,
new_purview)

def expand_effect_repertoire(self):
def expand_effect_repertoire(self, new_purview=None):
"""Expands an effect repertoire to be a distribution over an entire
network."""
return self.subsystem.expand_effect_repertoire(self.effect.purview,
self.effect.repertoire)
self.effect.repertoire,
new_purview)

def expand_partitioned_cause_repertoire(self):
"""Expands a partitioned cause repertoire to be a distribution over an
Expand Down
45 changes: 32 additions & 13 deletions pyphi/subsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ class Subsystem:
size (int): The number of nodes in the subsystem.
network (Network): The network the subsystem belongs to.
cut (Cut): The cut that has been applied to this subsystem.
connectivity_matrix (np.array): The connectivity matrix after applying the cut
perturb_vector (np.array): The vector of perturbation probabilities for each node
connectivity_matrix (np.array): The connectivity matrix after applying
the cut.
perturb_vector (np.array): The vector of perturbation probabilities for
each node.
null_cut (Cut): The cut object representing no cut.
past_tpm (np.array): The TPM conditioned on the past state of the
external nodes (nodes outside the subsystem).
Expand Down Expand Up @@ -284,7 +286,8 @@ def effect_repertoire(self, mechanism, purview):
# Marginalize-out non-mechanism purview inputs.
non_mechanism_inputs = inputs - set(mechanism)
for node in non_mechanism_inputs:
tpm = utils.marginalize_out(node.index, tpm, self.perturb_vector[node.index])
tpm = utils.marginalize_out(node.index, tpm,
self.perturb_vector[node.index])
# Incorporate this node's CPT into the future_nodes' conditional
# joint distribution by taking the product (with singleton
# broadcasting).
Expand Down Expand Up @@ -353,28 +356,44 @@ def unconstrained_effect_repertoire(self, purview):
"""
return self._unconstrained_repertoire(DIRECTIONS[FUTURE], purview)

def expand_repertoire(self, direction, purview, repertoire):
def expand_repertoire(self, direction, purview, repertoire,
new_purview=None):
"""Return the unconstrained cause or effect repertoire based on a
direction."""
validate.direction(direction)
# If not specified, the new purview is the entire network
if (new_purview is None):
new_purview = self.nodes
elif not (set([node.index for node in purview])
.issubset([node.index for node in new_purview])):
raise ValueError("Expanded purview must contain original purview.")
# Get the unconstrained repertoire over the other nodes in the network.
non_purview_nodes = tuple(frozenset(self.nodes) - frozenset(purview))
uc = self._unconstrained_repertoire(direction, non_purview_nodes)
non_purview_nodes = tuple(
frozenset([node.index for node in new_purview]) -
frozenset([node.index for node in purview])
)
uc = self._unconstrained_repertoire(
direction, self.indices2nodes(non_purview_nodes))
# Multiply the given repertoire by the unconstrained one to get a
# distribution over all the nodes in the network.
return repertoire * uc
expanded_repertoire = repertoire * uc
# Renormalize
if (np.sum(expanded_repertoire > 0)):
return expanded_repertoire / np.sum(expanded_repertoire)
else:
return expanded_repertoire

# TODO test expand cause repertoire
def expand_cause_repertoire(self, purview, repertoire):
def expand_cause_repertoire(self, purview, repertoire, new_purview=None):
"""Expand a partial cause repertoire over a purview to a distribution
over the entire subsystem's state space."""
return self.expand_repertoire(DIRECTIONS[PAST], purview, repertoire)
return self.expand_repertoire(DIRECTIONS[PAST], purview, repertoire,
new_purview)

# TODO test expand effect repertoire
def expand_effect_repertoire(self, purview, repertoire):
def expand_effect_repertoire(self, purview, repertoire, new_purview=None):
"""Expand a partial effect repertoire over a purview to a distribution
over the entire subsystem's state space."""
return self.expand_repertoire(DIRECTIONS[FUTURE], purview, repertoire)
return self.expand_repertoire(DIRECTIONS[FUTURE], purview, repertoire,
new_purview)

def cause_info(self, mechanism, purview):
"""Return the cause information for a mechanism over a purview."""
Expand Down
23 changes: 23 additions & 0 deletions test/test_big_phi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from pyphi import constants, config, compute, models, utils, convert, Network
from pyphi.constants import DIRECTIONS, PAST, FUTURE

from scipy.sparse.csgraph import connected_components
from scipy.sparse import csr_matrix

# Precision for testing.
PRECISION = 5
Expand Down Expand Up @@ -432,3 +434,24 @@ def test_big_mip_macro(macro_s, flushcache, restore_fs_cache):
flushcache()
mip = compute.big_mip(macro_s)
check_mip(mip, macro_answer)

def test_strongly_connected():
# A disconnected matrix
cm1 = np.array([[0, 0, 1],
[0, 1, 0],
[1, 0, 0]])
# A strongly connected matrix
cm2 = np.array([[0, 1, 0],
[0, 0, 1],
[1, 0, 0]])
# A weakly connected matrix
cm3 = np.array([[0, 1, 0],
[0, 0, 1],
[0, 1, 0]])
assert connected_components(csr_matrix(cm1), connection='strong')[0] > 1
assert connected_components(csr_matrix(cm2), connection='strong')[0] == 1
assert connected_components(csr_matrix(cm3), connection='strong')[0] > 1




50 changes: 50 additions & 0 deletions test/test_subsystem_expand.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from pyphi.compute import big_mip
from pyphi import Subsystem
import numpy as np
import example_networks
from pyphi.constants import EPSILON

micro = example_networks.micro()
micro.current_state = (0, 0, 0, 0)
micro.past_state = (0, 0, 0, 0)
micro_subsystem = Subsystem(range(micro.size), micro)
mip = big_mip(micro_subsystem)

CD = micro_subsystem.nodes[2:4]
BCD = micro_subsystem.nodes[1:4]
ABCD = micro_subsystem.nodes[0:4]

A = mip.unpartitioned_constellation[0]

cause = A.cause.mip.unpartitioned_repertoire
effect = A.effect.mip.unpartitioned_repertoire


def test_expand_cause_repertoire():
assert np.all(abs(A.expand_cause_repertoire(CD) - cause) < EPSILON)
assert np.all(abs(
A.expand_cause_repertoire(BCD).flatten(order='F') -
np.array([1/6 if i < 6 else 0 for i in range(8)])) < EPSILON)
assert np.all(abs(
A.expand_cause_repertoire(ABCD).flatten(order='F') -
np.array([1/12 if i < 12 else 0 for i in range(16)])) < EPSILON)
assert np.all(abs(A.expand_cause_repertoire(ABCD) -
A.expand_cause_repertoire()) < EPSILON)


def test_expand_effect_repertoire():
assert np.all(abs(A.expand_effect_repertoire(CD) - effect) < EPSILON)
assert np.all(abs(A.expand_effect_repertoire(BCD).flatten(order='F') -
np.array([.25725, .23275, .11025, .09975,
.11025, .09975, .04725, .04275])) < EPSILON)
assert np.all(abs(
A.expand_effect_repertoire(ABCD).flatten(order='F') -
np.array([.13505625, .12219375, .12219375, .11055625,
.05788125, .05236875, .05236875, .04738125,
.05788125, .05236875, .05236875, .04738125,
.02480625, .02244375, .02244375, .02030625])) < EPSILON)
assert np.all(abs(A.expand_effect_repertoire(ABCD) -
A.expand_effect_repertoire()) < EPSILON)

0 comments on commit e9291d4

Please sign in to comment.