
    ^j1                     D   d dl Z d dlmZ d dlmZmZ d dlZd dlmZ  G d de      Zdede	eef   fd	Z
d
ddedee	eef      fdZdede	eef   dedee   fdZd
dde	eef   de	eef   dee	eef   ee   f   fdZe j$                  d
dde	eef   de	eef   fd       Zy)    N)defaultdict)Any
NamedTuple)Tensorc                   J    e Zd ZU dZeeef   ed<   eeef   ed<   ee	   ed<   y)_GroupSwapinInfoa  Per-group bookkeeping for ``swap_in_optimizer_params_and_state``.

    Fields:
        live_group: live ``optimizer.param_groups[i]`` dict, mutated in place.
        swapin_group: per-group hyperparameter dict from the input state_dict
            (e.g. lr, betas, ...) plus packed ``params`` ids.
        swapin_params: replacement parameter tensors for ``live_group["params"]``.
            Same order as ``swapin_group["params"]`` (i.e. the i-th tensor
            corresponds to the i-th packed id), so the two are zippable.
    
live_groupswapin_groupswapin_paramsN)
__name__
__module____qualname____doc__dictstrr   __annotations__listr        a/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/torch/optim/_stateless.pyr   r   
   s/    	 S#XsCx. <r   r   statereturnc                     t        | t              s"t        dt        |       j                   d      t        d | D              rt        d      t        d | j                         D              rt        d      | S )zPCheck ``state`` is a dict mapping packed parameter ids to per-param state dicts.zswap_in_optimizer_params_and_state requires swapin_optim_state['state'] to be a dict mapping packed parameter ids to per-param state dicts, got .c              3   P   K   | ]  }t        |t        j                           y wN)
isinstancetorchr   ).0ks     r   	<genexpr>z(_validate_state_field.<locals>.<genexpr>#   s     
61:a&
6s   $&zmswap_in_optimizer_params_and_state requires optimizer.state_dict()-style state keyed by packed parameter ids.c              3   h   K   | ]*  \  }}t        |t              xr t        |t                , y wr   )r   intr   )r   r    vs      r   r!   z(_validate_state_field.<locals>.<genexpr>(   s-     
Tda:a9jD&9"99
Ts   02zeswap_in_optimizer_params_and_state requires per-parameter optimizer state entries to be dictionaries.)r   r   RuntimeErrortyper   anyitems)r   s    r   _validate_state_fieldr)      s    eT";''(+
 	

 
6
663
 	
 
Tekkm
TT0
 	
 Lr   	optimizerztorch.optim.Optimizerparam_groupsc                     t        |t              s"t        dt        |      j                   d      t        | j                        t        |      k7  rt        d      |S )zW``param_groups`` must be a list whose length matches the live optimizer's param groups.zvswap_in_optimizer_params_and_state requires swapin_optim_state['param_groups'] to be a list of param-group dicts, got r   zVswapin_optim_state has a different number of parameter groups than the live optimizer.)r   r   r%   r&   r   lenr+   )r*   r+   s     r   _validate_param_groups_fieldr.   0   sn     lD)66:<6H6Q6Q5RRSU
 	
 9!!"c,&77"
 	
 r   idxgroupr
   c           	         t        |t              st        d      |j                  d      }t        |t              rt        d |D              st        d      t        |d         t        |      k7  rt        d|  d      |D cg c]	  }||vs| }}|D cg c]	  }||vs| }}|s|rt        d|  d| d	| d
      |S c c}w c c}w )aV  Validate a single swap-in param group against its live counterpart and
    return its packed parameter ids.

    Asserts:
    1. swap-in group is a dict.
    2. ``group['params']`` is a list of ints, and its length matches the params of the live optim group.
    3. Keys inside swapin group should match the keys in the live optim group.
    zZswap_in_optimizer_params_and_state requires each optimizer param group to be a dictionary.paramsc              3   <   K   | ]  }t        |t                y wr   )r   r#   )r   pids     r   r!   z/_validate_group_against_live.<locals>.<genexpr>T   s      9!$
39s   zswap_in_optimizer_params_and_state requires optimizer.state_dict()-style param_groups[*]['params'] entries keyed by packed parameter ids.zswapin_optim_state param group zF has a different number of params than the live optimizer param group.zswap_in_optimizer_params_and_state requires optimizer.state_dict()-style param group keys to exactly match the live optimizer group keys for group z. Keys only in swap-in: z. Keys only in live: r   )r   r   r%   getr   allr-   )r/   r0   r
   swapin_param_idsr    swapin_only	live_onlys          r   _validate_group_against_liver:   A   s    lD)"
 	
 $''1&-S 9(89 6 O
 	
 5?s#344-cU 3: :
 	
 +=aun1=K=!;qQl%:;I;iE %%0M1FykQRT
 	
  >;s   	CC	C%Cswapin_parametersswapin_optim_statec                    | j                   st        d      t        |t              st        d      t	        |j                  d            }t        | |j                  d            }t        |j                               }d}t               }g }t        t        | j                  |d            D ]q  \  }	\  }
}t        |	|
|      }|j                  |       |t        |      z   }|t        |      kD  rt        d      ||| }|}|j!                  t#        |
||	             s |D cg c]	  }||vs| }}|rt        d
|d      ||fS c c}w )a  
    Validate and normalize optimizer state for ``swap_in_optimizer_params_and_state``.

    This follows the same structural assumptions as DCP-compatible optimizers,
    but consumes the raw ``optimizer.state_dict()`` format:
    ``state`` is keyed by packed parameter ids and each param group contains
    the live optimizer group fields plus a packed ``params`` list whose order
    matches ``optimizer.param_groups``.
    zHswap_in_optimizer_params_and_state requires initialized optimizer state.zMswap_in_optimizer_params_and_state requires a DCP-style optimizer state_dict.r   r+   r   Tstrictzrswap_in_optimizer_params_and_state requires the explicit parameter state to match optimizer.param_groups ordering.)r	   r
   r   zswap_in_optimizer_params_and_state requires swapin_optim_state['state'] to be keyed only by packed parameter ids from param_groups[*]['params']; got extra keys r   )r   r%   r   r   r)   r5   r.   r   valuesset	enumeratezipr+   r:   updater-   appendr   )r*   r;   r<   swapin_stateswapin_param_groupsflat_parametersflat_param_offsetseen_param_idsgroup_swapin_infosr/   r0   r
   r7   next_offsetr   r    
extra_keyss                    r   _prepare_swap_inrN   l   s    ??V
 	
 ($/[
 	
 ));)?)?)HIL6%)).9 ,3356O"uN&/I""$7E' #
""e\ 8ULQ./'#.>*??_--9  ((9+F'  	!! )+	
;#
J *EQn-D!EJE99CaI
 	

 +++ Fs   (	E2Ec              #   6  K   t        | ||      \  }}| j                  }| j                  D cg c]  }t        |       }}	 t	        t              }|D ]  }	|	j
                  |	j                  d<   |	j                  j                         D ]  \  }
}|
dk(  r||	j                  |
<    t        |	j
                  |	j                  d   d      D ]#  \  }}t        |j                  |i             ||<   %  || _        d t        | j                  |d      D ]&  \  }}|j                          |j                  |       ( || _        yc c}w # t        | j                  |d      D ]&  \  }}|j                          |j                  |       ( || _        w xY ww)u
  Temporarily replace an optimizer's parameters and state with the
    supplied params and optim states, then restore them on exit.

    For the duration of the context, all optimizer APIs (including
    user hooks) see the swap-in values; the live optimizer is restored on
    exit.

    The difference between this API and ``optimizer.load_state_dict`` is
    that ``optimizer.load_state_dict`` only updates the optimizer's state
    and leaves the parameters in ``param_groups`` untouched. This API also
    swaps in the parameters, so that ``optimizer.step()`` acts on the
    swap-in parameter tensors you supply.

    Args:
        optimizer: the live optimizer; its state must already be
            initialized.
        swapin_parameters: tensors to use as parameters during the context,
            provided in the same order as the existing input parameters to
            the optimizer (most commonly in ``model.named_parameters()``
            order).
        swapin_optim_state: an ``optimizer.state_dict()``-shaped dict
            (``{"state": ..., "param_groups": ...}``) holding the state to
            install. ``"state"`` is keyed by packed integer parameter ids
            and ``"param_groups"`` mirrors ``optimizer.param_groups``,
            with each ``"params"`` entry as a list of those packed ids
            and the remaining keys carrying per-group hyperparameters
            (``lr``, ``betas``, ``foreach``, ``capturable``, ...).
            Only in-place tensor edits propagate back to the user supplied
            ``swapin_optim_state``; all other side-effects (e.g.,
            assigning a new tensor to the optim state)
            are ignored.

    Example:

        One use of this API is to run ``optimizer.step()`` against
        ``FakeTensor`` versions of the parameters and state for nonstrict
        tracing — capturing an FX graph of the step without touching the
        live optimizer::

            from torch.fx.experimental.proxy_tensor import make_fx
            from torch._subclasses import FakeTensorMode
            from torch.utils import _pytree as pytree

            fake_mode = FakeTensorMode(allow_non_fake_inputs=True)
            with fake_mode:
                fake_params = {
                    n: fake_mode.from_tensor(p) for n, p in model.named_parameters()
                }
                fake_osd = pytree.tree_map_only(
                    torch.Tensor, fake_mode.from_tensor, optimizer.state_dict()
                )


            def step_fn(params, osd):
                with swap_in_optimizer_params_and_state(optimizer, params, osd):
                    optimizer.step()
                return params, osd


            gm = make_fx(step_fn)(fake_params, fake_osd)
    r2   Tr>   N)rN   r   r+   r   r   r   r	   r
   r(   rC   r5   clearrD   )r*   r;   r<   r   rK   original_stategoriginal_group_snapshotsrF   infokeyvalueswapin_paramparam_idr0   snapshots                   r   "swap_in_optimizer_params_and_staterZ      s    F !1$&8!E __N
 2;1G1GHAQHH)1<T1B& 	KD )-(:(:DOOH%"//557 -
U(?',$-
 +.""D$5$5h$?+ K&h .2%))Hb2I-J\*K	K$ '	  #""$<T 
 	#OE8 KKMLL"		#
 )	A  I6  #""$<T 
 	#OE8 KKMLL"		#
 )	s*   +FEFB<E >AFA
FF)
contextlibcollectionsr   typingr   r   r   r   r   r   r)   r   r   r.   r#   r:   tuplerN   contextmanagerrZ   r   r   r   <module>r`      s=    # "   z  " c3h *&69	$sCx."(	(S>( ( 
#Y	(VQ,&Q,CK(Q, S#XQ, 4S>4 0112	Q,h k)&k)CK(k) S#Xk) k)r   