
    ph                         S r SSKrSSKrSSKJrJrJr  SSKJr  SSKrSSK	r
SSKJr  SSKJrJr  S rSS	 jrS
 rS r " S S5      rS rS rS rSS jrS r\R                  S 5       rg)zZ
Functions used by more than one PyPhi module or class, or that might be of
external use.
    N)chaincombinationsproduct)time)comb   )config	constantsc                 @   ^ U (       a  [        U4S jU  5       5      $ S$ )z*Return the state-tuple of the given nodes.c              3   .   >#    U  H
  nTU   v   M     g 7fN ).0nnetwork_states     E/home/james-whalen/.local/lib/python3.13/site-packages/pyphi/utils.py	<genexpr>state_of.<locals>.<genexpr>   s     15aq!5s   r   )tuple)nodesr   s    `r   state_ofr      s    5:51511BB    c              #   j   #    U S:X  a  g[        SU S9 H  nU(       a  Uv   M  USSS2   v   M     g7f)aS  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``.
r   N)r   r   )repeat)r   )r   
big_endianstates      r   
all_statesr      s8      	Av*K"+	 +s   13c                 (    SU R                   l        U $ )zMake a NumPy array immutable.F)flags	writeableas    r   np_immutabler$   1   s    AGGHr   c                     U c  [        S5      $ [        R                  " U 5      n [        [        R
                  " U R                  U R                  5      5      R                  5       S5      $ )zReturn a hash of a NumPy array.N   )	hashnpascontiguousarrayinthashlibsha1viewdtype	hexdigestr"   s    r   np_hashr0   7   sN    yDz 	QAw||AFF177O,668"==r   c                   0    \ rS rSrSrS rS rS rS rSr	g)	np_hashableB   z(A hashable wrapper around a NumPy array.c                 @    [        UR                  5       5      U l        g r   )r$   copy_array)selfarrays     r   __init__np_hashable.__init__F   s    "5::<0r   c                 ,    [        U R                  5      $ r   )r0   r6   r7   s    r   __hash__np_hashable.__hash__I   s    t{{##r   c                 X    [         R                  " U R                  UR                  5      $ r   )r(   array_equalr6   )r7   others     r   __eq__np_hashable.__eq__L   s    ~~dkk5<<88r   c                 ,    [        U R                  5      $ r   )reprr6   r<   s    r   __repr__np_hashable.__repr__O   s    DKK  r   )r6   N)
__name__
__module____qualname____firstlineno____doc__r9   r=   rB   rF   __static_attributes__r   r   r   r2   r2   B   s    21$9!r   r2   c                 >    [        X-
  5      [        R                  :*  $ )z%Compare two values up to |PRECISION|.)absr
   EPSILON)xys     r   eqrS   S   s    qu:****r   c                 l   US:X  a  [         R                  " / 5      $ [         R                  " U 5      n US:X  a  U R                  O%[         R                  " SU R                  4/U-  5      n[         R                  " [	        X5      U5      nUR                  U R                  5      R                  SU5      $ )a'  NumPy implementation of ``itertools.combinations``.

Return successive ``r``-length combinations of elements in the array ``a``.

Args:
    a (np.ndarray): The array from which to get combinations.
    r (int): The length of the combinations.

Returns:
    np.ndarray: An array of combinations.
r    r   )r(   asarrayr.   fromiterr   r-   reshape)r#   r	data_typebs       r   combsr\   Y   s     	Avzz"~


1A6rxx"agg!0C'DI
L&	2A66!''?""2q))r   c           	          [        XSS9n[        R                  " [        R                  " [        [        U 5      U5      5      [        X!-  S9nUR                  SU5      $ )a  ``n``-dimensional version of itertools.combinations.

Args:
    a (np.ndarray): The array from which to get combinations.
    k (int): The desired length of the combinations.

Returns:
    np.ndarray: Indices that give the ``k``-combinations of ``n`` elements.

Example:
    >>> n, k = 3, 2
    >>> data = np.arange(6).reshape(2, 3)
    >>> data[:, comb_indices(n, k)]
    array([[[0, 1],
            [0, 2],
            [1, 2]],
    <BLANKLINE>
           [[3, 4],
            [3, 5],
            [4, 5]]])
T)exact)countr   )	r   r(   rW   r   from_iterabler   ranger*   rX   )r   kr_   indicess       r   comb_indicesrd   p   sR    . T"EkkLq156yG
 ??2q!!r   c                    ^  [        T 5      m U(       a  SnOSn[        U[        T 5      S-   5      nU(       a  [        U5      nT R	                  5         [
        R                  " U 4S jU 5       5      $ )af  Generate the power set of an iterable.

Args:
    iterable (Iterable): The iterable from which to generate the power set.

Keyword Args:
    nonempty (boolean): If True, don't include the empty set.
    reverse (boolean): If True, reverse the order of the powerset.

Returns:
    Iterable: An iterator over the power set.

Example:
    >>> ps = powerset(np.arange(2))
    >>> list(ps)
    [(), (0,), (1,), (0, 1)]
    >>> ps = powerset(np.arange(2), nonempty=True)
    >>> list(ps)
    [(0,), (1,), (0, 1)]
    >>> ps = powerset(np.arange(2), nonempty=True, reverse=True)
    >>> list(ps)
    [(1, 0), (1,), (0,)]
r   r   c              3   <   >#    U  H  n[        TU5      v   M     g 7fr   )r   )r   rY   iterables     r   r   powerset.<locals>.<genexpr>   s     L)Q|Ha88)s   )listra   lenreversedreverser   r`   )rg   nonemptyrl   start	seq_sizess   `    r   powersetrp      s`    0 H~HeS]Q./IY'	L)LLLr   c                   ^ ^ [         R                  R                  [         R                  R                  [        5      5      mU U4S jn[        U5       Vs/ s H  n[        R                  " U" U5      SS9PM      sn$ s  snf )zLoad numpy data from the data directory.

The files should stored in ``../data/<dir>`` and named
``0.npy, 1.npy, ... <num - 1>.npy``.

Returns:
    list: A list of loaded data, such that ``list[i]`` contains the the
    contents of ``i.npy``.
c                 `   > [         R                  R                  TST[        U 5      S-   5      $ )Ndataz.npy)ospathjoinstr)i	directoryroots    r   get_pathload_data.<locals>.get_path   s$    ww||D&)SVf_EEr   T)allow_pickle)rt   ru   abspathdirname__file__ra   r(   load)ry   numr{   rx   rz   s   `   @r   	load_datar      sX     77??277??845DF >C3ZHZBGGHQKd3ZHHHs   %A?c                     [        5       nU " U0 UD6n[        5       n[        XS-
  [        R                  5      Ul         U$ )z{Annotate the decorated function or method with the total execution
time.

The result is annotated with a `time` attribute.
)r   roundr	   	PRECISION)funcargskwargsrn   resultends         r   time_annotatedr      s<     FE4"6"F
&CV%5%56FKMr   )F)FF)rL   r+   rt   	itertoolsr   r   r   r   	decoratornumpyr(   scipy.specialr   rU   r	   r
   r   r   r$   r0   r2   rS   r\   rd   rp   r   r   r   r   r   <module>r      s}   

  	 2 2     C
,>! !"+*."D%MPI( 
 
r   