
    ph              	          S r SSKrSSKrSSKJr  SSKrSSKJr  SSK	J
r
JrJrJrJrJrJr  SSKJrJr  SSKJr  SS	KJr  SS
KJrJr  SSKJr  SSKJrJrJ r   \RB                  " \"5      r#Sr$\RJ                  " S\$5      r&S r'S r(S r)S r* " S S\" S/ SQ5      5      r+ " S S\5      r, " S S\" SSS/5      5      r- " S S\" SSS/5      5      r.S r/S  r0S! r1S" r2S# r3S$ r4 " S% S&5      r5S' r6  S,S( jr7  S-S) jr8S* r9S+ r:g).zN
Methods for coarse-graining systems to different levels of spatial analysis.
    N)
namedtuple)entropy   )computeconfig	constantsconvertdistributionutilsvalidate)ConditionallyDependentErrorStateUnreachableError)
NodeLabels)irreducible_purviews)expand_node_tpmgenerate_nodes)	Subsystem)is_state_by_statemarginalize_outtpm_indices
   partition_listsc                 <    [        [        [        U 5      5      5      $ )z8Generate a new set of node indices, the size of indices.)tuplerangelen)indicess    E/home/james-whalen/.local/lib/python3.13/site-packages/pyphi/macro.pyreindexr   !   s    s7|$%%    c                 j    [         R                  " U  Vs/ s H  n[        U5      PM     snSS9$ s  snf )z;Reconstruct the network TPM from a collection of node TPMs.)axis)npstackr   )	node_tpmstpms     r   rebuild_system_tpmr(   &   s*    88Y?Yc_S)Y?bII?s   0c                 b    U R                   S::  a  U $ U R                  5       S[        U 5      4   $ )a/  Remove singleton dimensions from the TPM.

Singleton dimensions are created by conditioning on a set of elements.
This removes those elements from the TPM, leaving a TPM that only
describes the non-conditioned elements.

Note that indices used in the original TPM must be reindexed for the
smaller TPM.
   .)ndimsqueezer   r'   s    r   remove_singleton_dimensionsr.   +   s/     xx1}
;;=k#..//r    c                 *   / nU R                    Ht  nUR                  nUR                   HD  nUR                  UR                  U5      (       a  M&  XbR
                  ;   d  M7  [        U/U5      nMF     UR                  U5        Mv     [        U5      n[        R                  " U5      n[        R                  " U R                  5      n[        R                  " U[        R                  R                  XqS-
  5      5      n[        R                   " U5      $ )zeIterate the TPM for the given number of timesteps.

Returns:
    np.ndarray: tpm * (noise_tpm^(t-1))
r   )nodestpm_oninputsin_same_boxindexoutput_indicesr   appendr(   r	   state_by_node2state_by_stater'   r$   dotlinalgmatrix_powerstate_by_state2state_by_node)	systemstepsblackboxr&   nodenode_tpm
input_node
noised_tpmr'   s	            r   run_tpmrC   =   s     I;;++J''

J??!8!88.
|XFH &
 	"  $I.J55jAJ

.
.vzz
:C &&bii,,ZC
DC//44r    c                   N    \ rS rSrSr\S 5       r\S 5       r\S 5       r	S r
Srg)	SystemAttrs[   zAn immutable container that holds all the attributes of a subsystem.

Versions of this object are passed down the steps of the micro-to-macro
pipeline.
c                     [        U R                  5      S   S:X  d   e[        S U R                   5       5      n[        XR                  5      $ )z"Return the labels for macro nodes.r   c              3   D   #    U  H  nS R                  U5      v   M     g7f)zm{}N)format).0is     r   	<genexpr>*SystemAttrs.node_labels.<locals>.<genexpr>f   s     A/@!ell1oo/@s    )listnode_indicesr   )selflabelss     r   node_labelsSystemAttrs.node_labelsb   sI     D%%&q)Q...At/@/@AA&"3"344r    c                     [        U R                  U R                  U R                  U R                  U R
                  5      $ N)r   r'   cmstaterO   rR   rP   s    r   r0   SystemAttrs.nodesi   s2    dhhT=N=N"..0 	0r    c                 n    [        U R                  U R                  U R                  U R                  5      $ rU   )rE   r'   rV   rO   rW   )r<   s    r   packSystemAttrs.packn   s*    6::vyy&2E2E!<<) 	)r    c                     U R                   Ul         U R                  Ul        U R                  Ul        U R                  Ul        U R                  Ul        U R
                  Ul        g rU   )r'   rV   rO   rR   r0   rW   )rP   r<   s     r   applySystemAttrs.applys   sJ    XX
GG	"//!--zzzzr     N)__name__
__module____qualname____firstlineno____doc__propertyrR   r0   staticmethodr[   r^   __static_attributes__r`   r    r   rE   rE   [   sH    
 5 5 0 0 ) )"r    rE   )r'   rV   rO   rW   c                      ^  \ rS rSrSr  SU 4S jjr\S 5       r\S 5       r\S 5       r	S r
\S 5       r\S	 5       r\S
 5       r\S 5       rS rSS jrS rS rS rS rU 4S jrU 4S jrSrU =r$ )MacroSubsystem|   a  A subclass of |Subsystem| implementing macro computations.

This subsystem performs blackboxing and coarse-graining of elements.

Unlike |Subsystem|, whose TPM has dimensionality equal to that of the
subsystem's network and represents nodes external to the system using
singleton dimensions, |MacroSubsystem| squeezes the TPM to remove these
singletons. As a result, the node indices of the system are also squeezed
to ``0..n`` so they properly index the TPM, and the state-tuple is
reduced to the size of the system.

After each macro update (temporal blackboxing, spatial blackboxing, and
spatial coarse-graining) the TPM, CM, nodes, and state are updated so that
they correctly represent the updated system.
c	                   > UR                   R                  U5      n	X l        Xl        X`l        Xpl        Xl        [        TU ]!  XXU5        [        R                  " Xx5        [        R                  U 5      n
U R                  U
5      n
Ub7  [        R
                  " U5        UR                  5       nU R                  Xz5      n
US:w  a-  Uc   e[        R                  " U5        U R!                  XgU
5      n
Ub  U R#                  Xz5      n
UbB  [        R                  " U5        UR                  5       nU R%                  XR&                  U
5      n
U
R)                  U 5        [        R*                  " U 5        g )Nr   )rR   coerce_to_indicesnetwork_statemicro_node_indices
time_scaler>   coarse_grainsuper__init__r   blackbox_and_coarse_grainrE   r[   _squeezer   _blackbox_partial_noise_blackbox_time_blackbox_space_coarsegrain_spaceis_cutr^   	subsystem)rP   networkrW   r0   cut
mice_cacherp   r>   rq   ro   r<   	__class__s              r   rs   MacroSubsystem.__init__   sO    %00BB5I #"4$ ();*M**8B!!$' v& h''')H11(CF ?'''
+((vFF ))(;F #!!,/'//1L,,\;;OFT4 r    c                    U R                   [        U R                  5      :X  d   e[        U R                  5      n[        U R                  5      nU R                  [
        R                  " X5         n[        R                  " XR                  5      n[        U5      n[        X#XE5      n[        S U 5       5      n[        X#XT5      $ )zSqueeze out all singleton dimensions in the Subsystem.

Reindexes the subsystem so that the nodes are ``0..n`` where ``n`` is
the number of internal indices in the system.
c              3   8   #    U  H  oR                   v   M     g 7frU   )r1   )rJ   r?   s     r   rL   *MacroSubsystem._squeeze.<locals>.<genexpr>   s      ?   )rO   r   r'   r.   rV   r$   ix_r   state_ofrW   r   r   r(   rE   )r<   internal_indicesr'   rV   rW   rO   r0   s          r   ru   MacroSubsystem._squeeze   s     ""k&**&====&vzz2)&**5
 YYrvv.AB/> /0s< ! ? ??3L88r    c                    / nUR                    Hb  nUR                  nUR                   H2  nU R                  XSR                  5      (       d  M%  [        U/U5      nM4     UR                  U5        Md     [        U5      nUR                  US9$ )z6Noise connections from hidden elements to other boxes.r-   )	r0   r1   r2   hidden_fromr4   r   r6   r(   _replace)r>   r<   r&   r?   r@   rA   r'   s          r   rv   &MacroSubsystem._blackbox_partial_noise   s~     	LLD{{H"kk
''
JJ??.
|XFH * X& ! !+3''r    c                     UR                  5       n[        X U5      n[        UR                  5      n[        R
                  " XD45      n[        X5UR                  UR                  5      $ )z3Black box the CM and TPM over the given time_scale.)r   rC   r   rO   r$   onesrE   rW   )rp   r>   r<   r'   nrV   s         r   rw   MacroSubsystem._blackbox_time   sX     ##%f(3 ##$WWaV_3F$7$7FFr    c                 h   [        UR                  UR                  5      nUR                  [	        U5      :X  d   e[        U5      n[        U5      n[        R                  " XD45      n[        R                  " [        U5      SS9 Hv  u  pgU R                  R                  U5      nU R                  R                  U   n	U R                  [        R                   " X5         R#                  5       S:  d  Mp  SXVU4'   Mx     UR%                  UR&                  5      n
UR(                  n[+        X5X5      $ )aE  Blackbox the TPM and CM in space.

Conditions the TPM on the current value of the hidden nodes. The CM is
set to universal connectivity.

.. TODO: change this ^

This shrinks the size of the TPM by the number of hidden indices; now
there is only `len(output_indices)` dimensions in the TPM and in the
state of the subsystem.
r*   repeatr   r   )r   hidden_indicesr'   r5   r   r.   r   r$   zeros	itertoolsproductr   r>   
outputs_of	partitionrV   r   summacro_staterW   macro_indicesrE   )rP   r>   r<   r'   r   rV   rK   joutputstorW   rO   s               r   rx   MacroSubsystem._blackbox_space  s     h55vzzB&&+c*::::)#.MXXqf%%eAhq9DAmm..q1G((+Bwwrvvg*+//1A5a4 : $$V\\2--3L88r    c                     U R                  UR                  U(       + S9nU R                  nU R                  UR                  5      n[        U5      n[        R                  " Xf45      n[        X7XE5      $ )z&Spatially coarse-grain the TPM and CM.check_independence)		macro_tpmr'   r   r   rW   r   r$   r   rE   )rq   rz   r<   r'   rO   rW   r   rV   s           r   ry   !MacroSubsystem._coarsegrain_space   sm     $$JJJ % 9 $11((6 WWaV_3L88r    c                     U R                   $ )zThe indices of this system to be cut for |big_phi| computations.

For macro computations the cut is applied to the underlying
micro-system.
)ro   rX   s    r   cut_indicesMacroSubsystem.cut_indices/  s     &&&r    c              #      #    [         R                  " U R                  SS9 H:  nU R                  U5      nU R                  R                  U5      (       d  M6  Uv   M<     g7f)zThe mechanisms of this system that are currently cut.

Note that although ``cut_indices`` returns micro indices, this
returns macro mechanisms.

Yields:
    tuple[int]
TnonemptyN)r   powersetrO   macro2micror}   splits_mechanism)rP   	mechanismmicro_mechanisms      r   cut_mechanismsMacroSubsystem.cut_mechanisms8  sM      (9(9DII"..y9Oxx((99 Js   AA!	A!c                 .    U R                   R                  $ )zSLabels for the nodes that can be cut.

These are the labels of the micro elements.
)r|   rR   rX   s    r   cut_node_labelsMacroSubsystem.cut_node_labelsG  s     ||'''r    c           
          [        U R                  U R                  U R                  UU R                  U R
                  U R                  S9$ )zReturn a cut version of this |MacroSubsystem|.

Args:
    cut (Cut): The cut to apply to this |MacroSubsystem|.

Returns:
    MacroSubsystem: The cut version of this |MacroSubsystem|.
)r}   rp   r>   rq   )rj   r|   rn   ro   rp   r>   rq   )rP   r}   s     r   	apply_cutMacroSubsystem.apply_cutO  sD     LL##]]**, 	,r    c                 p    [         R                  " U R                  5      n[        U R                  XU5      $ )z>Override Subsystem implementation using Network-level indices.)r   r   rO   r   rV   )rP   	directionr   purviewsall_purviewss        r   potential_purviews!MacroSubsystem.potential_purviewsb  s.    ~~d&7&78#GGY<9 	9r    c                    S nU R                   (       aT  U R                  (       aC  U" U R                  R                  U5      nU" U R                   R                  [        U5      5      $ U R                   (       a  U" U R                   R                  U5      $ U R                  (       a  U" U R                  R                  U5      $ U$ )zTReturn all micro indices which compose the elements specified by
``macro_indices``.
c                 ~   ^  [         R                  R                  U 4S jU 5       5      n[        [	        U5      5      $ )Nc              3   .   >#    U  H
  nTU   v   M     g 7frU   r`   )rJ   rK   r   s     r   rL   EMacroSubsystem.macro2micro.<locals>.from_partition.<locals>.<genexpr>m  s      :5&3	!m   )r   chainfrom_iterabler   sorted)r   r   micro_indicess   `  r   from_partition2MacroSubsystem.macro2micro.<locals>.from_partitionl  s4    %OO99 :5&3:5 5M.//r    )r>   rq   r   r   )rP   r   r   cg_micro_indicess       r   r   MacroSubsystem.macro2microh  s    	0
 ==T..-d.?.?.I.I.; =!$--"9"9")*:";= =]]!$--"9"9=II!$"3"3"="=}MMr    c           	          U R                   (       d  [        S5      e[        [        [	        U R                  U5      5      R                  U R                   R                  5      5      5      $ )zaGiven a set of macro elements, return the blackbox output elements
which compose these elements.
zSystem is not blackboxed)r>   
ValueErrorr   r   setr   intersectionr5   )rP   r   s     r   macro2blackbox_outputs%MacroSubsystem.macro2blackbox_outputs|  sT     }}788VC]+

,t}}33
46 7 	7r    c                 8    S[        U R                  5      -   S-   $ )NzMacroSubsystem())reprr0   rX   s    r   __repr__MacroSubsystem.__repr__  s     4

#33c99r    c                     [        U 5      $ rU   )r   rX   s    r   __str__MacroSubsystem.__str__  s    Dzr    c                   > [        U 5      [        U5      :w  a  g[        TU ]	  U5      =(       aY    U R                  UR                  :H  =(       a9    U R                  UR                  :H  =(       a    U R
                  UR
                  :H  $ )zhTwo macro systems are equal if each underlying |Subsystem| is equal
and all macro attributes are equal.
F)typerr   __eq__rp   r>   rq   )rP   otherr   s     r   r   MacroSubsystem.__eq__  sp     :e$u% 85#3#338/8 !!U%7%77	9r    c                 v   > [        [        TU ]	  5       U R                  U R                  U R
                  45      $ rU   )hashrr   __hash__rp   r>   rq   )rP   r   s    r   r   MacroSubsystem.__hash__  s7    W__]] ! 	!r    )r>   rq   ro   rn   rp   )NNNr   NN)F)ra   rb   rc   rd   re   rs   rg   ru   rv   rw   rx   ry   rf   r   r   r   r   r   r   r   r   r   r   r   rh   __classcell__)r   s   @r   rj   rj   |   s    ( IM;?3!j 9 98 ( (  
G 
G9> 9 9 ' '     ( (,&9(	7:
9! !r    rj   c                   `    \ rS rSrSr\S 5       r\S 5       rS rS r	S r
S rS	 rSS
 jrSrg)CoarseGraini  zRepresents a coarse graining of a collection of nodes.

Attributes:
    partition (tuple[tuple]): The partition of micro-elements into
        macro-elements.
    grouping (tuple[tuple[tuple]]): The grouping of micro-states into
        macro-states.
c                 L    [        [        S U R                   5       5      5      $ )z>Indices of micro elements represented in this coarse-graining.c              3   6   #    U  H  o  H  o"v   M     M     g 7frU   r`   rJ   partidxs      r   rL   ,CoarseGrain.micro_indices.<locals>.<genexpr>       KNDdsCdCN   r   r   r   rX   s    r   r   CoarseGrain.micro_indices       VKDNNKKLLr    c                 P    [        [        [        U R                  5      5      5      $ )z2Indices of macro elements of this coarse-graining.)r   r   r   r   rX   s    r   r   CoarseGrain.macro_indices  s     U3t~~./00r    c                 ,    [        U R                  5      $ rU   r   r   rX   s    r   __len__CoarseGrain.__len__      4>>""r    c                    ^ [        [        U R                  [        U R                  5      5      5      m[	        U4S jU R
                   5       5      n[        XR                  5      $ )a=  Re-index this coarse graining to use squeezed indices.

The output grouping is translated to use indices ``0..n``, where ``n``
is the number of micro indices in the coarse-graining. Re-indexing does
not effect the state grouping, which is already index-independent.

Returns:
    CoarseGrain: A new |CoarseGrain| object, indexed from ``0..n``.

Example:
    >>> partition = ((1, 2),)
    >>> grouping = (((0,), (1, 2)),)
    >>> coarse_grain = CoarseGrain(partition, grouping)
    >>> coarse_grain.reindex()
    CoarseGrain(partition=((0, 1),), grouping=(((0,), (1, 2)),))
c              3   N   >#    U  H  n[        U4S  jU 5       5      v   M     g7f)c              3   .   >#    U  H
  nTU   v   M     g 7frU   r`   rJ   r4   _maps     r   rL   0CoarseGrain.reindex.<locals>.<genexpr>.<genexpr>       15%$u+5r   Nr   rJ   groupr   s     r   rL   &CoarseGrain.reindex.<locals>.<genexpr>  &      
' 15111'   "%)dictzipr   r   r   r   r   grouping)rP   r   r   s     @r   r   CoarseGrain.reindex  sS    " C**GD4F4F,GHI 

 
	 9mm44r    c                    ^ ^^ [        T5      [        T R                  5      :X  d   eT R                  5       m[        R                  " T5      m[        UUU 4S jT R                   5       5      $ )a  Translate a micro state to a macro state

Args:
    micro_state (tuple[int]): The state of the micro nodes in this
        coarse-graining.

Returns:
    tuple[int]: The state of the macro system, translated as specified
    by this coarse-graining.

Example:
    >>> coarse_grain = CoarseGrain(((1, 2),), (((0,), (1, 2)),))
    >>> coarse_grain.macro_state((0, 0))
    (0,)
    >>> coarse_grain.macro_state((1, 0))
    (1,)
    >>> coarse_grain.macro_state((1, 1))
    (1,)
c              3      >#    U  HA  n[        T[        TR                  U   5         5      TR                  U   S    ;   a  S OSv   MC     g7f)r   r   N)r   rN   r   r  )rJ   rK   micro_state	reindexedrP   s     r   rL   *CoarseGrain.macro_state.<locals>.<genexpr>  sR      20 k$y/B/B1/E*FGHa(+,Q1230s   A	A)r   r   r   r$   arrayr   r   rP   r  r  s   ``@r   r   CoarseGrain.macro_state  s^    ( ;3t'9'9#::::
 LLN	hh{+ 2"002 2 	2r    c                     [         R                  " [        U R                  5      5      nU Vs/ s H(  n[        R
                  " U R                  U5      5      PM*     nn[        R                  " U5      $ s  snf )a   Return a mapping from micro-state to the macro-states based on the
partition and state grouping of this coarse-grain.

Return:
    (nd.ndarray): A mapping from micro-states to macro-states. The
    |ith| entry in the mapping is the macro-state corresponding to the
    |ith| micro-state.
)	r   
all_statesr   r   r	   state2le_indexr   r$   r  )rP   micro_statesr  mappings       r   make_mappingCoarseGrain.make_mapping  sk     ''D,>,>(?@ '34&2{ ))$*:*:;*GH&2 	 4xx  4s   /A5c                    [         R                  " USS9  U R                  5       nS[        U R                  5      -  n[
        R                  " X345      n[        S[        U R                  5      -  5      n[        R                  " USS9nU H  u  pxXBU   X(   4==   XU4   -  ss'   M     [
        R                  " U V	s/ s H  n	[        R                  " U	5      PM     sn	5      $ s  sn	f )zCreate a state-by-state coarse-grained macro TPM.

Args:
    micro_tpm (nd.array): The state-by-state TPM of the micro-system.

Returns:
    np.ndarray: The state-by-state TPM of the macro-system.
Fr   r*   r   )r   r'   r  r   r   r$   r   r   r   r   r   r  r
   	normalize)
rP   state_by_state_micro_tpmr  num_macro_statesr   r  micro_state_transitionsprevious_statecurrent_staterows
             r   macro_tpm_sbsCoarseGrain.macro_tpm_sbs  s     	-%H##%D$6$6 77HH.AB	Q#d&8&8"99:"+"3"3L"K
 .E)Nn-w/EEF()FGIF .E
 xx	J	//4	JKKJs    C(c                     [        U5      (       d  [        R                  " U5      nU R                  U5      nU(       a  [        R
                  " U5        [        R                  " U5      $ )a  Create a coarse-grained macro TPM.

Args:
    micro_tpm (nd.array): The TPM of the micro-system.
    check_independence (bool): Whether to check that the macro TPM is
        conditionally independent.

Raises:
    ConditionallyDependentError: If ``check_independence`` is ``True``
        and the macro TPM is not conditionally independent.

Returns:
    np.ndarray: The state-by-node TPM of the macro-system.
)r   r	   r7   r   r   conditionally_independentr;   )rP   	micro_tpmr   r   s       r   r   CoarseGrain.macro_tpm!  sQ     !++<<YGI&&y1	..y933I>>r    r`   N)T)ra   rb   rc   rd   re   rf   r   r   r   r   r   r  r   r   rh   r`   r    r   r   r     sQ     M M 1 1#502@!$L:?r    r   r   r  c                   l    \ rS rSrSr\S 5       r\S 5       r\S 5       rS r	S r
S rS	 rS
 rS rSrg)Blackboxi;  zClass representing a blackboxing of a system.

Attributes:
    partition (tuple[tuple[int]]): The partition of nodes into boxes.
    output_indices (tuple[int]): Outputs of the blackboxes.
c                 |    [        [        [        U R                  5      [        U R                  5      -
  5      5      $ )z*All elements hidden inside the blackboxes.)r   r   r   r   r5   rX   s    r   r   Blackbox.hidden_indicesE  s8     VC 2 23 3 345 6 7 	7r    c                 L    [        [        S U R                   5       5      5      $ )z.Indices of micro-elements in this blackboxing.c              3   6   #    U  H  o  H  o"v   M     M     g 7frU   r`   r   s      r   rL   )Blackbox.micro_indices.<locals>.<genexpr>N  r   r   r   rX   s    r   r   Blackbox.micro_indicesK  r   r    c                 ,    [        U R                  5      $ )z3Fresh indices of macro-elements of the blackboxing.)r   r5   rX   s    r   r   Blackbox.macro_indicesP  s     t**++r    c                 ,    [        U R                  5      $ rU   r   rX   s    r   r   Blackbox.__len__U  r   r    c                     U R                   U   n[        U5      R                  U R                  5      n[	        [        U5      5      $ )zThe outputs of the partition at ``partition_index``.

Note that this returns a tuple of element indices, since coarse-
grained blackboxes may have multiple outputs.
)r   r   r   r5   r   r   )rP   partition_indexr   r   s       r   r   Blackbox.outputs_ofX  s;     NN?3	i.--d.A.ABVG_%%r    c                    ^ [        [        U R                  [        U R                  5      5      5      m[	        U4S jU R
                   5       5      n[	        U4S jU R                   5       5      n[        X5      $ )aG  Squeeze the indices of this blackboxing to ``0..n``.

Returns:
    Blackbox: a new, reindexed |Blackbox|.

Example:
    >>> partition = ((3,), (2, 4))
    >>> output_indices = (2, 3)
    >>> blackbox = Blackbox(partition, output_indices)
    >>> blackbox.reindex()
    Blackbox(partition=((1,), (0, 2)), output_indices=(0, 1))
c              3   N   >#    U  H  n[        U4S  jU 5       5      v   M     g7f)c              3   .   >#    U  H
  nTU   v   M     g 7frU   r`   r   s     r   rL   -Blackbox.reindex.<locals>.<genexpr>.<genexpr>q  r   r   Nr   r   s     r   rL   #Blackbox.reindex.<locals>.<genexpr>p  r  r  c              3   .   >#    U  H
  nTU   v   M     g 7frU   r`   )rJ   rK   r   s     r   rL   r9  t  s     D0C1tAw0Cr   )r  r  r   r   r   r   r5   r'  )rP   r   r5   r   s      @r   r   Blackbox.reindexb  se     C**GD4F4F,GHI 

 
	 D0C0CDD	22r    c                     [        U5      [        U R                  5      :X  d   eU R                  5       n[        R                  " UR
                  U5      $ )zCompute the macro-state of this blackbox.

This is just the state of the blackbox's output indices.

Args:
    micro_state (tuple[int]): The state of the micro-elements in the
        blackbox.

Returns:
    tuple[int]: The state of the output indices.
)r   r   r   r   r   r5   r  s      r   r   Blackbox.macro_statex  sD     ;3t'9'9#::::LLN	~~i66DDr    c                     XR                   ;   d   eX R                   ;   d   eU R                   H  nX;   d  M
  X#;   d  M    g   g)z>Return ``True`` if nodes ``a`` and ``b``` are in the same box.TF)r   r   )rP   abr   s       r   r3   Blackbox.in_same_box  sE    &&&&&&&&&&NNDyQY # r    c                 X    XR                   ;   =(       a    U R                  X5      (       + $ )z=Return True if ``a`` is hidden in a different box than ``b``.)r   r3   )rP   r?  r@  s      r   r   Blackbox.hidden_from  s$    '''F0@0@0F,FFr    r`   N)ra   rb   rc   rd   re   rf   r   r   r   r   r   r   r   r3   r   rh   r`   r    r   r'  r'  ;  se     7 7
 M M , ,#&3,E"	Gr    r'  r5   c                 v    U [         :  a  [        [        U    5      $ [        SR	                  [         5      5      e)a_  Return a list of partitions of the |N| binary nodes.

Args:
    N (int): The number of nodes under consideration.

Returns:
    list[list]: A list of lists, where each inner list is the set of
    micro-elements corresponding to a macro-element.

Example:
    >>> _partitions_list(3)
    [[[0, 1], [2]], [[0, 2], [1]], [[0], [1, 2]], [[0], [1], [2]]]
zBPartition lists not yet available for system with {} nodes or more) _NUM_PRECOMPUTED_PARTITION_LISTSrN   _partition_listsr   rI   )Ns    r   _partitions_listrH    s=     	,-$Q'(("F#CDF 	Fr    c              #      ^ #    [        T 5      n[        U5      nUS:  a  [        [        U5      5      /US'   U H  n[	        U 4S jU 5       5      v   M     g7f)a  Return a list of all possible coarse grains of a network.

Args:
    indices (tuple[int]): The micro indices to partition.

Yields:
    tuple[tuple]: A possible partition. Each element of the tuple
    is a tuple of micro-elements which correspond to macro-elements.
r   r"   c              3   N   >#    U  H  n[        U4S  jU 5       5      v   M     g7f)c              3   .   >#    U  H
  nTU   v   M     g 7frU   r`   )rJ   rK   r   s     r   rL   +all_partitions.<locals>.<genexpr>.<genexpr>  s     3d'!*dr   Nr   )rJ   r   r   s     r   rL   !all_partitions.<locals>.<genexpr>  s&      + ) 3d333 )r  N)r   rH  rN   r   r   )r   r   
partitionsr   s   `   r   all_partitionsrO    sZ      	GA!!$J1uuQx.)
2	 + )+ + 	+  s   AAc              #   f  #    [        U 5      (       d  [        S5      eU  Vs/ s H0  n[        U5      S:  a  [        [        U5      S-   5      OS/S///PM2     nn[        R
                  " U6  H9  n[        S U 5       5      (       d  M  [        [        S U 5       5      5      v   M;     gs  snf 7f)aX  Return all possible groupings of states for a particular coarse graining
(partition) of a network.

Args:
    partition (tuple[tuple]): A partition of micro-elements into macro
        elements.

Yields:
    tuple[tuple[tuple]]: A grouping of micro-states into macro states of
    system.

TODO: document exactly how to interpret the grouping.
z:Each part of the partition must have at least one element.r   r   c              3   >   #    U  H  n[        U5      S :  v   M     g7f)   N)r   )rJ   elements     r   rL    all_groupings.<locals>.<genexpr>  s     8xGs7|axs   c              3   F   #    U  H  n[        S  U 5       5      v   M     g7f)c              3   8   #    U  H  n[        U5      v   M     g 7frU   r   )rJ   rW   s     r   rL   *all_groupings.<locals>.<genexpr>.<genexpr>  s     #EfUE%LLfr   Nr   )rJ   statess     r   rL   rT    s%      6,4& $#Ef#EEE,4s   !N)allr   r   rH  r   r   r   )r   r   micro_groupingsr  s       r   all_groupingsr[    s      y>> $ % 	% 6?@5>T ;>d)a-'D	A6 cA3ZL)5>  @ %%78x888 6,46 6 7 7 8@s    B17B,-B1
'B1c              #   n   #    [        U 5       H"  n[        U5       H  n[        X5      v   M     M$     g7f)zGenerator over all possible |CoarseGrains| of these indices.

Args:
    indices (tuple[int]): Node indices to coarse grain.

Yields:
    CoarseGrain: The next |CoarseGrain| for ``indices``.
N)rO  r[  r   )r   r   r  s      r   all_coarse_grainsr]    s0      $G,	%i0Hi22 1 -s   35c              #      #    [        U R                  5       H;  n[        U5       H)  n[        X5      n [        R
                  " X5        Uv   M+     M=     g! [         a     M?  f = f7f)zGenerator over all |CoarseGrains| for the given blackbox.

If a box has multiple outputs, those outputs are partitioned into the same
coarse-grain macro-element.
N)rO  r5   r[  r   r   rt   r   )r>   r   r  rq   s       r   all_coarse_grains_for_blackboxr_    sc      $H$;$;<	%i0H&y;L228J  1 =
  s(   2A)AA)
A&"A)%A&&A)c              #      #    [        U 5       HF  n[        R                  " U 5       H)  n[        X5      n [        R
                  " U5        Uv   M+     MH     g! [         a     M?  f = f7f)zGenerator over all possible blackboxings of these indices.

Args:
    indices (tuple[int]): Nodes to blackbox.

Yields:
    Blackbox: The next |Blackbox| of ``indices``.
N)rO  r   r   r'  r   r>   r   )r   r   r5   r>   s       r   all_blackboxesra    sb      $G,	 $nnW5N	:H!!(+ N 6 -  s(   3A*AA*
A'#A*&A''A*c                   :    \ rS rSrSr SS jrS r\S 5       rSr	g)	MacroNetworki  a  A coarse-grained network of nodes.

See the :ref:`macro-micro` example in the documentation for more
information.

Attributes:
    network (Network): The network object of the macro-system.
    phi (float): The |big_phi| of the network's major complex.
    micro_network (Network): The network object of the corresponding micro
        system.
    micro_phi (float): The |big_phi| of the major complex of the
        corresponding micro-system.
    coarse_grain (CoarseGrain): The coarse-graining of micro-elements
        into macro-elements.
    time_scale (int): The time scale the macro-network run over.
    blackbox (Blackbox): The blackboxing of micro elements in the network.
    emergence (float): The difference between the |big_phi| of the macro-
        and the micro-system.
Nc                 X    Xl         X l        X0l        X@l        X`l        XPl        Xpl        g rU   )r|   r<   phi	micro_phirp   rq   r>   )rP   r|   r<   	macro_phirf  rq   rp   r>   s           r   rs   MacroNetwork.__init__&  s(     "$( r    c                 N    SR                  U R                  U R                  5      $ )Nz$MacroNetwork(phi={0}, emergence={1}))rI   re  	emergencerX   s    r   r   MacroNetwork.__str__1  s"    5<<HHdnn& 	&r    c                 d    [        U R                  U R                  -
  [        R                  5      $ )z?Difference between the |big_phi| of the macro and micro systems)roundre  rf  r   	PRECISIONrX   s    r   rj  MacroNetwork.emergence5  s$     TXX.0@0@AAr    )r>   rq   rf  r|   re  r<   rp   )r   N)
ra   rb   rc   rd   re   rs   r   rf   rj  rh   r`   r    r   rc  rc    s,    * )-	!& B Br    rc  c                     [        S5      n[        SS5      n[        U5       HA  n [        XUUS9n[
        R                  " U5      nXs-
  [        R                  :  d  M=  UnUnMC     X44$ ! [         a     MU  f = f)a.  Find the maximal coarse-graining of a micro-system.

Args:
    network (Network): The network in question.
    state (tuple[int]): The state of the network.
    internal_indices (tuple[int]): Nodes in the micro-system.

Returns:
    tuple[int, CoarseGrain]: The phi-value of the maximal |CoarseGrain|.
-infr`   rq   )	floatr   r]  rj   r   r   re  r   EPSILON)r|   rW   r   max_phimax_coarse_grainrq   r{   re  s           r   coarse_grainingrw  ;  s     FmG"2r*)*:;	&w7G4@BI
 kk)$MY...G+ < && + 		s   A++
A98A9c              #     ^^#    Uc  S/nU4S jnU4S jn[         R                  " U R                  5       H:  nU H1  nU" U5       H"  n	U" X5       H  n
 [        XUUU	U
S9v   M     M$     M3     M<     g! [        [
        4 a     M7  f = f7f)z:Generator over all possible macro-systems for the network.Nr   c                 .   > T(       d  S /$ [        U 5      $ rU   )ra  )r<   do_blackboxs    r   
blackboxes%all_macro_systems.<locals>.blackboxes_  s    6Mf%%r    c                 J   > T(       d  S /$ U c  [        U5      $ [        U 5      $ rU   )r]  r_  )r>   r<   do_coarse_grains     r   coarse_grains(all_macro_systems.<locals>.coarse_grainse  s)    6M$V,,-h77r    )rp   r>   rq   )r   r   rO   rj   r   r   )r|   rW   rz  r~  time_scalesr{  r  r<   rp   r>   rq   s     ``       r   all_macro_systemsr  Y  s      c&8 ..!5!56%J&v.$1($CL!,#F'1%-)5	7 7 %D / & 7 279 ! !s*   ABA6%B6B
B	B

Bc                 T   [         R                  " X5      R                  n[        S5      nSn[	        XUUUS9 Hj  n[         R                  " U5      n	X-
  [
        R                  :  d  M1  U	n[        U U	UUR                  UR                  UR                  UR                  S9nMl     U$ )aj  Check for the emergence of a micro-system into a macro-system.

Checks all possible blackboxings and coarse-grainings of a system to find
the spatial scale with maximum integrated information.

Use the ``do_blackbox`` and ``do_coarse_grain`` args to specifiy whether to
use blackboxing, coarse-graining, or both. The default is to just
coarse-grain the system.

Args:
    network (Network): The network of the micro-system under investigation.
    state (tuple[int]): The state of the network.
    do_blackbox (bool): Set to ``True`` to enable blackboxing. Defaults to
        ``False``.
    do_coarse_grain (bool): Set to ``True`` to enable coarse-graining.
        Defaults to ``True``.
    time_scales (list[int]): List of all time steps over which to check
        for emergence.

Returns:
    MacroNetwork: The maximal macro-system generated from the
    micro-system.
rq  N)rz  r~  r  )r|   rg  rf  r<   rp   r>   rq   )r   major_complexre  rs  r  r   rt  rc  ro   rp   r>   rq   )
r|   rW   rz  r~  r  rf  ru  max_networkr{   re  s
             r   rj  rj  ~  s    2 %%g599IFmGK&w;7F3>@	 kk)$MY...G&# 33$//"++&335K@  r    c           	         / n[         R                  " U R                  SS9nU H  n[        XU5      n[        R
                  " U5      nUR                  [        U5      XdS /5        [        U5       HB  n [        XUUS9n[        R
                  " U5      nUR                  [        U5      XdU/5        MD     M     U$ ! [         a     MY  f = f)NTr   rr  )r   r   rO   r   r   re  r6   r   r]  rj   r   )	r|   rW   list_of_phisystemsr<   micro_subsystemre  rq   r{   s	            r   phi_by_grainr    s    KnnW11DAG#GF;kk/*C0#tDE-f5L*768DF	
 ++i(CI\JK 6   / s   5B==
C
Cc           
         [         R                  " U 5        [        R                  " U R                  5      n[
        R                  " US5      n[
        R                  " U Vs/ s H  n[        X2S5      PM     sn5      $ s  snf )u  Return the effective information of the given network.

.. note::
    For details, see:

    Hoel, Erik P., Larissa Albantakis, and Giulio Tononi.
    “Quantifying causal emergence shows that macro can beat micro.”
    Proceedings of the
    National Academy of Sciences 110.49 (2013): 19790-19795.

    Available online: `doi: 10.1073/pnas.1314922110
    <http://www.pnas.org/content/110/49/19790.abstract>`_.
r   g       @)r   
is_networkr	   r7   r'   r$   meanr   )r|   sbs_tpmavg_repertoire
repertoires       r   effective_infor    sn      227;;?GWWWa(N77&-/&-
 J<&-/ 0 0 /s   "A?)FFN)FTN);re   r   loggingcollectionsr   numpyr$   scipy.statsr    r   r   r   r	   r
   r   r   
exceptionsr   r   rQ   r   r|   r   r?   r   r   r{   r   r'   r   r   r   	getLoggerra   logrE  	load_datarF  r   r(   r.   rC   rE   rj   r   r'  rH  rO  r[  r]  r_  ra  rc  rw  r  rj  r  r  r`   r    r   <module>r     s9  
   "   P P P J  ) 1   @ @ ! $&  ??#4#CE &
J
0$5<"*]CE "Bb!Y b!J	W?*][*,EF W?t[Gz*{4D&EF [G|F,+(763 *'B 'BT'< JO"&"!J BF.d20r    