
    ph                     h    S r SSKJr  SSKrSSKJrJr  SSKJ	r	  S r
S rS	 rS
 rS rS rS rS rg)z=
Functions for manipulating transition probability matrices.
    )chainN   OFFON)
all_statesc           	          [        [        R                  " [        R                  " U R                  SS 5      S:H  5      S   5      $ )z'Return the indices of nodes in the TPM.N   r   )tuplenpwherearrayshapetpms    C/home/james-whalen/.local/lib/python3.13/site-packages/pyphi/tpm.pytpm_indicesr      s3    "((399Sb>2a78;<<    c                 l    U R                   S:H  =(       a    U R                  S   U R                  S   :H  $ )zKReturn ``True`` if ``tpm`` is in state-by-state form, otherwise
``False``.
r   r   r   )ndimr   r   s    r   is_state_by_stater      s,     88q=9SYYq\SYYq\99r   c                     [        S5      //[        U5      -  nU H  nX$   [        R                  /X4'   M     [	        [
        R                  " U5      5      nU [        U5         $ )ap  Return a TPM conditioned on the given fixed node indices, whose states
are fixed according to the given state-tuple.

The dimensions of the new TPM that correspond to the fixed nodes are
collapsed onto their state, making those dimensions singletons suitable for
broadcasting. The number of dimensions of the conditioned TPM will be the
same as the unconditioned TPM.
N)slicelenr   newaxislistr   from_iterabler   )r   fixed_nodesstateconditioning_indicesis        r   condition_tpmr#      se     #4[M?SZ7#(8RZZ"8    3 34H IJ u)*++r   c                 z    [         R                  " S/U R                  S-
  -  U R                  S   /-   5      nX-  $ )z_Broadcast a state-by-node TPM so that singleton dimensions are expanded
over the full network.
r   r   r
   )r   onesr   r   )r   unconstraineds     r   
expand_tpmr'   1   s9     GGQC388a<0CIIbM?BCMr   c                     UR                  [        U 5      SS9[        R                  " UR                  5      [        U 5         R                  5       -  $ )a  Marginalize out nodes from a TPM.

Args:
    node_indices (list[int]): The indices of nodes to be marginalized out.
    tpm (np.ndarray): The TPM to marginalize the node out of.

Returns:
    np.ndarray: A TPM with the same number of dimensions, with the nodes
    marginalized out.
T)keepdims)sumr   r   r   r   r   prod)node_indicesr   s     r   marginalize_outr-   9   sF     775&76
D./4468 8r   c                 R   ^ ^^^^ U4S jmUUU 4S jm[        U4S jU 5       5      $ )a/  Infer the presence or absence of an edge from node A to node B.

Let |S| be the set of all nodes in a network. Let |A' = S - {A}|. We call
the state of |A'| the context |C| of |A|. There is an edge from |A| to |B|
if there exists any context |C(A)| such that |Pr(B | C(A), A=0) != Pr(B |
C(A), A=1)|.

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.
c                 R   > U ST [         -   U TS -   nU ST [        -   U TS -   nX4$ )z\Given a context C(A), return the states of the full system with A
OFF and ON, respectively.
Nr   )contexta_offa_onas      r   a_in_context infer_edge.<locals>.a_in_contextX   sC     c!GABK/r{R'!"+-}r   c                 :   > T" U 5      u  pTU   T   TU   T   :g  $ )z9Return ``True`` if A has an effect on B, given a context. )r0   r1   r2   r4   br   s      r   a_affects_b_in_context*infer_edge.<locals>.a_affects_b_in_context`   s*    "7+5z!}D	!,,r   c              3   4   >#    U  H  nT" U5      v   M     g 7f)Nr7   ).0r0   r9   s     r   	<genexpr>infer_edge.<locals>.<genexpr>e   s     Gh7%g..hs   )any)r   r3   r8   contextsr9   r4   s   ``` @@r   
infer_edgerA   H   s      -
 GhGGGr   c                     U R                   S   n[        [        US-
  5      5      n[        R                  " X4[
        S9n[        R                  " UR                   5       H  u  pE[        XXR5      X4   U'   M     U$ )z\Infer the connectivity matrix associated with a state-by-node TPM in
multidimensional form.
r
   r   )dtype)r   r   r   r   emptyintndindexrA   )r   network_sizeall_contextscmr3   r8   s         r   infer_cmrJ   h   sh     99R=LL1$456L	<.c	:B

288$ca6a %Ir   c                 l   U R                    Vs/ s H  oR                  S   PM     nnU Vs/ s H  nUR                  U R                  S9PM     nnU Vs/ s H  n[        R
                  " US5      PM     nn[        [        [        U5      5      n[        R                  " USS9$ s  snf s  snf s  snf )zCReconstitute the TPM of a subsystem using the individual node TPMs.).r   )axisr
   )
nodesr   squeezeexternal_indicesr   expand_dimsr   mapr'   concatenate)	subsystemnode	node_tpmsr   s       r   reconstitute_tpmrV   t   s    
 /8oo>od&!oI> C 	334   5>>ISR(II> SY/0I >>)"-- ? ?s   B'#B,!B1)__doc__	itertoolsr   numpyr   	constantsr   r   utilsr   r   r   r#   r'   r-   rA   rJ   rV   r7   r   r   <module>r\      sB   
    =
:,(8H@	.r   