diff --git a/pyphi/actual.py b/pyphi/actual.py index 4541128b6..b9e4e1d47 100644 --- a/pyphi/actual.py +++ b/pyphi/actual.py @@ -410,14 +410,20 @@ def find_causal_link(self, direction, mechanism, purviews=False, if not purviews: max_ria = _null_ac_ria(self.mechanism_state(direction), direction, mechanism, None) - else: - # This max should be most positive - max_ria = max(self.find_mip(direction, mechanism, purview, - allow_neg) - for purview in purviews) - - # Construct the corresponding CausalLink - return CausalLink(max_ria) + return CausalLink(max_ria) + + # Finds rias with maximum alpha + all_ria = [self.find_mip(direction, mechanism, purview, allow_neg=allow_neg) + for purview in purviews] + max_ria = max(all_ria) + purviews = [ria.purview for ria in all_ria if ria.alpha == max_ria.alpha] + # Selected rias whose purview is not a superset of any other + def is_not_superset(purview): + return all((not set(purview).issuperset(set(other_purview))) or + (set(purview) == set(other_purview)) for other_purview in purviews) + + extended_purview = filter(is_not_superset, purviews) + return CausalLink(max_ria, tuple(extended_purview)) def find_actual_cause(self, mechanism, purviews=False): """Return the actual cause of a mechanism.""" diff --git a/pyphi/models/actual_causation.py b/pyphi/models/actual_causation.py index 08416baa3..501a3ff98 100644 --- a/pyphi/models/actual_causation.py +++ b/pyphi/models/actual_causation.py @@ -131,8 +131,9 @@ class CausalLink(cmp.Orderable): up to |PRECISION|, the size of the mechanism is compared. """ - def __init__(self, ria): + def __init__(self, ria, extended_purview=None): self._ria = ria + self._extended_purview = tuple(extended_purview) if extended_purview is not None else None @property def alpha(self): @@ -163,6 +164,17 @@ def purview(self): """ return self._ria.purview + @property + def extended_purview(self): + """tuple[tuple[int]]: List of purviews over which this causal link is + maximally irreducible. + + Note: It will contain multiple purviews iff causal link has + undetermined actual causes/effects (e.g. two irreducible causes with same alpha + over different purviews). + """ + return self._extended_purview + @property def ria(self): """AcRepertoireIrreducibilityAnalysis: The irreducibility analysis for @@ -175,10 +187,10 @@ def node_labels(self): return self._ria.node_labels def __repr__(self): - return fmt.make_repr(self, ['ria']) + return fmt.make_repr(self, ['ria', 'extended_purview']) def __str__(self): - return "CausalLink\n" + fmt.indent(fmt.fmt_ac_ria(self.ria)) + return "CausalLink\n" + fmt.indent(fmt.fmt_causal_link(self)) unorderable_unless_eq = \ AcRepertoireIrreducibilityAnalysis.unorderable_unless_eq diff --git a/pyphi/models/fmt.py b/pyphi/models/fmt.py index 931f14e80..8c47652a4 100644 --- a/pyphi/models/fmt.py +++ b/pyphi/models/fmt.py @@ -425,16 +425,32 @@ def fmt_repertoire(r): return box('\n'.join(lines)) +def fmt_extended_purview(extended_purview, node_labels=None): + """Format an extended purview.""" + if len(extended_purview) == 1: + return fmt_mechanism(extended_purview[0], node_labels=node_labels) -def fmt_ac_ria(ria): + purviews = [fmt_mechanism(purview, node_labels=node_labels) for purview in extended_purview] + return '[' + ', '.join(purviews) + ']' + + +def fmt_causal_link(causal_link): + """Format a CausalLink.""" + return fmt_ac_ria(causal_link, extended_purview=causal_link.extended_purview) + +def fmt_ac_ria(ria, extended_purview=None): """Format an AcRepertoireIrreducibilityAnalysis.""" causality = { - Direction.CAUSE: (fmt_mechanism(ria.purview, ria.node_labels), + Direction.CAUSE: (fmt_mechanism(ria.purview, ria.node_labels) + if extended_purview is None + else fmt_extended_purview(ria.extended_purview, ria.node_labels), ARROW_LEFT, fmt_mechanism(ria.mechanism, ria.node_labels)), Direction.EFFECT: (fmt_mechanism(ria.mechanism, ria.node_labels), ARROW_RIGHT, - fmt_mechanism(ria.purview, ria.node_labels)) + fmt_mechanism(ria.purview, ria.node_labels) + if extended_purview is None + else fmt_extended_purview(ria.extended_purview, ria.node_labels)) }[ria.direction] causality = ' '.join(causality) @@ -443,7 +459,6 @@ def fmt_ac_ria(ria): alpha=round(ria.alpha, 4), causality=causality) - def fmt_account(account, title=None): """Format an Account or a DirectedAccount.""" if title is None: @@ -454,9 +469,9 @@ def fmt_account(account, title=None): body = '' body += 'Irreducible effects\n' - body += '\n'.join(fmt_ac_ria(m) for m in account.irreducible_effects) + body += '\n'.join(fmt_causal_link(m) for m in account.irreducible_effects) body += '\nIrreducible causes\n' - body += '\n'.join(fmt_ac_ria(m) for m in account.irreducible_causes) + body += '\n'.join(fmt_causal_link(m) for m in account.irreducible_causes) return '\n' + header(title, body, under_char='*') diff --git a/pyphi/utils.py b/pyphi/utils.py index c2a9948b7..c6bffbabc 100644 --- a/pyphi/utils.py +++ b/pyphi/utils.py @@ -198,7 +198,7 @@ def load_data(directory, num): def get_path(i): # pylint: disable=missing-docstring return os.path.join(root, 'data', directory, str(i) + '.npy') - return [np.load(get_path(i)) for i in range(num)] + return [np.load(get_path(i), allow_pickle=True) for i in range(num)] # Using ``decorator`` preserves the function signature of the wrapped function,