
    ,Vh8                        d Z ddlZddlZddlZddlZddlmZ ddlmZ ddl	m
Z ddl	mZ ddlmZmZmZmZmZmZmZmZ ddlmZmZ dd	lmZmZmZ g d
Z G d dej>                        Z d dZ!dedee   dedee"e#f   dee#e#f   f
dZ$de#dee   dedee"e#f   dededeee#e#f   fdZ% G d de       Z&	 d!dee   dedee"e#f   dee#   dedefdZ' ejP                  e'd      Z)y)"z@Support for random optimizers, including the random-greedy path.    N)deque)Decimal)choices)seed)AnyDict	GeneratorIterableListOptionalTupleUnion)helperspaths)ArrayIndexType	ArrayTypePathType)RandomGreedyrandom_greedyrandom_greedy_128c                   \   e Zd ZdZ	 	 	 	 	 ddedee   dedee	e
ef   def
dZed	efd
       Zed	ee	e
ef   fd       Zej                   dee	e
ef   d	dfd       Zdee   deded	eeddf   fdZddZdee   dedeeef   d	eeef   fdZ	 ddee   dedeeef   dee   d	ef
dZd Zy)RandomOptimizera  Base class for running any random path finder that benefits
    from repeated calling, possibly in a parallel fashion. Custom random
    optimizers should subclass this, and the `setup` method should be
    implemented with the following signature:

    ```python
    def setup(self, inputs, output, size_dict):
        # custom preparation here ...
        return trial_fn, trial_args
    ```

    Where `trial_fn` itself should have the signature::

    ```python
    def trial_fn(r, *trial_args):
        # custom computation of path here
        return ssa_path, cost, size
    ```

    Where `r` is the run number and could for example be used to seed a
    random number generator. See `RandomGreedy` for an example.


    Parameters:
        max_repeats: The maximum number of repeat trials to have.
        max_time: The maximum amount of time to run the algorithm for.
        minimize:  Whether to favour paths that minimize the total estimated flop-count or
            the size of the largest intermediate created.
        parallel: Whether to parallelize the random trials, by default `False`. If
            `True`, use a `concurrent.futures.ProcessPoolExecutor` with the same
            number of processes as cores. If an integer is specified, use that many
            processes instead. Finally, you can supply a custom executor-pool which
            should have an API matching that of the python 3 standard library
            module `concurrent.futures`. Namely, a `submit` method that returns
            `Future` objects, themselves with `result` and `cancel` methods.
        pre_dispatch: If running in parallel, how many jobs to pre-dispatch so as to avoid
            submitting all jobs at once. Should also be more than twice the number
            of workers to avoid under-subscription. Default: 128.

    Attributes:
        path: The best path found so far.
        costs: The list of each trial's costs found so far.
        sizes: The list of each trial's largest intermediate size so far.
    Nmax_repeatsmax_timeminimizeparallelpre_dispatchc                    |dvrt        d      || _        || _        || _        t	        j
                  |      | _        d| _        || _        || _	        g | _
        g | _        t        d      t        d      d| _        d| _        |  |  y )N)flopssizez.`minimize` should be one of {'flops', 'size'}.Finfr   )
ValueErrorr   r   r   r   get_better_fnbetter	_parallelr   r   costssizesfloatbest_repeats_start)selfr   r   r   r   r   s         F/home/dcms/DCMS/lib/python3.12/site-packages/opt_einsum/path_random.py__init__zRandomOptimizer.__init__A   s     ,,MNN&  ))(349 ( "
 "
.3ElE%L$Q	    returnc                 F    t        j                  | j                  d         S )zThe best path found so far.ssa_path)r   ssa_to_linearr)   r+   s    r,   pathzRandomOptimizer.path\   s     ""499Z#899r.   c                     | j                   S N)r%   r3   s    r,   r   zRandomOptimizer.parallela   s    ~~r.   c                 D   t        | dd      r| j                  j                          || _        d| _        |du rd | _        y |du rddlm}  |       | _        d| _        y t        |t        t        f      r$ddlm}  |t        |            | _        d| _        y || _        y )N_managing_executorFTr   )ProcessPoolExecutor)
getattr	_executorshutdownr%   r8   concurrent.futuresr9   
isinstanceintr   )r+   r   r9   s      r,   r   zRandomOptimizer.parallele   s     4-u5NN##%!"'u!DNt>02DN&*D#hg/>0X?DN&*D# "r.   repeatstrial_fnargsc              #     K   t               | _        |D ]  }t        | j                        | j                  k  r8| j                  j	                   | j
                  j                  ||g|        ]| j                  j                         j                           | j                  r8| j                  j                         j                          | j                  r7yyw)zMLazily generate results from an executor without submitting all jobs at once.N)	r   _futureslenr   appendr;   submitpopleftresult)r+   r@   rA   rB   rs        r,   _gen_results_parallelz%RandomOptimizer._gen_results_parallel   s       	3A4==!D$5$55$$%:T^^%:%:8Q%N%NO--'')0022		3 mm--'')0022 mms   CC" C"c                 `    | j                   "| j                  D ]  }|j                           y y r6   )r;   rD   cancel)r+   fs     r,   _cancel_futureszRandomOptimizer._cancel_futures   s-    >>%]] 
 &r.   inputsoutput	size_dictc                     t         r6   )NotImplementedError)r+   rP   rQ   rR   s       r,   setupzRandomOptimizer.setup   s
     "!r.   memory_limitc                 D   | j                  |||       | j                  t        j                         }| j                  |||      \  | j                  t        | j                        z   }|| j                  z   }t        ||      }| j                  | j                  |      }	nfd|D        }	|	D ]  \  }
}}| j                  j                  |       | j                  j                  |       | j                  ||| j                  d   | j                  d         }|r-|| j                  d<   || j                  d<   |
| j                  d<   | j                  t        j                         | j                  z   kD  s n | j                          | j                   S )Nc              3   0   K   | ]  } |g   y wr6    ).0rJ   
trial_argsrA   s     r,   	<genexpr>z+RandomOptimizer.__call__.<locals>.<genexpr>   s     @1hq.:.@s   r   r    r1   )_check_args_against_first_callr   timerU   r*   rE   r&   r   ranger;   rK   rF   r'   r$   r)   rO   r4   )r+   rP   rQ   rR   rV   t0r_startr_stopr@   trialsr1   costr    found_new_bestr[   rA   s                 @@r,   __call__zRandomOptimizer.__call__   sr    	++FFIF ==$B#zz&&)D*%%DJJ74+++( >>%//:NF@@F %+ 	 HdDJJd#JJd# "[[tTYYw5GSYIZ[N%)		'"$(		&!(0		*% )		b4==>P0P	" 	yyr.   c                 T    t        | dd      r| j                  j                          y y )Nr8   F)r:   r;   r<   r3   s    r,   __del__zRandomOptimizer.__del__   s$    4-u5NN##% 6r.   )    Nr   F   )r/   Nr6   )__name__
__module____qualname____doc__r?   r   r(   strr   boolr   r-   propertyr   r4   r   setterr
   r   r	   rK   rO   r   r   r   r   rU   rf   rh   rY   r.   r,   r   r      s   +^ $(.3 5/ 	
 gs*+ 6 :h : : %gs 23   __"tWc'9!: "t " ":3Xc] 3c 3QT 3YbcfhlnrcrYs 3
"^$" " S>	"
 
sCx" '+,^$, , S>	,
 sm, 
,\&r.   r   c                 t   d}g }| rJ||k  rEt        j                  |       \  }}}	}
||vs|	|vr*|j                  |||	|
f       |dz  }| r||k  rE|dk(  ry|dk(  r|d   S |D cg c]
  }|d   d    }}|d   }|r|t        dt	        |            z  }|dk(  r|D cg c]  }||k(  rdnd }}n)|D cg c]  }t        j                  ||z
   |z          }}t        t        |      |      \  }|j                  |      \  }}}	}
|D ]  }t        j                  | |        |||	|
fS c c}w c c}w c c}w )a^  A contraction 'chooser' that weights possible contractions using a
    Boltzmann distribution. Explicitly, given costs `c_i` (with `c_0` the
    smallest), the relative weights, `w_i`, are computed as:

        $$w_i = exp( -(c_i - c_0) / temperature)$$

    Additionally, if `rel_temperature` is set, scale `temperature` by
    `abs(c_0)` to account for likely fluctuating cost magnitudes during the
    course of a contraction.

    Parameters:
        queue: The heapified list of candidate contractions.
        remaining: Mapping of remaining inputs' indices to the ssa id.
        temperature: When choosing a possible contraction, its relative probability will be
            proportional to `exp(-cost / temperature)`. Thus the larger
            `temperature` is, the further random paths will stray from the normal
            'greedy' path. Conversely, if set to zero, only paths with exactly the
            same cost as the best at each step will be explored.
        rel_temperature: Whether to normalize the `temperature` at each step to the scale of
            the best cost. This is generally beneficial as the magnitude of costs
            can vary significantly throughout a contraction.
        nbranch: How many potential paths to calculate probability for and choose from at each step.

    Returns:
        cost
        k1
        k2
        k3
    r      Ng        )weights)heapqheappoprF   maxabsmathexprandom_choicesr_   popheappush)queue	remainingnbranchtemperaturerel_temperaturenr   rd   k1k2k12choicer&   cmincenergieschosenothers                     r,   thermal_chooserr      s   < 	
AG
AK!MM%0b"cY"I"5b"c*+	Q AK 	AvAvqz(/0fVAYq\0E08D s1c$i(( c389adA)99 BGGADHHq4x[;67GG uQx:IVF+D"b#  %ue$% R- 1 : Hs   %D+D05#D5r1   rP   rQ   rR   r/   c           	         t        t        t        |            }t        |      }t        t	        t        |                  }d}d}| D ]  \  }}t        j                  ||||||      \  }	}
|j                  |       |j                  |       |j                  t        |             |j                  |	       ||
z  }t        |t        j                  |	|            } ||fS )z.Compute the flops and max size of an ssa path.r   )listmap	frozensetsetr_   rE   r   calc_k12_flopsdiscardaddrF   rx   r   compute_size_by_dict)r1   rP   rQ   rR   r   
total_costmax_sizeijr   flops12s              r,   ssa_path_compute_costr     s     #i()FvFE#f+&'IJH O1++FFIq!YWW!!c&k"cg
x!=!=c9!MNO xr.   rJ   	choose_fncost_fnc                     | dk(  rd}t        |        t        j                  |||||      }t        ||||      \  }}|||fS )zKA single, repeatable, greedy trial run. **Returns:** ``ssa_path`` and cost.r   N)random_seedr   ssa_greedy_optimizer   )	rJ   rP   rQ   rR   r   r   r1   rd   r    s	            r,   _trial_greedy_ssa_path_and_costr   1  sP     	Av	N((IwWH&xKJD$T4r.   c                        e Zd Z	 	 	 	 ddededededef
 fdZe	defd       Z
d	ee   d
edeeef   deeef   fdZ xZS )r   r   r   r   r   kwargsc                 \    || _         || _        || _        || _        t	        |   di | y)a  Parameters:
        cost_fn: A function that returns a heuristic 'cost' of a potential contraction
                with which to sort candidates. Should have signature
                `cost_fn(size12, size1, size2, k12, k1, k2)`.
        temperature: When choosing a possible contraction, its relative probability will be
                proportional to `exp(-cost / temperature)`. Thus the larger
                `temperature` is, the further random paths will stray from the normal
                'greedy' path. Conversely, if set to zero, only paths with exactly the
                same cost as the best at each step will be explored.
        rel_temperature: Whether to normalize the ``temperature`` at each step to the scale of
                the best cost. This is generally beneficial as the magnitude of costs
                can vary significantly throughout a contraction. If False, the
                algorithm will end up branching when the absolute cost is low, but
                stick to the 'greedy' path when the cost is high - this can also be
                beneficial.
        nbranch: How many potential paths to calculate probability for and choose from at each step.
        kwargs: Supplied to RandomOptimizer.
        NrY   )r   r   r   r   superr-   )r+   r   r   r   r   r   	__class__s         r,   r-   zRandomGreedy.__init__G  s3    4 &."6"r.   r/   c                     | j                   dk(  ryt        j                  t        | j                  | j                   | j
                        S )zThe function that chooses which contraction to take - make this a
        property so that ``temperature`` and ``nbranch`` etc. can be updated
        between runs.
        rt   N)r   r   r   )r   	functoolspartialr   r   r   r3   s    r,   r   zRandomGreedy.choose_fng  sB     <<1  ((LL 00	
 	
r.   rP   rQ   rR   c                 L    t         }|||| j                  | j                  f}||fS r6   )r   r   r   )r+   rP   rQ   rR   fnrB   s         r,   rU   zRandomGreedy.setupw  s*     -	4>>4<<H4xr.   )zmemory-removed-jitterg      ?T   )rk   rl   rm   ro   r(   rp   r?   r   r-   rq   r   r   r   r   r   rU   __classcell__)r   s   @r,   r   r   F  s     /  $## # 	#
 # #@ 
3 
 
^$  S>	
 
sCxr.   r   idx_dictrV   optimizer_kwargsc                 .    t        di |} || |||      S )z5A simple wrapper around the `RandomGreedy` optimizer.rY   )r   )rP   rQ   r   rV   r   	optimizers         r,   r   r     s#     0/0IVVX|<<r.   rj   )r   )r   rt   Tr6   )*rn   r   rv   rz   r^   collectionsr   decimalr   randomr   r|   r   r   typingr   r   r	   r
   r   r   r   r   
opt_einsumr   r   opt_einsum.typingr   r   r   __all__PathOptimizerr   r   ro   r?   r   r   r   r   r   r   rY   r.   r,   <module>r      sr   F       , & O O O % A A
@}&e)) }&@BJ       CH~	 
 38_ 2 
      CH~	 
     8S# *9? 9@ #'		= 	=	= 38n	= 3-		=
 	= 	= &I%%mE r.   