
    z	i{4                    ~   S r SSKJr  SSKrSSKrSSKrSSKrSSKrSSKrSSK	r	SSK
Jr  SSKJr  \R                  " 5       rS rS rS r\R&                  SS	 j5       rS
 rSS jrSqSqSrSr\R&                  SSS jj5       r\R8                  S 5       r\R8                  SS j5       r\\l        \\l         SS jr!g)zi
Routines for running Python functions in parallel using process pools
from the multiprocessing library.
    )annotationsN)ProcessPoolExecutor)user_configc                $    U u  pp4U" U/UQ70 UD6$ N )paramtaskvalue	task_argstask_kwargss        O/home/james-whalen/.local/lib/python3.13/site-packages/qiskit/utils/parallel.py_task_wrapperr   H   s!    ,1)T)1	1[11    c                     [        [        SS 5      =n b  [        U " S5      5      nO[        R                  " 5       =(       d    SnUS-  =(       d    S$ )Nsched_getaffinityr         )getattroslen	cpu_count)r   num_cpuss     r   #_physical_cpus_assuming_twofold_smtr   M   sI    $R)<dCCP (+,<<>&QMar   c                 `    [         R                  " SS9=n c  [        R                  S;  $ U S;   $ )NT)
allow_none)darwinwin32)fork
forkserver)multiprocessingget_start_methodsysplatform)set_start_methods    r   _parallel_defaultr&   X   s7    +<<MMV ||#666555r   c                    [         R                  " S5      =n (       a   [        U 5      n U S:  a  U $ S$ [        R                  SS5      =nb
  US:  a  U$ S$ [        5       $ ! [         a    [        R
                  " SU  S35         NSf = f)aE  Get the number of processes that a multiprocessing parallel call will use by default.

Such functions typically also accept a ``num_processes`` keyword argument that will supersede
the value returned from this function.

In order of priority (highest to lowest), the return value will be:

1. The ``QISKIT_NUM_PROCS`` environment variable, if set.
2. The ``num_processes`` key of the Qiskit user configuration file, if set.
3. Half of the logical CPUs available to this process, if this can be determined.  This is a
   proxy for the number of physical CPUs, assuming two-fold simultaneous multithreading (SMT);
   empirically, multiprocessing performance of Qiskit seems to be worse when attempting to use
   SMT cores.
4. 1, if all else fails.

If a user-configured value is set to a number less than 1, it is treated as if it were 1.
QISKIT_NUM_PROCSr   r   zAfailed to interpret environment 'QISKIT_NUM_PROCS' as a number: ''num_processesN)	r   getenvint
ValueErrorwarningswarnCONFIGgetr   )env_num_processesuser_num_processess     r   default_num_processesr4   a   s    ( II&8999		E #$5 6 ):A(=$D1D$jj$??L%7!%;!BB.00  	MM&'q*	s   A! !$BBc                     [         R                  " 5       SR                  [         R                  " 5       5      [         R                  " 5       [         R
                  " 5       [        5       S.$ )a  Basic hardware information about the local machine.

Attempts to estimate the number of physical CPUs in the machine, even when hyperthreading is
turned on. CPU count defaults to 1 when true count can't be determined.

Returns:
    dict: The hardware information.
z, )python_compilerpython_buildpython_versionr   cpus)r$   r6   joinr7   r8   systemr   r   r   r   local_hardware_infor<      sK     $335		("7"7"9:"113oo35 r   c                 0    [         R                  " 5       SL $ )aU  Checks whether the current process is the main one.

Since Python 3.8, this is identical to the standard Python way of calculating this::

    >>> import multiprocessing
    >>> multiprocessing.parent_process() is None

This function is left for backwards compatibility, but there is little reason not to use the
built-in tooling of Python.
N)r!   parent_processr   r   r   is_main_processr?      s     ))+t33r   FFALSETRUEc                \   U c
  [        5       OU n U S:  a  g[        R                  " S[        5      [        :w  a  g[        b  [        $ [
        (       a
  [        5       $ [        R                  " S5      =nb  UR                  5       S:H  $ [        R                  SS5      =nb  U$ [        5       $ )a  Decide whether a multiprocessing function should spawn subprocesses for parallelization.

In particular, this is how :func:`parallel_map` decides whether to use multiprocessing or not.
The ``num_processes`` argument alone does not enforce parallelism; by default, Qiskit will only
use process-based parallelism when a ``fork``-like process spawning start method is in effect.
You can override this decision either by setting the :mod:`multiprocessing` start method you
use, setting the ``QISKIT_PARALLEL`` environment variable to ``"TRUE"``, or setting
``parallel = true`` in your user settings file.

This function includes two context managers that can be used to temporarily modify the return
value of this function:

.. autofunction:: qiskit.utils::should_run_in_parallel.override
.. autofunction:: qiskit.utils::should_run_in_parallel.ignore_user_settings

Args:
    num_processes: the maximum number of processes requested for use (``None`` implies the
        default).

Examples:
    Temporarily override the configured settings to disable parallelism::

        >>> with should_run_in_parallel.override(True):
        ...     assert should_run_in_parallel(8)
        >>> with should_run_in_parallel.override(False):
        ...     assert not should_run_in_parallel(8)
Nr   FQISKIT_IN_PARALLELQISKIT_PARALLELtrueparallel_enabled)
r4   r   r+   _IN_PARALLEL_ALLOW_PARALLELISM_PARALLEL_OVERRIDE_PARALLEL_IGNORE_USER_SETTINGSr&   lowerr0   r1   )r*   env_qiskit_paralleluser_qiskit_parallels      r   should_run_in_parallelrM      s    > 0=/D)+-Mq
		&(FG)	* %!!%% ""!yy):;;H"((*f44 &

+=t DDQ##r   c               #     #    [         R                  5         [        Ssn q Sv   U q[         R                  5         g! U q[         R                  5         f = f7f)zA context manager within which :func:`should_run_in_parallel` will ignore environmental
configuration variables.

In particular, the ``QISKIT_PARALLEL`` environment variable and the user-configuration file are
ignored within this context.TN)rM   cache_clearrI   )previouss    r   _parallel_ignore_user_settingsrQ      sO      &&(/Mt,H,-)1&**, *2&**,   A< AAAc              #     #    [         R                  5         [        U snq Sv   Uq[         R                  5         g! Uq[         R                  5         f = f7f)a  A context manager within which :func:`should_run_in_parallel` will return the given
``value``.

This is not a *complete* override; Qiskit will never attempt to parallelize if only a single
process is available, and will not allow process-based parallelism at a depth greater than 1.N)rM   rO   rH   )r   rP   s     r   _parallel_overriderT      sO      &&(#5u H -%**, &**,rR   c                :  ^ ^^ Tc  0 OTmUc
  [        5       n[        U5      S:  d  [        U5      (       d  U Vs/ s H  nT " U/TQ70 TD6PM     sn$ U UU4S jU 5       n[        R                  " S[
        5      n[        [        R                  S'    [        US9 n[        UR                  [        U5      5      sSSS5        U[        R                  S'   $ s  snf ! , (       d  f       O= f U[        R                  S'   g! U[        R                  S'   f = f)a  
Parallel execution of a mapping of `values` to the function `task`. This
is functionally equivalent to::

    result = [task(value, *task_args, **task_kwargs) for value in values]

This will parallelise the results if the number of ``values`` is greater than one and
:func:`should_run_in_parallel` returns ``True``.  If not, it will run in serial.

Args:
    task (func): Function that is to be called for each value in ``values``.
    values (array_like): List or array of values for which the ``task`` function is to be
        evaluated.
    task_args (list): Optional additional arguments to the ``task`` function.
    task_kwargs (dict): Optional additional keyword argument to the ``task`` function.
    num_processes (int): Number of processes to spawn.  If not given, the return value of
        :func:`default_num_processes` is used.

Returns:
    result: The result list contains the value of ``task(value, *task_args, **task_kwargs)`` for
    each value in ``values``.

Examples:

    .. plot::
       :include-source:
       :nofigs:

        import time
        from qiskit.utils import parallel_map
        def func(_):
                time.sleep(0.1)
                return 0
        parallel_map(func, list(range(10)));
Nr   c              3  0   >#    U  H  nTUTT4v   M     g 7fr   r   ).0r   r
   r   r   s     r   	<genexpr>parallel_map.<locals>.<genexpr>5  s     LVE4	;7Vs   rC   )max_workers)r4   r   rM   r   r+   rG   _IN_PARALLEL_FORBID_PARALLELISMenvironr   listmapr   )	r
   valuesr   r   r*   r   
work_itemsprevious_in_parallelexecutors	   ` ``     r   parallel_maprc     s    H $+"K-/
6{Q4]CCDJKF5U6Y6+6FKKLVLJ 99%9;YZ'FBJJ#$@ ];x]J?@ <; ,@

'( L <;; ,@

'(+?

'(s)   C	D C=	D 
C-)D D)returnr,   )rd   boolr   )r*   z
int | Nonerd   re   )r   re   )r   NN)"__doc__
__future__r   
contextlib	functoolsr!   r   r$   r#   r.   concurrent.futuresr   qiskitr   
get_configr0   r   r   r&   cacher4   r<   r?   rH   rI   rG   r[   rM   contextmanagerrQ   rT   ignore_user_settingsoverriderc   r   r   r   <module>rq      s   d
 #    	  
  2  
			!2
 6  1  1F$4  !& !( "(  1 1h - -& - -& /M  +"4  2@r   