
    ^jU                    n
   U d Z ddlmZ ddlZddlZddlZddlmZmZ ddl	m
Z
 ddlmZmZ ddlZddlmZ dd	lmZ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  e       rddlZddl m!Z! ddl"m#Z# ddl$m%Z%  e       rddl&Z&ddl'm(Z( ddl)m*Z+ erddl,m-Z-  e       r
ddl'm.Z.m/Z/m0Z0  ejb                  e2      Z3 G d de      Z4e
d        Z5dYdZ6 edd      d        Z7 eddd      d        Z8 edd      d         Z9 edd!      d"        Z: edd#      d$        Z; edd%      d&        Z< edd'      d(        Z=dZd)Z> edd*d+      d,        Z? edd-d.      d/        Z@ edd0d1      d2        ZA edd3      d4        ZB edd5      d6        ZC edd7      d8        ZD edd9      d:        ZE edd;      d<        ZF edd=      d>        ZG edd?      d@        ZH eddA      dB        ZI eddC      dD        ZJ eKej                  ej                  ej                  ej                  ej                  ej                  h      ZR ed      d[dE       ZS ed      d[dF       ZT ed      d[dG       ZU ed      d[dH       ZV eW       ZX e       reXj                  ej                  j                  j                  j                  ej                  j                  j                  j                  ej                  j                  j                  j                  ej                  j                  j                  j                  ej                  j                  j                  j                  h        ed      d[dI       Zb ed      d[dJ       Zc ed      d[dK       Zd ed      d[dL       Ze ed      d[dM       Zfd\dNZg	 d]	 	 	 	 	 	 	 	 	 d^dOZhd_d`dPZidadbdQZjdadRZkdS ZldT Zmi ZndUeodV<    e       rCenj                  ej                  j                  j                  j                  eiej                  j                  j                  j                  ehej                  j                  j                  j                  ejej                  j                  j                  j                  ejej                  j                  j                  j                  ekej                  j                  j                  j                  emej                  j                  j                  j                  emej                  eli       dcdWZ{e{gZ|dddXZ}y)euX  ONNX exporter.

Extends `DynamoExporter` with five extra stages that convert an `ExportedProgram`
into an ONNX model via `torch.onnx.export`:

1. **Torch patches** (`_PATCHES["onnx"]` via `apply_patches("onnx")`): reversibly
   monkey-patch `torch` ops at tracing time so `torch.export` and `torch.onnx.export`
   emit ONNX-lowerable patterns. Reverted on exit.
2. **ONNX patches** (`_PATCHES["onnx"]` via `apply_patches("onnx")`): reversibly
   hook `torch.onnx` internals — specifically `_prepare_exported_program_for_export`,
   so the FX node fixes (stage 3) run again right after `run_decompositions`.
   Same registry as stage 1, installed by the same `apply_patches` call.
3. **FX node fixes** (`_FX_NODE_FIXES["onnx"]` via `apply_fx_node_fixes("onnx", gm)`):
   per-node in-place rewrites on the `GraphModule` to drop or replace nodes ONNX
   can't lower (alias, in-place ops, dead comparisons, `_assert_*`, …). Triggered
   both directly after `torch.export` and indirectly via the stage 2 hook.
4. **ONNX translations** (`_ONNX_TRANSLATION_TABLE`): custom onnxscript functions
   passed as `custom_translation_table` that override the default torchlib
   lowering for specific aten ops where it's buggy or missing.
5. **ONNX IR fixes** (`_IR_FIXES` via `apply_onnx_ir_fixes`): post-export in-place
   fixes on the `ONNXProgram` IR for ORT compatibility.
    )annotationsN)MutableMappingSequence)contextmanager)TYPE_CHECKINGAny   )logging)is_onnxscript_availableis_torch_available   )
OnnxConfig)DynamoExporter)apply_fx_node_fixesapply_patchesduplicate_leaf_tensorsget_leaf_tensorsregister_fx_node_fixregister_patch)ExportedProgram)ONNXProgram)masking_utils)aten_index_put)opset18)PreTrainedModel)BOOLINT64TRealc                  H     e Zd ZdZg dZddddZ	 	 	 	 	 	 	 	 d fdZ xZS )OnnxExportera  Exporter that converts a [`PreTrainedModel`] to an ONNX `ONNXProgram`.

    Example:

    ```python
    >>> from transformers.exporters.exporter_onnx import OnnxExporter, OnnxConfig

    >>> exporter = OnnxExporter()
    >>> onnx_program = exporter.export(model, inputs, config=OnnxConfig(dynamic=True))
    >>> outputs = onnx_program(**inputs)  # run in-memory
    >>> exporter.export(model, inputs, config=OnnxConfig(output_path="model.onnx"))  # save to disk
    ```
    )torchonnx
onnxscriptz2.12.0z1.21.0z0.7.0c                   t        |t              rt        di |}n(t        |      t        urt	        dt        |             t        |      5 \  }}t        d      5  t        | !  |||      }t        ||      \  }}t        d|j                         t        j                  j                  |d|j                  ||t        j                   t        |            t"        |j$                  |j&                  |j(                  |j*                        }d d d        d d d        t-               |S # 1 sw Y   xY w# 1 sw Y   "xY w)Nz1Expected config to be an OnnxConfig or dict, got r"   )config )
argsfinput_namesoutput_nameskwargscustom_translation_tableopset_versionexternal_dataexport_paramsoptimize)
isinstancedictr   type	TypeErrorpatch_model_outputsr   superexportdisambiguate_io_namesr   graph_moduler!   r"   output_pathcopydeepcopy_ONNX_TRANSLATION_TABLEr-   r.   r/   r0   apply_onnx_ir_fixes)	selfmodelsample_inputsr%   inputs_namesoutputs_namesexported_programonnx_program	__class__s	           o/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/transformers/exporters/exporter_onnx.pyr7   zOnnxExporter.exporti   s/    fd#)&)F&\+OPTU[P\~^__ ' 	+HL--X^J_ 	05um\b0c*?m*\'L-(8(E(EF(-

(9(9 $$(*}}T-%89)@$22$22$22 ): )L		 	$ 	L)'	 	 	 	s%   D= B4D1D=1D:	6D==E)r@   r   rA   zMutableMapping[str, Any]r%   zOnnxConfig | dict[str, Any]returnr   )__name__
__module____qualname____doc__required_packagestested_versionsr7   __classcell__)rF   s   @rG   r    r    W   sL     8 (('RO 0 ,	
 
     r    c              #     K   g g | j                   t        j                        fd       }	 || _         f | _         y# | _         w xY ww)zWrap `model.forward` to return a flat `dict[str, Tensor]` with duplicated outputs,
    and capture the input/output tensor names from the traced forward in the yielded
    `(inputs_names, outputs_names)` lists.
    c            	         t        t         | i |            }j                  t        |      j                                j                  |j                                |S N)r   r   extendkeys)r'   r+   outputsrB   original_forwardrC   s      rG   patched_forwardz,patch_model_outputs.<locals>.patched_forward   sU    "#9:JD:[TZ:[#\],V499;<W\\^,rP   N)forward	functoolswraps)r@   rX   rB   rW   rC   s     @@@rG   r5   r5      s_      !L!M}}__%& ')'M))((s   0AA A	AAc                    t        |       j                  t        |            D ]0  }d| | | j                  |      <   d| ||j                  |      <   2 | |fS )zEPrefix any name that appears in both lists with `input.` / `output.`.zinput.zoutput.)setintersectionindex)rB   rC   names      rG   r8   r8      sl    L!..s=/AB D39$\''-.5<TF3Cm))$/0D &&rP   r"   ztorch.wherec                     d fd	}|S )z,Normalize dtypes and scalars in torch.where.c                   t        |t        j                        rOt        |t        j                        r5|j                  |j                  k7  r|j	                  |j                        }nt        |t        j                        rHt        |t
        t        t        f      r-t        j                  ||j                  |j                        }nat        |t        j                        rGt        |t
        t        t        f      r,t        j                  ||j                  |j                        }|
| |       S |	 | |      S  | ||      S )Ndtypedevice)
r1   r!   Tensorrd   tointfloatbooltensorre   )	conditionxyoriginals      rG   patchz_patch_where.<locals>.patch   s    a&:a+F177VWV]V]K]QWWA5<<(ZC;M-NQaggahh?A5<<(ZC;M-NQaggahh?A9I&&YIq))Iq!,,rP   NNr&   ro   rp   s   ` rG   _patch_wherers      s    - LrP   ztorch.unsqueezeztorch.Tensor.unsqueezec                      fd}|S )z+Support complex tensors in torch.unsqueeze.c                    t        j                  |       r< | j                  |      } | j                  |      }t        j                  ||      S  | |      S rS   )r!   
is_complexrealimagcomplex)self_or_inputdimrw   rx   ro   s       rG   rp   z_patch_unsqueeze.<locals>.patch   sS    M*M..4DM..4D==t,,s++rP   r&   rr   s   ` rG   _patch_unsqueezer|      s    , LrP   z/transformers.masking_utils._vmap_expansion_sdpac                    d }|S )z;Replace vmap-based mask expansion with broadcast expansion.c                      fd}|S )Nc                    t        j                  | |||      } | j                  | j                  d   |j                  d   |j                  d   |j                  d         }|S Nr   )r   _non_vmap_expansion_sdpaexpandshape)batch_arangehead_arangeq_arange	kv_arange
brodcastedresultmask_functions         rG   	_expandedzA_patch_broadcast_mask_expansion.<locals>.patch.<locals>._expanded   sl    &??k[cenoJ"J/66""1%{'8'8';X^^A=NPYP_P_`aPbF MrP   r&   )r   r   s   ` rG   rp   z._patch_broadcast_mask_expansion.<locals>.patch   s    	 rP   r&   )	_originalrp   s     rG   _patch_broadcast_mask_expansionr      s     LrP   ztorch.nn.RMSNorm.forwardc                      fd}|S )zAUse non-fused RMS normalization when elementwise_affine is False.c                (   | j                   s}|j                  t        j                        j	                  d      j                  dd      }|t        j                  || j                  z         z  j                  |j                        S  | |      S )Nr	   T)keepdim)	elementwise_affinerg   r!   float32powmeanrsqrtepsrd   )r?   rm   variancero   s      rG   rp   z&_patch_rms_norm_forward.<locals>.patch   sr    &&ttEMM*..q166r46HHHtxx$788<<QWWEEa  rP   r&   rr   s   ` rG   _patch_rms_norm_forwardr      s    ! LrP   ztorch.randpermc                R    t         j                  t         j                  ddddd}|S )uT   Implement randperm via argsort(rand(n)) — no ONNX decomposition for aten.randperm.NF)rd   layoutre   
pin_memory	generatorc               t    t        j                  t        j                  | |            j                  |      S )Nre   )r!   argsortrandrg   )nrd   r   re   r   r   s         rG   rp   z_patch_randperm.<locals>.patch   s'    }}UZZ&9:==eDDrP   )r!   int64stridedrr   s     rG   _patch_randpermr      s&      ++emmDUZfj E LrP   ztorch.histcc                    dddd}|S )u~  Replace `torch.histc` with a statically-shaped, deterministic equivalent.

    The default torchlib `aten_histc` translation rejects integer input (`torch.histc only
    works on float`), and the obvious workaround — casting to float — calls `_histc_cuda`
    which has no deterministic implementation on CUDA. `bincount`'s output is an unbacked
    SymInt under torch.export and trips downstream meta-shape guards (e.g. grouped_mm's
    `offs` size check). Pre-allocating `torch.zeros(bins)` + `scatter_add_` keeps the output
    shape pinned to `bins` (a Python int), and `scatter_add_` is deterministic on integer
    indices.
    N)outc                  | j                  d      }||cxk(  rdk(  r@n n=|j                         j                         }|j                         j                         }nTt	        j
                  t        |      |j                        }t	        j
                  t        |      |j                        }||z
  |z  }|j                         |z
  |z  j                         j                  d|dz
        }	| j                         r| j                  nt        j                  }
t	        j                  ||
| j                        }|j                  d|	t	        j                  |	|
            S )Nr   r   r   r   rc   rd   )reshapeminri   maxr!   rk   re   longclamp_is_floating_pointrd   zerosscatter_add_	ones_like)inputbinsr   r   r   flatmin_valmax_val	bin_widthidx	out_dtypecountss               rG   rp   z_patch_histc.<locals>.patch  s   }}R #??hhj&&(Ghhj&&(Gll5:dkkBGll5:dkkBGw&$.	

w&)399;BB1dQhO#(#:#:#<EKK%++	T5<<H""1c5??3i+PQQrP   )d   r   r   r&   rr   s     rG   _patch_histcr     s    RD R LrP   z4onnxscript.onnx_opset._impl.opset13.Opset13.Constantc                      fd}|S )u	  Substitute `op.Constant(value_ints=[])` with an explicit empty INT64 tensor.

    Upstream onnxscript's `aten_index_put` does `op.Constant(value_ints=none_indices)`
    where `none_indices` can be empty (when every input dim has an advanced index).
    `onnx_ir` then logs an ambiguous-type warning because an empty Python list has no
    derivable element type. Swap the empty-`value_ints` call for `value=ir.tensor([], INT64)`
    — semantically identical, no ambiguity. Drop once onnxscript fixes the call site.
    c                    |j                  d      g k(  rL|j                  d       t        j                  t	        j
                  g t        j                              |d<    | g|i |S )N
value_intsr   value)getpoponnx_irrk   nparrayr   )r?   r'   r+   ro   s      rG   rp   z&_patch_opset13_constant.<locals>.patch+  sV    ::l#r)JJ|$%nnRXXb-IJF7O.t.v..rP   r&   rr   s   ` rG   _patch_opset13_constantr      s    / LrP   c                   fd}|S )zFDecompose cummax/cummin via triangular-mask reduction (O(N^2) memory).c                6   | j                   |   }| j                  |d      } |j                  d      j                  g |j                   d d || }t	        j
                  ||t        j                  | j                        j                         }| j                  t        j                  k(  r
dk7  }n| j                         rX
dk(  r)t	        j                  | j                        j                  n(t	        j                  | j                        j                  }nW
dk(  r)t	        j                  | j                        j                  n(t	        j                  | j                        j                  }t	        j                  d|| j                  | j                        }t	        j                   |||      }
dk(  r|j                  d      n|j                  d      }	|	j"                  j                  d|      |	j$                  j                  d|      fS )Nr   rc   r   r&   r{   )r   movedim	unsqueezer   r!   onesrj   re   trilrd   r   finfor   r   iinfofullwherevaluesindices)r   r{   r   rm   x_gridincludefill_valfillmaskedr   modes             rG   rp   z&_patch_cummax_or_cummin.<locals>.patch7  s   KKMM#r"'R''<"<q<!<**QELLINNP;;%**$u}H$$&7;u}u{{5;;/33%++V[VaVaJbJfJfH7;u}u{{5;;/33%++V[VaVaJbJfJfHzz"hekk%,,OWfd3$(EMfjjRj vzzbz7Izz!!"c*CKK,?,?C,HHHrP   r&   )ro   r   rp   s    ` rG   _patch_cummax_or_cumminr   4  s    I  LrP   ztorch.cummaxztorch.Tensor.cummaxc                    t        | d      S )Nr   r   r   ro   s    rG   _patch_cummaxr   J      "8%88rP   ztorch.cumminztorch.Tensor.cumminc                    t        | d      S )Nr   r   r   r   s    rG   _patch_cumminr   O  r   rP   z	torch.expztorch.Tensor.expc                      fd}|S )u   Lower `exp` on complex tensors via Euler — onnxscript has no dispatch for `aten.exp` on
    complex inputs. Real inputs hit the original path.c                    t        j                  |       r^ | j                        }t        j                  || j                  j                         z  || j                  j                         z        S  |       S rS   )r!   rv   rw   ry   rx   cossin)r   	magnitudero   s     rG   rp   z_patch_exp.<locals>.patchY  sZ    E" ,I==UZZ^^-=!=y5::>>K[?[\\rP   r&   rr   s   ` rG   
_patch_expr   T  s    
 LrP   ztorch.fft.irfftc                    dd}|S )uY  Replace `irfft` with `ifft` over the conjugate-mirrored input — ORT's `DFT` op rejects the
    `is_onesided=1`/`inverse=1` combination that torch's `irfft` lowers to. Mirroring restores the
    full spectrum so the inverse path uses two-sided DFT, which ORT accepts. Assumes even `n`
    (which is the common case for STFT-based audio codecs).c                b   |d| j                   |   dz
  z  }t        d       g| j                  z  }t        dd      ||<   t        j                  | | t        |         j                  |g      j                         g|      }t        j                  j                  ||||      j                  S )Nr	   r   r   )dimsr   )r   r{   norm)r   slicendimr!   cattupleflipconjfftifftrw   )r   r   r{   r   slcr   s         rG   rp   z_patch_irfft.<locals>.patchi  s    9U[[%)*AT{mejj(B<Cyy%uSz!2!7!7cU!7!C!H!H!JKQTUyy~~daSt~<AAArP   )Nr   Nr&   rr   s     rG   _patch_irfftr   b  s    B LrP   ztorch.bucketizec                    dddd}|S )zUVectorized bucketize avoiding scalar-constant tensors that cause alias/detach issues.F)	out_int32rightc               v   |j                         dk(  rHt        j                  | t        j                        }|r|j	                  t        j
                        S |S |r|| j                  d      k  }n|| j                  d      k  }|j                  d      }|r|j	                  t        j
                        S |S )Nr   r   r   )numelr!   
zeros_liker   rg   int32r   sum)r   
boundariesr   r   r   masks         rG   rp   z_patch_bucketize.<locals>.patchx  s    "%%e5;;?F-6699U[[)BFB!44D 33D")2vyy%>>rP   r&   rr   s     rG   _patch_bucketizer  t  s     /45 	? LrP   ztorch.searchsortedc                    ddddddd}|S )uG  Decompose searchsorted via broadcast comparison + sum — no ONNX op for searchsorted.

    For sorted inputs the insertion index equals the count of elements satisfying
    the comparison (< for left, <= for right). This is O(N*M) instead of the
    real binary-search O(M log N) but only uses ops with ONNX translations.
    FN)r   r   sider   sorterc               
   ||dk(  }|r$| j                  d      |j                  d      k  }n#| j                  d      |j                  d      k  }|j                  d      }|r|j                  t        j                        S |S )Nr   r   r   )r   r   rg   r!   r   )	sorted_sequencer   r   r   r  r   r  r  r   s	            rG   rp   z"_patch_searchsorted.<locals>.patch  s{    GOE",,R0F4D4DR4HHD",,R063C3CB3GGD")2vyy%>>rP   r&   rr   s     rG   _patch_searchsortedr	    s     5:TW[dh ? LrP   z
torch.fullc                     dd fd
}|S )z`Force dtype=torch.long when fill_value is int and no dtype specified (ONNX defaults to float32).Nr   c                    | E|j                  dt        |      dkD  r|d   nd       }t        |t              rt        j
                  }  |d| i|S )N
fill_valuer   rd   )r   lenr1   rh   r!   r   )rd   r'   r+   r  ro   s       rG   rp   z_patch_full.<locals>.patch  sQ    =LSY]$q'PTUJ*c*

5U5f55rP   r&   rr   s   ` rG   _patch_fullr    s       6 LrP   ztorch.masked.meanc                    ddddd}|S )zCManual masked mean: avoids sum/int_count Div type mismatch in ONNX.NF)r{   r   rd   c                   |j                         }|j                  |d      j                  d      }| |z  j                  ||      |r|n|j                         z  }||j	                  |      S |S )NTr{   r         ?r   )ri   r   clampsqueezerg   )r   r  r{   r   rd   
mask_floatr   r   s           rG   rp   z!_patch_masked_mean.<locals>.patch  ss    ZZ\
NNsDN177C7@*$))c7)CGqYZYbYbYde#(#4vyy@&@rP   r&   rr   s     rG   _patch_masked_meanr    s     #'T A LrP   ztorch.masked.varc                    ddddd}|S )zBManual masked var: avoids sum/int_count Div type mismatch in ONNX.NFT)r{   r   unbiasedc               J   |j                         }|j                  |d      j                  d      }| |z  j                  |d      |z  }| |z
  j                  d      |z  j                  ||      }|r|dz
  n|}	|s|	j	                         }	||	j                  d      z  S )NTr  r  r  r	   )ri   r   r  r   r  )
r   r  r{   r   r  r  r   r   vardenoms
             rG   rp   z _patch_masked_var.<locals>.patch  s    ZZ\
NNsDN177C7@
"''C'>B!!!$z16636P%S1MMOEU[[S[)))rP   r&   rr   s     rG   _patch_masked_varr    s     #' * LrP   ztorch.Tensor.masked_scatterc                    d }|S )zPCumsum-gather-where strategy for masked_scatter (avoids ScatterND ORT failures).c                n   |j                  |       }|j                  d      }|j                  t        j                        j                  d      dz
  j                  d      }|j                  d      |   }t        j                  ||| j                  d            j                  | j                        S )Nr   r   r   r  )		expand_asr   rg   r!   r   cumsumr  r   r   )r?   r  source	flat_mask	positionsgathereds         rG   rp   z$_patch_masked_scatter.<locals>.patch  s    ~~d#LL$	\\%++.55a81<CCCJ	>>"%i0{{9hR0@AII$**UUrP   r&   rr   s     rG   _patch_masked_scatterr&    s    V LrP   z
torch.rollc                     d fd	}|S )a  Replace `torch.roll(input, shifts, dims)` with explicit `narrow + cat` shifts.

    `torch.roll`'s torch.export lowering emits a `Shape(start, end)` op that can resolve to an
    empty INT64 result; the downstream `Slice` then has mismatched `axes` and `ends` lengths
    and ORT rejects the graph with `ShapeInferenceError` (seen in Gemma4-Unified Vision2Text,
    where roll is composed with an in-place scatter `[..., 0] = value`). The explicit form is
    bit-exact and traces to plain Slice + Concat nodes.
    c                   t        |t              rt        |t              r|f}|f}nMt        |t        t        f      r-t        |t        t        f      rt	        |      t	        |      k(  s
 	| ||      S | }t        ||      D ]m  \  }}|j                  |      }|dkD  r||z  nd}|dk(  r)|j                  |||z
  |      }|j                  |d||z
        }t        j                  ||g|      }o |S )Nr   r   )
r1   rh   r   listr  zipsizenarrowr!   r   )
r   shiftsr   r   shiftr{   lengthfrontbackro   s
            rG   rp   z_patch_roll.<locals>.patch  s    fc"z$'<YF7DVeT]3
4%QU8W\_`f\gknoskt\tE6400fd+ 	4JE3XXc]F&,qjEFNaEzJJsFUNE:E::c1fun5D))UDMs3C	4 
rP   rS   r&   rr   s   ` rG   _patch_rollr2    s    $ LrP   zHtorch.onnx._internal.exporter._core._prepare_exported_program_for_exportc                      fd}|S )a  Run the FX node fixes immediately after the ONNX internal decomposition step.

    `torch.onnx.export` internally calls `run_decompositions` with the ONNX
    decomposition table, which can introduce new symbolic-guard nodes (e.g.
    `operator.le(sym_size, int_oo)`). These overflow during ONNX translation.
    Wrapping the prepare step lets us apply our FX fixes immediately after.

    <Tip warning={true}>

    This hooks `torch.onnx._internal.exporter._core._prepare_exported_program_for_export`,
    a private PyTorch API. It may break on PyTorch version upgrades. If it does,
    find the new entry point in `torch/onnx/_internal/exporter/_core.py`
    where `ExportedProgram.run_decompositions` is called and hook there instead.

    </Tip>
    c               H     | |      }t        d|j                         |S )N)registryr"   )r   r9   )epr5  r   ro   s      rG   rp   z(_patch_prepare_for_export.<locals>.patch  s$    "x0FF$7$78rP   r&   rr   s   ` rG   _patch_prepare_for_exportr7    s    &
 LrP   c                v   |j                   t        vryt        |j                        dk(  r| j                  j                  |       yt        d |j                  D              rG	  |j                   |j                   }|j                  |       | j                  j                  |       yy# t        $ r Y yw xY w)u.  Erase or constant-fold comparison nodes involving symbolic infinities.

    torch.export emits guards like ``%le_3 = operator.le(sym_size, int_oo)`` where
    ``int_oo`` is a sympy ``IntInfinity`` object.  The ONNX translator tries to lower it
    to a C long and overflows.  Two cases handled:

    * No users → erase the node outright (PyTorch DCE skips Python callables).
    * Any arg is a non-FX-Node constant (e.g. ``int_oo``) → evaluate the comparison at
      graph-construction time, replace all uses with the Python bool result, and erase.
    Fr   Tc              3  f   K   | ])  }t        |t        j                  j                          + y wrS   )r1   r!   fxNode).0as     rG   	<genexpr>z'_fix_dead_comparison.<locals>.<genexpr>6  s"     
?z!UXX]]++
?s   /1)
target_COMPARISON_OPSr  usersgraph
erase_nodeanyr'   	Exceptionreplace_all_uses_with)gmnoder   s      rG   _fix_dead_comparisonrI  $  s     {{/)
4::!
D!

?TYY
??	 T[[$)),F 	""6*
D!  		s   %B, ,	B87B8c                    |j                   t        j                  j                  j                  j
                  ury|j                  |j                  d          | j                  j                  |       y)zHReplace alias(x) -> x to break the alias -> detach_ -> index_put_ chain.Fr   T)
r?  r!   opsatenaliasdefaultrF  r'   rB  rC  rG  rH  s     rG   
_fix_aliasrP  A  sR     {{%))....666tyy|,HHrP   c                   |j                   t        j                  j                  j                  j
                  ury| j                  j                  |      5  | j                  j                  t        j                  j                  j                  j
                  |j                  |j                        }ddd       |j                         | j                  j                  |       y# 1 sw Y   6xY w)z2Replace in-place detach_ with out-of-place detach.Fr'   r+   NT)r?  r!   rK  rL  detach_rN  rB  inserting_beforecall_functiondetachr'   r+   rF  rC  rG  rH  news      rG   _fix_detach_inplacerY  K  s     {{%))..00888		"	"4	( hhh$$UYY^^%:%:%B%B[_[f[f$ghs#HH	h h   AC++C4c                   |j                   t        j                  j                  j                  j
                  ury| j                  j                  |      5  | j                  j                  t        j                  j                  j                  j
                  |j                  |j                        }ddd       |j                         | j                  j                  |       y# 1 sw Y   6xY w)z8Replace in-place index_put_ with out-of-place index_put.FrR  NT)r?  r!   rK  rL  
index_put_rN  rB  rT  rU  	index_putr'   r+   rF  rC  rW  s      rG   _fix_index_put_inplacer^  W  s     {{%))..33;;;		"	"4	( khh$$UYY^^%=%=%E%EDII^b^i^i$jks#HH	k krZ  c                `    |j                   t        vry| j                  j                  |       y)zFErase assertion / shape-constraint nodes that have no ONNX equivalent.FT)r?  _ASSERTION_OPSrB  rC  rO  s     rG   _fix_assertionra  p  s(     {{.(HHrP   c                   |j                   t        j                  j                  j                  j
                  ury| j                  j                  |      5  |j                  d   }|j                  d   }| j                  j                  t        j                  j                  j                  j                  |df      }| j                  j                  t        j                  j                  j                  j                  |df      }| j                  j                  t        j                  j                  j                  j
                  ||f      }| j                  j                  t        j                  j                  j                  j                  |t        j                  f      }| j                  j                  t        j                  j                  j                   j
                  ||f      }| j                  j                  t        j                  j                  j"                  j$                  |||f      }	ddd       |j'                  	       | j                  j)                  |       y# 1 sw Y   6xY w)z=Replace in-place fill_diagonal_ with out-of-place equivalent.Fr   r   r'   NT)r?  r!   rK  rL  fill_diagonal_rN  rB  rT  r'   rU  sym_sizerh   eyerg   rd   rj   	full_liker   r?   rF  rC  )
rG  rH  
tensor_argr  rowscolsrf  eye_boolfill_tensorrX  s
             rG   _fix_fill_diagonal_inplacerm  y  s    {{%))..77???		"	"4	( 	jYYq\
YYq\
xx%%eiinn&=&=&A&AUV%Xxx%%eiinn&=&=&A&AUV%Xhh$$UYY^^%7%7%?%?tTl$S88))%))..*;*;*A*AejjHY)Zhh,,UYY^^-E-E-M-MU_akTl,mhh$$UYY^^%9%9%>%>hP[]gEh$i	j 	s#HH	j 	js   G0I<<Jc                   |j                   t        j                  j                  j                  j
                  ury| j                  j                  |      5  | j                  j                  t        j                  j                  j                  j
                  |j                  |j                        }ddd       |j                         | j                  j                  |       y# 1 sw Y   6xY w)z.Replace in-place triu_ with out-of-place triu.FrR  NT)r?  r!   rK  rL  triu_rN  rB  rT  rU  triur'   r+   rF  rC  rW  s      rG   _fix_triu_inplacerq    s     {{%))....666		"	"4	( fhh$$UYY^^%8%8%@%@tyyY]YdYd$efs#HH	f frZ  c                   |j                   t        j                  j                  j                  j
                  ury|j                  d   }t        |j                        dkD  r|j                  d   nd}t        |j                        dkD  r|j                  d   nd}| j                  j                  |      5  | j                  j                  t        j                  j                  j                  j                  |||f      }ddd       |j                         | j                  j                  |       y# 1 sw Y   6xY w)	zMReplace aten.sort.stable with aten.sort.default (which has ONNX translation).Fr   r	   r      rc  NT)r?  r!   rK  rL  sortstabler'   r  rB  rT  rU  rN  rF  rC  )rG  rH  self_argr{   
descendingrX  s         rG   _fix_sort_stablerx    s     {{%))..--444yy|Hdii.1,$))A,"C!$TYY!!31J		"	"4	( dhh$$UYY^^%8%8%@%@RUWaGb$cds#HH	d ds   8AD::Ec                l   |j                   t        j                  j                  j                  j
                  uryt        |j                        dk  s1t        |j                  d   t        j                  j                        sy| j                  j                  |      5  | j                  j                  t        j                  j                  j                  j                  |j                        }ddd       |j                         | j                  j!                  |       y# 1 sw Y   6xY w)a  Rewrite remainder.Scalar to remainder.Tensor when the 'scalar' arg is actually a tensor.

    After decomposition the second operand of ``aten.remainder.Scalar`` can be a graph
    node (SymbolicTensor) rather than a Python scalar.  The ONNX torchlib translation for
    ``remainder.Scalar`` calls ``int()`` on it and crashes.  Rewriting to
    ``remainder.Tensor`` uses the two-tensor ONNX translation which handles this correctly.
    Fr	   r   rc  NT)r?  r!   rK  rL  	remainderScalarr  r'   r1   r:  r;  rB  rT  rU  rf   rF  rC  rW  s      rG   _fix_remainder_scalarr|    s     {{%))..22999
499~DIIaL%((--!H		"	"4	( Vhh$$UYY^^%=%=%D%D499$UVs#HH	V Vs   !AD**D3c                   | j                   7|j                   +t        | j                         t        |j                         kD  ryt        |j                         t        | j                         z
  }t        | j                   |j                   |d       D ])  \  }}	 t        |      t        |      }}|dk7  s#||k7  s) y y# t        t
        f$ r Y  yw xY w)u  Static-shape check: does ``values.shape`` broadcast against ``self.shape``?

    Returns ``True`` only when every dim of ``values`` is statically known and either
    equals the corresponding (right-aligned) dim of ``self`` or is ``1``. Used to dispatch
    `_aten_index_put` between the broadcast and flat-gather paths — bailing on dynamic /
    unknown dims keeps us on the safe flat-gather fallback.
    NFr   T)r   r  r*  rh   r4   
ValueError)r   r?   offsetv_dims_dims        rG   _values_broadcast_to_selfr    s     ||tzz1S5FTZZ5X_s6<<00FFLL$**VW*=> u	u:s5z5E A:%5. 	 :& 		s   C  CCc                *   |d   }|duxr3 t        t        |dd      dd      t        j                  j                  k(  }|st	        | |||      S t        t        | j                        t        |j                        z
        D ]-  }t        j                  |t        j                  dg            }/ t        j                  |t        j                  |             }t        ||       r@t        j                  |t        j                  |             }t        j                  |||       S t        j                  |t        j                  dg            }	t        j                   |	d      }
t        j"                  |
t        j                  dg            }t        j$                  t        j&                  |t        j                  d	g            t        j                  dg            }t        j                  |t        j                  dg            }t        j(                  ||      }t        j                  | t        j                  dg            }t        j                  |	||      }t        j                  |t        j                  |             S )
u  Bool-mask index_put with two paths; delegates non-bool-mask cases to torchlib.

    For `self[bool_mask] = values`, PyTorch supports two distinct shapes for ``values``:
    1. Broadcasts against ``self.shape`` (e.g. scalar `tensor[~mask] = 0`) — handled by
       `Expand(values, Shape(self)) + Where(mask, expanded, self)`.
    2. Equals ``bool_mask.sum()`` along its first dim, with remaining dims matching
       ``self`` (e.g. `inputs_embeds[image_mask] = image_features_flat`) — handled by
       the flat cumulative-count-Gather + Where trick.

    Path 1 is correct only when broadcast-compatibility can be statically verified — for
    dynamic shapes we fall through to path 2, which is also torchlib's default behaviour.
    r   Nr3   rd   r   r      rg   r   )getattrr   DataTyper   r   ranger  r   op	UnsqueezeConstantExpandShaper  WhereReshapeCastCumSumClipSubGather)r?   r   r   
accumulate	bool_maskis_bool_expanded_maskexpanded_valuesr#  flat_mask_intcsr$  flat_valuesr%  	flat_selfr   s                    rG   _aten_index_putr    s   $ 
Is'')VT*JGUY"Z^e^n^n^s^s"s  dGVZ@@3tzz?S%99: JLLBKKB4,HI	JIIi$8M .))FBHHTN;xx==

="++"*FGIGGI!,M	="++!"=	>Br2;;1##>?YZX[A\]I**VR[[RD%ABKyyi0H

4!=>IXXi95F::fbhhtn--rP   c                   t        j                  dg      }t        j                  t        j                  | d      t        j                  dg            }t        j                  ||      }|dkD  r+t        j
                  |t        j                  |g            }t        j                  | |t        j                  ddg      d      }t        j                  |t        j                  dg      d      S )a  ONNX implementation of `torch.bincount`: count occurrences of non-negative ints.

    No native ONNX op. We use `OneHot(self, depth=max+1, values=[0,1])` then `ReduceSum`
    along the input axis. Weights are unused (splinter's only caller passes none).
    r   r  r   )keepdimsr   axis)r  r  r  	ReduceMaxAddMaxOneHot	ReduceSum)r?   weights	minlengthoner   depthone_hots          rG   _aten_bincountr     s     ++!
%Cll2<<q92;;STRU;VWGFF7C E1}ubkki[ABiieR[[QF%C"MG<<!<qIIrP   c           	        |j                   d   }t        |t              st        d      t	        j
                  |d      }t	        j                  dg      }t	        j                  dg      }g }	|}
t        |      D ]  }t	        j                  |g      }t	        j                  |dz   g      }t	        j                  ||||      }t	        j                  | |
||      }t	        j                  t	        j                  ||||      |      }|	j                  t	        j                  ||             |}
 t	        j                  |	ddiS )uZ  ONNX implementation of `aten._grouped_mm.default`.

    `_grouped_mm(mat_a: (M, K), mat_b: (G, K, N), offs: (G,))` computes `out[r] =
    mat_a[r] @ mat_b[group(r)]` where rows are sorted by group and `offs` holds the
    cumulative end index per group.

    Per-group `Slice + MatMul + Concat`. `G` (number of experts) is static for any
    concrete model, so unroll at translation time: emit one `Slice + MatMul` triple
    per group and a final `Concat`. Avoids the `(M, K, N)` materialisation a naive
    `weight[group_idx]` gather would emit — peak memory is `O(M·N + max(n_g)·K + K·N)`.
    r   zW_aten_grouped_mm: number of experts (mat_b.shape[0]) must be static at translation timer  r  r  r   r  )r   r1   rh   r~  r  r  r  r  SliceSqueezeappendMatMulConcat)mat_amat_boffsbiasr   Goffs_i64axes_0zero_1drV   prev_endgg_log_hienda_gw_gs                    rG   _aten_grouped_mmr    s    	AAarsswwt"H[[QC(FkkaS)GGH1X {{qc*{{q1ug.hhxtV4hhuhV4jj%tV<fEryyc*+ 99g&A&&rP   c           	        |+t        j                  | t        j                  dg            }t        j                  |t        j                  dg            }t	        |t
              rt        j                  d|g      }nmt        j                  t        j                  |d      t        j                  dg            }t        j                  t        j                  dg      |d      }t        j                  t        j                  ||      t        j                  dg            S t        | j                        }||z   |z  }	t        j                  | t        j                  |	dz   g            }t	        |t
              r(dg|dz   z  }
||
|	dz   <   t        j                  |
      }nt        j                  t        j                  |d      t        j                  dg            }t        j                  t        j                  dg|	dz   z        |t        j                  dg||	z
  dz
  z        d      }t        j                  ||      }t        j                  t        j                  | d|	      t        j                  dg      t        j                  | |	dz   	      d      }t        j                  ||      S )
a  ONNX implementation of `aten.repeat_interleave.self_int`.

    Torchlib's translation raises on `dim is None` and broadcasts incorrectly for the
    1-D + symbolic-repeats case (its tile shape is `[r, 1]` instead of `[1, r]`). We
    always rewrite: flatten when `dim is None`, then `Unsqueeze + Tile + Reshape` along
    the chosen axis. Handles both Python-int and 0-D-tensor `repeats`.
    r   r  r   r  r  r   r  )startr  )r  )r  r  r  r  r1   rh   r  r  Tiler  r   r  )r?   repeatsr{   output_sizer   unsq
tile_shaper	self_rankpos_dimtilestiledfinal_shapes                rG    _aten_repeat_interleave_self_intr  1  s	    {zz$t <=||D"++!"=>gs#G=J

2777q12;;2$3OPA2;;1##>JJzz"''$
3R[[RD5QRRDJJIY)+G<<bkkgk]CDD'3y1}%$gk[[E2
JJrwww1-r{{rd/KLYYKKA3'A+#67KKA3)g*=*A#BC	

 GGD*%E))
QG,
t$
Wq[)	K ::e[))rP   c           
     L   t        j                  t        j                  t        j                  t        j                  |       t        j                  |                  t        j
                  t        j                  | |      t        j                  j                  j                              }t        | d      r| j                  j                  n|j                  j                  }t        j                  t        j                  | |      t        j
                  ||            S )u2  Correct floor division (toward -inf) for signed integer SymInts.

    Torchlib's `operator_floordiv` translation only handles positive operands (plain `Div`,
    which truncates). For signed ints — including SymInt shape arithmetic — apply the
    offset correction `floor(a/b) = trunc(a/b) - (sign(a) != sign(b) AND a mod b != 0)`,
    matching `aten_floor_divide`. Without this, the Python ceil-div idiom `-(-x // y)`
    produces `1 + trunc((y - 1 - x) / y)` instead of `ceil(x / y)`, which silently breaks
    any shape arithmetic that crosses zero.
    r  rd   )r  AndNotEqualSignr  Modr   r  r   r   hasattrrd   r  Div)r?   otherr  rd   s       rG   _operator_floordivr  \  s     VV
rxxrwwu~67
tU#(8(8(=(=(C(CDF !(g 6DJJEKK<M<ME66"&&u%rwwv%'@AArP   c                   | j                   t        j                  j                  k(  rt	        |t
        t        t        f      rt        |      n"t        |j                  j                               }|rt        j                  | |      S t        j                  | t        j                  |            S t        j                  ||       }t        j                  |||       S )u  ONNX implementation of `aten.masked_fill.{Scalar,Tensor}`.

    Upstream torchlib lowers this to `Where(mask, value, self)`. ORT's CPU EP has no
    `Where(16)` kernel for BOOL inputs, so when `self` is BOOL we rewrite the op using
    boolean primitives: `masked_fill(self, mask, True)` → `self | mask`,
    `masked_fill(self, mask, False)` → `self & ~mask`. Non-bool `self` keeps the default.
    )rd   r   r  r   r1   rj   rh   ri   const_valuenumpyr  Orr  r  CastLiker  )r?   r  r   	fill_true
value_casts        rG   _aten_masked_fillr  n  s     zzW%%***#-edC5G#HDKdSXSdSdSjSjSlNm	55t$$vvdBFF4L))UD)J88D*d++rP   zdict[Any, Any]r=   c                    t        | j                               D ]N  }|j                  dk(  st        j                  dt        j
                  j                  d      |j                  d<   P y)zASet sorted=1 on TopK nodes (ORT CUDA EP rejects TopK without it).TopKsortedr   N)r)  	all_nodesop_typer   AttrAttributeTypeINT
attributes)
graph_likeir_nodes     rG   _fix_ir_topk_sortedr    sW    
,,./ `??f$+2<<'BWBWB[B[]^+_Gx(`rP   c                    | j                   j                  g| j                   j                  j                         }t        D ]  }|D ]
  } ||         y)zNApply each `(graph_like) -> None` IR fix to the main graph and every function.N)r@   rB  	functionsr   	_IR_FIXES)rE   graphsfixrB  s       rG   r>   r>     sU      &&O););)E)E)L)L)NOF  	EJ	rP   )rB   	list[str]rC   r  rH   ztuple[list[str], list[str]])r   str)rG  ztorch.fx.GraphModulerH  ztorch.fx.NoderH   rj   )r   r   r?   r   rH   rj   )F)
r?   r   r   zSequence[INT64 | BOOL | None]r   r   r  rj   rH   r   r   )r?   r   r  rh   rH   r   rq   )r  r   r  r   r  r   rH   r   )r  zonnx_ir.GraphrH   None)rE   r   rH   r  )~rL   
__future__r   r;   rZ   operatorcollections.abcr   r   
contextlibr   typingr   r   r  r   utilsr
   utils.import_utilsr   r   configsr   exporter_dynamor   r   r   r   r   r   r   r!   torch.exportr   
torch.onnxr    r   r   +onnxscript.function_libs.torch_lib.ops.corer   onnxscript.onnx_opsetr   r  modeling_utilsr   r   r   r   
get_logger__file__loggerr    r5   r8   rs   r|   r   r   r   r   r   r   r   r   r   r   r  r	  r  r  r  r&  r2  r7  	frozensetleltgegteqner@  rI  rP  rY  r^  r]   r`  updaterK  rL  _assert_asyncrN  msg_assert_scalar_assert_tensor_metadatasym_constrain_range_for_sizera  rm  rq  rx  r|  r  r  r  r  r  r  r  r=   __annotations__bincountr]  _grouped_mmtransformersgrouped_mm_fallbackrepeat_interleaveself_intmasked_fillr{  rf   floordivr  r  r>   r&   rP   rG   <module>r"     s   . #    4 % %   L  +  ,&  J30 RR 
		H	%0> 0n ) )0'  & '( )+CD
 E
 IJ K  23	 4	 () * & ': NO P&, (=>9 ?9 (=>9 ?9 %78
 9
 )* +" )* +" ,- .* % & +,	 -	 *+ ,  56
 7
 % &N bc dB X[[(++x{{HKKQYQ\Q\^f^i^ijk f 8 f  f  f  IINN((00IINN((,,IINN))11IINN22::IINN77??	
 f  f & f  f  f 22 	&.
&.*&. &. 	&.
 &.RJ'D(*VB$," +-  ,""IINN##++^IINN$$,,oIINN&&..0@II""66>>@PIINN,,557WIINN&&--/@IINN&&--/@1		
4` 	
rP   