# PyTorch ONNX Conversion Report

```
✅ Obtain model graph with `torch.export.export(..., strict=False)`
⚪ Obtain model graph with `torch.export.export(..., strict=True)`
⚪ Obtain model graph with `torch.export.draft_export`
✅ Decompose operators for ONNX compatibility
✅ Translate the graph into ONNX
✅ Run `onnx.checker` on the ONNX model
✅ Execute the model with ONNX Runtime
✅ Validate model output accuracy
```

## Error messages

```pytb
No errors
```

## Exported program

```python
ExportedProgram:
    class GraphModule(torch.nn.Module):
        def forward(self, p_model_0_weight: "f32[1, 3, 8, 8]", p_model_0_bias: "f32[1]", p_model_3_weight: "f32[10, 1024]", p_model_3_bias: "f32[10]", x: "f32[s77, 3, 256, 256]"):
             # 
            sym_size_int_1: "Sym(s77)" = torch.ops.aten.sym_size.int(x, 0)
            
             # File: /data/ai-parade/toolkit/tests/end2end/fixtures/FooBarModel/.venv/lib/python3.12/site-packages/torch/nn/modules/conv.py:548 in forward, code: return self._conv_forward(input, self.weight, self.bias)
            conv2d: "f32[s77, 1, 32, 32]" = torch.ops.aten.conv2d.default(x, p_model_0_weight, p_model_0_bias, [8, 8], [2, 2]);  x = p_model_0_weight = p_model_0_bias = None
            
             # File: /data/ai-parade/toolkit/tests/end2end/fixtures/FooBarModel/.venv/lib/python3.12/site-packages/torch/nn/modules/activation.py:144 in forward, code: return F.relu(input, inplace=self.inplace)
            relu: "f32[s77, 1, 32, 32]" = torch.ops.aten.relu.default(conv2d);  conv2d = None
            
             # File: /data/ai-parade/toolkit/tests/end2end/fixtures/FooBarModel/.venv/lib/python3.12/site-packages/torch/nn/modules/flatten.py:56 in forward, code: return input.flatten(self.start_dim, self.end_dim)
            view: "f32[s77, 1024]" = torch.ops.aten.view.default(relu, [sym_size_int_1, 1024]);  relu = sym_size_int_1 = None
            
             # File: /data/ai-parade/toolkit/tests/end2end/fixtures/FooBarModel/.venv/lib/python3.12/site-packages/torch/nn/modules/linear.py:134 in forward, code: return F.linear(input, self.weight, self.bias)
            linear: "f32[s77, 10]" = torch.ops.aten.linear.default(view, p_model_3_weight, p_model_3_bias);  view = p_model_3_weight = p_model_3_bias = None
            return (linear,)
            
Graph signature: 
    # inputs
    p_model_0_weight: PARAMETER target='model.0.weight'
    p_model_0_bias: PARAMETER target='model.0.bias'
    p_model_3_weight: PARAMETER target='model.3.weight'
    p_model_3_bias: PARAMETER target='model.3.bias'
    x: USER_INPUT
    
    # outputs
    linear: USER_OUTPUT
    
Range constraints: {s77: VR[1, int_oo]}

```

## ONNX model

```python
<
    ir_version=10,
    opset_imports={'pkg.onnxscript.torch_lib.common': 1, '': 18},
    producer_name='pytorch',
    producer_version='2.9.1+cu128',
    domain=None,
    model_version=None,
>
graph(
    name=main_graph,
    inputs=(
        %"input"<FLOAT,[s77,3,256,256]>
    ),
    outputs=(
        %"linear"<FLOAT,[s77,10]>
    ),
    initializers=(
        %"model.0.weight"<FLOAT,[1,3,8,8]>{TorchTensor(...)},
        %"model.0.bias"<FLOAT,[1]>{TorchTensor<FLOAT,[1]>(Parameter containing: tensor([0.4584], requires_grad=True), name='model.0.bias')},
        %"model.3.weight"<FLOAT,[10,1024]>{TorchTensor(...)},
        %"model.3.bias"<FLOAT,[10]>{TorchTensor<FLOAT,[10]>(Parameter containing: tensor([-0.8847,  0.8561,  0.3020,  0.6523, -0.9348,  0.9466,  0.1234, -0.3637, 0.0956,  0.1469], requires_grad=True), name='model.3.bias')}
    ),
) {
     0 |  # node_Shape_0
          %"val_0"<?,?> ⬅️ ::Shape(%"input") {end=1, start=0}
     1 |  # node_sym_size_int_1
          %"sym_size_int_1"<INT64,[]> ⬅️ ::Squeeze(%"val_0")
     2 |  # node_conv2d
          %"conv2d"<FLOAT,[s77,1,32,32]> ⬅️ ::Conv(%"input", %"model.0.weight"{...}, %"model.0.bias"{[0.45836174488067627]}) {group=1, pads=(2, 2, 2, 2), auto_pad='NOTSET', strides=(8, 8), dilations=(1, 1)}
     3 |  # node_relu
          %"relu"<FLOAT,[s77,1,32,32]> ⬅️ ::Relu(%"conv2d")
     4 |  # node_Constant_1
          %"val_1"<?,?> ⬅️ ::Constant() {value_ints=(-1,)}
     5 |  # node_Reshape_2
          %"val_2"<INT64,?> ⬅️ ::Reshape(%"sym_size_int_1", %"val_1") {allowzero=0}
     6 |  # node_Constant_3
          %"val_3"<INT64,?> ⬅️ ::Constant() {value_ints=(1024,)}
     7 |  # node_Concat_4
          %"val_4"<?,?> ⬅️ ::Concat(%"val_2", %"val_3") {axis=0}
     8 |  # node_view
          %"view"<FLOAT,[s77,1024]> ⬅️ ::Reshape(%"relu", %"val_4") {allowzero=1}
     9 |  # node_linear
          %"linear"<FLOAT,[s77,10]> ⬅️ ::Gemm(%"view", %"model.3.weight"{...}, %"model.3.bias"{[-0.8846927881240845, 0.8561344146728516, 0.30204296112060547, 0.6522780656814575, -0.9348465204238892, 0.9466043710708618, 0.123374342918396, -0.3637460470199585, 0.09561705589294434, 0.14687907695770264]}) {beta=1.0, transB=1, alpha=1.0, transA=0}
    return %"linear"<FLOAT,[s77,10]>
}

<
    opset_imports={'': 18},
>
def pkg.onnxscript.torch_lib.common::Rank(
    inputs=(
        %"input"<?,?>
    ),
    outputs=(
        %"return_val"<?,?>
    ),
) {
    0 |  # n0
         %"tmp"<?,?> ⬅️ ::Shape(%"input")
    1 |  # n1
         %"return_val"<?,?> ⬅️ ::Size(%"tmp")
    return %"return_val"<?,?>
}

<
    opset_imports={'': 18},
>
def pkg.onnxscript.torch_lib.common::IsScalar(
    inputs=(
        %"input"<?,?>
    ),
    outputs=(
        %"return_val"<?,?>
    ),
) {
    0 |  # n0
         %"tmp"<?,?> ⬅️ ::Shape(%"input")
    1 |  # n1
         %"tmp_0"<?,?> ⬅️ ::Size(%"tmp")
    2 |  # n2
         %"tmp_1"<?,?> ⬅️ ::Constant() {value_int=0}
    3 |  # n3
         %"return_val"<?,?> ⬅️ ::Equal(%"tmp_0", %"tmp_1")
    return %"return_val"<?,?>
}
```

## Analysis

PyTorch ONNX Conversion Analysis

## Model Information

The model has 10443 parameters and 0 buffers (non-trainable parameters).
Number of parameters per dtype:
```python
defaultdict(<class 'int'>, {torch.float32: 10443})
```
Number of buffers per dtype:
```python
defaultdict(<class 'int'>, {})
```

Inputs:
- `x`: `TensorMetadata(shape=torch.Size([s77, 3, 256, 256]), dtype=torch.float32, requires_grad=False, stride=(196608, 65536, 256, 1), memory_format=torch.contiguous_format, is_quantized=False, qparams={})`

Outputs:
- `linear`: `TensorMetadata(shape=torch.Size([s77, 10]), dtype=torch.float32, requires_grad=False, stride=(10, 1), memory_format=torch.contiguous_format, is_quantized=False, qparams={})`

The FX graph has 11 nodes in total. Number of FX nodes per op:
- `placeholder`: 5
- `call_function`: 5
- `output`: 1


Of the call_function nodes, the counts of operators used are:

- `aten.sym_size.int`: 1
- `aten.conv2d.default`: 1
- `aten.relu.default`: 1
- `aten.view.default`: 1
- `aten.linear.default`: 1

## ONNX Conversion Information

All operators in the model have registered ONNX decompositions.

## Decomposition comparison

Ops exist only in the ExportedProgram before decomposition: `['aten.flatten.using_ints']`

Ops exist only in the ExportedProgram after decomposition: `['aten.sym_size.int', 'aten.view.default']`


## Verification results

`linear`: `max_abs_diff=1.430511e-06`, `max_rel_diff=1.177746e-06`, `abs_diff_hist=torch.return_types.histogram(
hist=tensor([6., 4., 0., 0., 0., 0., 0., 0., 0.]),
bin_edges=tensor([0.0000e+00, 1.0000e-06, 1.0000e-05, 1.0000e-04, 1.0000e-03, 1.0000e-02,
        1.0000e-01, 1.0000e+00, 1.0000e+01, 1.0000e+06]))`, `rel_diff_hist=torch.return_types.histogram(
hist=tensor([9., 1., 0., 0., 0., 0., 0., 0., 0.]),
bin_edges=tensor([0.0000e+00, 1.0000e-06, 1.0000e-05, 1.0000e-04, 1.0000e-03, 1.0000e-02,
        1.0000e-01, 1.0000e+00, 1.0000e+01, 1.0000e+06]))`
