# Path: pyphi/constants.py
# OFF = (0,)
# 
# ON = (1,)
# 
# Path: pyphi/utils.py
# def all_states(n, big_endian=False):
#     """Return all binary states for a system.
# 
#     Args:
#         n (int): The number of elements in the system.
#         big_endian (bool): Whether to return the states in big-endian order
#             instead of little-endian order.
# 
#     Yields:
#         tuple[int]: The next state of an ``n``-element system, in little-endian
#         order unless ``big_endian`` is ``True``.
#     """
#     if n == 0:
#         return
# 
#     for state in product((0, 1), repeat=n):
#         if big_endian:
#             yield state
#         else:
#             yield state[::-1]  # Convert to little-endian ordering


from itertools import chain
from .constants import OFF, ON
from .utils import all_states
import numpy as np

    Args:
        tpm (np.ndarray): The TPM in state-by-node, multidimensional form.
        a (int): The index of the putative source node.
        b (int): The index of the putative sink node.
    Returns:
        bool: ``True`` if the edge |A -> B| exists, ``False`` otherwise.
    """

    def a_in_context(context):
        """Given a context C(A), return the states of the full system with A
        OFF and ON, respectively.
        """
        a_off = context[:a] + OFF + context[a:]
        a_on = context[:a] + ON + context[a:]
        return (a_off, a_on)

    def a_affects_b_in_context(context):
        """Return ``True`` if A has an effect on B, given a context."""
        a_off, a_on = a_in_context(context)
        return tpm[a_off][b] != tpm[a_on][b]

    return any(a_affects_b_in_context(context) for context in contexts)


def infer_cm(tpm):
    """Infer the connectivity matrix associated with a state-by-node TPM in
    multidimensional form.
    """
    network_size = tpm.shape[-1]

# Path: pyphi/constants.py
# OFF = (0,)
# 
# ON = (1,)
# 
# Path: pyphi/utils.py
# def all_states(n, big_endian=False):
#     """Return all binary states for a system.
# 
#     Args:
#         n (int): The number of elements in the system.
#         big_endian (bool): Whether to return the states in big-endian order
#             instead of little-endian order.
# 
#     Yields:
#         tuple[int]: The next state of an ``n``-element system, in little-endian
#         order unless ``big_endian`` is ``True``.
#     """
#     if n == 0:
#         return
# 
#     for state in product((0, 1), repeat=n):
#         if big_endian:
#             yield state
#         else:
#             yield state[::-1]  # Convert to little-endian ordering


# Path: pyphi/tpm.py
from itertools import chain
from .constants import OFF, ON
from .utils import all_states
import numpy as np


    Args:
        tpm (np.ndarray): The TPM in state-by-node, multidimensional form.
        a (int): The index of the putative source node.
        b (int): The index of the putative sink node.
    Returns:
        bool: ``True`` if the edge |A -> B| exists, ``False`` otherwise.
    """

    def a_in_context(context):
        """Given a context C(A), return the states of the full system with A
        OFF and ON, respectively.
        """
        a_off = context[:a] + OFF + context[a:]
        a_on = context[:a] + ON + context[a:]
        return (a_off, a_on)

    def a_affects_b_in_context(context):
        """Return ``True`` if A has an effect on B, given a context."""
        a_off, a_on = a_in_context(context)
        return tpm[a_off][b] != tpm[a_on][b]

    return any(a_affects_b_in_context(context) for context in contexts)


def infer_cm(tpm):
    """Infer the connectivity matrix associated with a state-by-node TPM in
    multidimensional form.
    """
    network_size = tpm.shape[-1]

    all_contexts = tuple(all_states(network_size - 1))