
    ^jSH              	       &   d Z ddlZddlZddlmZ ddlmZ ddlmZ  e       Z	dZ
deded	dfd
Zddededed	efdZded	efdZdeej"                  e   z  d	dfdZdeeef   d	eeef   fdZdeeef   ded	eeef   fdZdeeef   ded	dfdZy)z"Checkpoint and state-dict helpers.    N)OrderedDict)Any)
get_logger)
state_dictglobal_stepzpytorch-lightning_versionloopsoptimizer_stateslr_schedulersckpt_patch_sizemodel_patch_sizereturnc                 &    t        d|  d| d      )uH  Raise a descriptive ValueError for a patch_size incompatibility.

    Args:
        ckpt_patch_size: patch_size recorded in (or inferred from) the checkpoint.
        model_patch_size: patch_size the current model is configured with.

    Raises:
        ValueError: Always — describes the mismatch and how to resolve it.
    z+The checkpoint was trained with patch_size=z(, but the current model uses patch_size=z. The checkpoint is incompatible with this model architecture. To resolve this, either instantiate/configure the model with the checkpoint's patch_size or use a checkpoint that was trained with the same patch_size as the current model.)
ValueError)r   r   s     f/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/utilities/state_dict.py_raise_patch_size_mismatchr      s1     
5o5F G&' ([	[     argsfielddefaultc                 `    t        | t              r| j                  ||      S t        | ||      S )ad  Get a field from checkpoint ``"args"``, handling both dict and attribute access.

    New checkpoints (PTL training stack) store ``"args"`` as a plain ``dict`` (via ``TrainConfig.model_dump()``).
    Legacy checkpoints (pre-PTL engine or the pre-release PTL code) stored it as a ``Namespace``-like object.  This
    helper abstracts both so callers do not need to branch on the type.

    Args:
        args: The ``checkpoint["args"]`` value.
        field: Field name to retrieve.
        default: Value returned when the field is absent.

    Returns:
        The field value, or ``default`` if not found.
    )
isinstancedictgetgetattr)r   r   r   s      r   _ckpt_args_getr   /   s.     $xxw''4((r   epochc                     | dz   }ddddd}dddd}ddd}||||d}i ddii |i |ddi |i |d	i d
i |i |d	i |i |d	dii i |i |d	i i |i |ddi |i |d	dS )u  Build a minimal ``fit_loop`` state dict that restores the epoch counter.

    ``BestModelCallback`` stores ``trainer.current_epoch`` as ``"epoch"`` in the checkpoint.  That value is captured
    during ``on_validation_end``, which fires *before* the loop's epoch-end hooks increment the counter.  To resume
    training *after* that epoch, PTL's epoch-progress counter must be set to ``epoch + 1`` so that
    ``trainer.current_epoch == epoch + 1`` when the new ``trainer.fit()`` call begins.

    Optimizer and scheduler states are intentionally omitted — loading a ``.pth`` file starts a fresh optimizer for the
    new training phase.

    Args:
        epoch: The ``"epoch"`` value from the checkpoint (``trainer.current_epoch``
            at the time of ``on_validation_end``).

    Returns:
        A ``fit_loop`` state dict compatible with :meth:`pytorch_lightning.loops._FitLoop.load_state_dict`.
       r   )readystarted	processed	completed)r   r    r"   )r   r"   _batches_that_steppedF)totalcurrentis_last_batch)r$   r%   	optimizer)step	zero_grad)r   zepoch_loop.state_dictzepoch_loop.batch_progresszepoch_loop.scheduler_progressz,epoch_loop.automatic_optimization.state_dictz0epoch_loop.automatic_optimization.optim_progressz)epoch_loop.manual_optimization.state_dictz2epoch_loop.manual_optimization.optim_step_progresszepoch_loop.val_loop.state_dictz"epoch_loop.val_loop.batch_progressepoch_progress )r   nzero4zero3zero2n4s         r   _make_fit_loop_stater1   C   s   $ 		AAAAFEAA6Ea(E	CB"91!=Y %y"&
 Y %y*
 9;&Y(%y
 'Y(%y	=
 68Y %y?
 +-Y %y"/
 V"v
I( (r   
checkpointc                     ddl }|j                  | dd      }|d   |d   d}d	|v r|d	   |d	<   d
|v r|d
   |d
<   t        D ]  }||v s||   ||<    d|v r|d   }dD ]  }||vsdi i||<    t        j                  j                  t        j                  j                  t        j                  |                   }t        j                  |d      5 }|j                  }	ddd       	 |j                  |	       t        j                  |	|        t        j                  j                  |	      rt        j                  |	       yy# 1 sw Y   hxY w# t        j                  j                  	      rt        j                  |	       w w xY w)a  Strip a checkpoint file down to ``model``, ``args``, and PTL-compatible keys.

    Preserves ``model_name`` (when present) so that ``RFDETR.from_checkpoint()`` can still resolve the model class from
    the stripped file.  Also preserves ``rfdetr_version`` (when present) for provenance tracking.

    Also preserves ``state_dict``, ``global_step``, ``pytorch-lightning_version``, ``loops``, ``optimizer_states``, and
    ``lr_schedulers`` when present so the stripped checkpoint can still be used directly with
    ``trainer.fit(ckpt_path=...)``.

    Overwrites the file atomically so a partial write cannot corrupt it.

    Args:
        checkpoint: Path to the ``.pth`` checkpoint file to strip in place.
    r   NcpuF)map_locationweights_onlymodelr   )r7   r   
model_namerfdetr_versionr   )validate_loop	test_loopr   )dirdelete)torchload_PTL_COMPAT_KEYSospathdirnameabspathfspathtempfileNamedTemporaryFilenamesavereplaceexistsremove)
r2   r>   r   new_state_dictkeyr   loop_keycheckpoint_dirtmp_filetmp_paths
             r   strip_checkpointrS      s     JUOJG$6"N
 z!'1,'?|$:%+56F+G'( 2*",S/N32 . w'6 	5Hu$#/"4h	5 WW__RWW__RYYz5J%KLN		$	$	F !(==! 

>8,


8Z(77>>(#IIh $! ! 77>>(#IIh $s   E
,(E 
E7Fr   c                 n    t               }| j                         D ]  \  }}|dd dk(  r|dd }|||<    |S )a
  Remove the ``module.`` prefix added by ``DataParallel`` / ``DistributedDataParallel``.

    Args:
        state_dict: State dict potentially containing ``module.``-prefixed keys.

    Returns:
        New ``OrderedDict`` with ``module.`` stripped from all keys.
    N   zmodule.)r   items)r   rM   kvs       r   clean_state_dictrY      sR     !]N  " 1Ra5I!"Aq r   r7   c                    t        |d      r|j                  d   nd}|| S t        |dd      du}t        t        |dd            xs |}|s| S t	        d | D              r| S | j                         D ci c]  \  }}|j                  d      s|| }}}|s| S t        j                  d	t        |             |j                         D ]+  \  }}|j                  dd
d      }|j                         | |<   - | S c c}}w )a7  Clone backbone projector weights into ``cross_attn_projector`` for dual-projector models.

    Older checkpoints can contain only ``backbone.0.projector.*`` keys. Dual-projector models expect an additional
    ``backbone.0.cross_attn_projector.*`` branch; this helper seeds it by cloning projector weights when missing.

    Args:
        state_dict: Checkpoint model-state dictionary (mutated in place).
        model: Instantiated model used to detect whether dual projector mode is active.

    Returns:
        The same ``state_dict`` object for convenience.
    backboner   Ncross_attn_projectordual_projectorFc              3   >   K   | ]  }|j                  d         yw) backbone.0.cross_attn_projector.N)
startswith).0rN   s     r   	<genexpr>z0remap_projector_to_cross_attn.<locals>.<genexpr>   s     
T#3>><=
Ts   zbackbone.0.projector.z`Cloning %d backbone projector key(s) into cross_attn_projector for dual-projector compatibility.r_   r   )hasattrr[   r   boolanyrV   r`   loggerinfolenrJ   clone)	r   r7   r[   has_cross_attn_projectordual_projector_enabledrN   valueprojector_keysnew_keys	            r   remap_projector_to_cross_attnro      s    %,E:$>u~~a DH  'x1GNVZZ!'(4De"LMiQi!

T
TT3=3C3C3EqZS%XoIpc5jqNq
KKjN %**, ,
U++57Y[\]#kkm
7,  rs   6C;C;
model_argsc                    | j                  di       j                  dd      }|l|j                  d   }t        |dd      }|N||dz   k7  rF|dz   |k  rt        j	                  d|dz
  ||       n"t        j	                  d|dz
  ||dz
  |dz
         | j                  d	      }d}|t        |d
      }|yd}| j                  di       j                  |      }t        |dd      }	|	Gt        |	      dk(  r9|	d   |	d   k(  r.t        |	d         }
t        |d
d      }||
|k7  rt        |
|       d	| vry| d	   }t        |d      }t        |dd      }||||k7  r|rt        d      t        d      t        |d
      }t        |d
d      }||||k7  rt        ||       t        |d      }t        |dd      }|!|||k7  r|rt        d      t        d      yyy)uy  Validate that a checkpoint is compatible with the model configuration.

    Checks for mismatches in ``segmentation_head`` and ``patch_size`` between the checkpoint's saved training arguments
    and the current model configuration. Raises a descriptive :class:`ValueError` before ``load_state_dict`` fires so
    that users receive a clear, actionable message instead of a cryptic tensor size mismatch error.

    If either side is missing an attribute (e.g. a legacy checkpoint saved before
        ``segmentation_head`` or ``patch_size`` was added to ``args``), that specific
        check is skipped silently — this preserves backwards compatibility with pre-existing checkpoints.

    Args:
        checkpoint: Loaded checkpoint dictionary, expected to contain an optional
            ``"args"`` key with training namespace attributes or a plain dict.
        model_args: Namespace (e.g. ``types.SimpleNamespace``) with at least
            ``segmentation_head`` and ``patch_size`` attributes describing the current model.

    Raises:
        ValueError: If ``segmentation_head`` or ``patch_size`` in the checkpoint
            args do not match those of the model, or if the ``patch_size`` inferred from the DINOv2 projection weight
            shape differs from ``model_args.patch_size`` when no explicit ``args.patch_size`` is present.

    Note:
        This helper does not mutate ``model_args``. It emits ``logger.warning`` (not an exception) for class-count
        mismatches so that callers can still proceed with reinitialization or weight loading.

        When ``"args"`` is absent or ``args.patch_size`` is not set, a fallback infers ``patch_size`` from the DINOv2
        patch-embedding projection weight shape (key
        ``backbone.0.encoder.encoder.embeddings.patch_embeddings.projection.weight``). This fallback **can raise**
        :class:`ValueError` on a mismatch, providing a clear error before the cryptic :class:`RuntimeError` from
        :meth:`~torch.nn.Module.load_state_dict` would otherwise fire. For all other attributes (e.g.
        ``segmentation_head``), if either side is missing, that check is skipped silently — preserving backward
        compatibility.

        Two class-count scenarios are distinguished:

        * Backbone pretrain: the checkpoint head was trained with more classes
          than the current ``model_args.num_classes``. In this case the detection head is typically reinitialized or
          trimmed externally to match the configured number of classes.
        * Fine-tuned checkpoint: the checkpoint head was trained with fewer
          classes than the current ``model_args.num_classes``. If you intend to reuse the checkpoint's classification
          head as-is, set ``model_args.num_classes`` to ``ckpt_num_classes - 1`` (the value reported in the warning)
          before loading the state dict to align the configuration and silence the warning.
    r7   zclass_embed.biasNr   num_classesr   zrCheckpoint has %d classes but model is configured for %d. The detection head will be re-initialized to %d classes.zCheckpoint has %d classes but model is configured for %d. Using checkpoint class count (%d). Pass num_classes=%d to suppress this warning.r   
patch_sizezHbackbone.0.encoder.encoder.embeddings.patch_embeddings.projection.weightshape         segmentation_headzThe checkpoint was trained with a segmentation head, but the current model does not have one. Load the weights into a segmentation model (e.g. RFDETRSegNano) instead of a detection model.zThe current model has a segmentation head, but the checkpoint was trained without one. Load the weights into a detection model (e.g. RFDETRNano) instead of a segmentation model.use_grouppose_keypointszThe checkpoint was trained with a keypoint head, but the current model does not have one. Load the weights into a keypoint model (e.g. RFDETRKeypointPreview) instead of a detection model.zThe current model has a keypoint head, but the checkpoint was trained without one. Load the weights into a detection model (e.g. RFDETRNano) instead of a keypoint model.)
r   rt   r   rf   warningr   rh   intr   r   )r2   rp   ckpt_class_biasckpt_num_classesmodel_num_classes
_ckpt_args_ckpt_patch_size_from_args_patch_proj_key_ckpt_proj_w_ckpt_proj_shape_inferred_ps	_model_ps	ckpt_argsckpt_segmentation_headmodel_segmentation_headr   r   ckpt_keypoint_headmodel_keypoint_heads                      r   !validate_checkpoint_compatibilityr      s   Z !nnWb1556H$OO"*003(/
M4(P(-=ARUVAV-V 1$'77O$q(%% D %q(%$q($q($ 'J-1%3J%M"!)d!~~gr266G"<$?'C0@,AQ,FK[\]K^brstbuKu/34L$+Jd$KI$)B*<CZ6"I*8DW*X+2:?RTX+Y 	*#/"&==!p 
 m 
 #1L"IO#*:|T#J"'7'C[kHk"?4DE&4Y@Y&Z'.z;TVZ'[%*=*IN`dwNwt 
 i  Ox*I%r   )N)__doc__rA   rF   collectionsr   typingr   rfdetr.utilities.loggerr   rf   r@   r|   r   strr   r   r1   PathLikerS   rY   objectro   r   r+   r   r   <module>r      s   ) 	  #  .	  s t $) )S )3 )# )(? ? ?D6 r{{3'7!7 6 D 6 rc3h KS4I "&d38n &V &PTUXZ]U]P^ &RF$sCx. Fc FVZ Fr   