Repository: microsoft/onnxscript
Pull request #2212: [pass] Remove unused initialized inputs in DCE
Author: login: leshabirukov
Labels: name: topic: passes
Created: 2025-04-17T10:09:53Z | Merged: 2025-04-23T00:34:39Z

Fix https://github.com/microsoft/onnxscript/issues/2211

This pull request enhances the functionality of the `RemoveUnusedNodesPass` class and its associated methods by introducing an option to remove unused initialized inputs. It also updates the corresponding tests to validate this new behavior. The changes improve the flexibility of the unused node removal process and ensure the model input signature remains consistent unless explicitly modified.

### Enhancements to `RemoveUnusedNodesPass`:

* Added a new `remove_initialized_inputs` attribute to the `RemoveUnusedNodesPass` class, allowing the removal of unused initialized inputs when enabled. This change modifies the model input signature if unused inputs are removed (`onnxscript/ir/passes/common/unused_removal.py`).



Diff:
diff --git a/onnxscript/ir/passes/common/unused_removal.py b/onnxscript/ir/passes/common/unused_removal.py
index 112bf2be45..de4446bd62 100644
--- a/onnxscript/ir/passes/common/unused_removal.py
+++ b/onnxscript/ir/passes/common/unused_removal.py
@@ -93,10 +93,27 @@ def _remove_unused_nodes_in_graph_like(function_or_graph: ir.Function | ir.Graph
 
 
 class RemoveUnusedNodesPass(ir.passes.InPlacePass):
+    """Pass for removing unused nodes and initializers.
+
+    Attributes:
+        remove_initialized_inputs: When an unused initializer is simultaneously a graph input,
+            remove that input as well. Note that this will change the model input signature.
+    """
+
+    def __init__(self, remove_initialized_inputs: bool = False):
+        super().__init__()
+        self.remove_initialized_inputs = remove_initialized_inputs
+
     def call(self, model: ir.Model) -> ir.passes.PassResult:
         count = _remove_unused_nodes_in_graph_like(model.graph)
         graph_outputs = frozenset(model.graph.outputs)
         initializers = model.graph.initializers
+        if self.remove_initialized_inputs:
+            graph_inputs = model.graph.inputs
+            for i, inp in reversed(list(enumerate(graph_inputs))):
+                if inp.name in initializers and not (inp in graph_outputs or inp.uses()):
+                    del graph_inputs[i]
+                    count += 1
         for init in list(initializers.values()):
             if not (init in graph_outputs or init.uses()):
                 assert init.name is not None
diff --git a/onnxscript/ir/passes/common/unused_removal_test.py b/onnxscript/ir/passes/common/unused_removal_test.py
index 664b36577c..d0a27626ed 100644
--- a/onnxscript/ir/passes/common/unused_removal_test.py
+++ b/onnxscript/ir/passes/common/unused_removal_test.py
@@ -13,13 +13,15 @@
 class RemoveUnusedTest(unittest.TestCase):
     using_ir: bool
 
-    def remove_unused_nodes(self, model: onnx.ModelProto):
+    def remove_unused_nodes(
+        self, model: onnx.ModelProto, remove_initialized_inputs: bool = False
+    ):
         if self.using_ir:
             model_ir = ir.serde.deserialize_model(model)
-            onnxscript.optimizer.remove_unused_nodes(model_ir)
+            onnxscript.optimizer.remove_unused_nodes(model_ir, remove_initialized_inputs)
             model = ir.serde.serialize_model(model_ir)
             return model
-        onnxscript.optimizer.remove_unused_nodes(model)
+        onnxscript.optimizer.remove_unused_nodes(model, remove_initialized_inputs)
         return model
 
     def test_remove_unused_nodes(self):
@@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_unused_initialized_inputs_are_removed_when_requested(self):
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, remove_initialized_inputs=True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.input), 1)
+
+    def test_unused_initialized_inputs_are_kept_by_default(self):
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.input), 2)
+
+    @parameterized.parameterized.expand([True, False])
+    def test_unused_inputs_are_not_removed(self, remove_initialized_inputs: bool):
+        # preserve inputs as part of interface
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(
+            model, remove_initialized_inputs=remove_initialized_inputs
+        )
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.input), 2)
+
     def test_partially_used_nodes(self):
         model = onnx.parser.parse_model(
             """
diff --git a/onnxscript/optimizer/__init__.py b/onnxscript/optimizer/__init__.py
index a6e8ea2fc5..7cb0653a05 100644
--- a/onnxscript/optimizer/__init__.py
+++ b/onnxscript/optimizer/__init__.py
@@ -112,15 +112,19 @@ def fold_constants(
         return result
 
 
-def remove_unused_nodes(model: ir.Model | onnx.ModelProto) -> None:
+def remove_unused_nodes(
+    model: ir.Model | onnx.ModelProto, remove_initialized_inputs: bool = False
+) -> None:
     """Removes unused nodes from a model inplace."""
     if isinstance(model, ir.Model):
-        onnxscript.ir.passes.common.unused_removal.RemoveUnusedNodesPass()(model)
+        onnxscript.ir.passes.common.unused_removal.RemoveUnusedNodesPass(
+            remove_initialized_inputs=remove_initialized_inputs
+        )(model)
     else:
         model_ir = ir.serde.deserialize_model(model)
-        model_ir = onnxscript.ir.passes.common.unused_removal.RemoveUnusedNodesPass()(
-            model_ir
-        ).model
+        model_ir = onnxscript.ir.passes.common.unused_removal.RemoveUnusedNodesPass(
+            remove_initialized_inputs=remove_initialized_inputs
+        )(model_ir).model
         new_proto = ir.serde.serialize_model(model_ir)
         model.Clear()
         model.CopyFrom(new_proto)


Reviews:
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T15:24:08Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T15:30:51Z
user: login: leshabirukov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-17T15:36:56Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T15:48:46Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-17T16:18:43Z
user: login: leshabirukov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T15:35:50Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-18T17:43:51Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-18T17:45:37Z
user: login: leshabirukov
body: How I can approve it? It grayed  

-----
ok
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T09:25:17Z
user: login: leshabirukov
body: Fixed tests, changed flag defaults
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T10:02:40Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T15:20:53Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T15:23:55Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-21T22:36:37Z
user: login: justinchuby
body: Could you fix lint errors? You may install lintrunner to run them locally: https://github.com/microsoft/onnxscript#coding-style
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-21T22:37:10Z
user: login: leshabirukov
body: fixing tests, cosmetics
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T13:18:20Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:32:10Z
user: login: leshabirukov
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T15:32:59Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:33:28Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:33:40Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:34:27Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:35:01Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:35:08Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:35:28Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:35:49Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:37:17Z
user: login: justinchuby
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-22T15:38:35Z
user: login: leshabirukov
body: aaand...
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T15:44:53Z
user: login: justinchuby
body: Thank you for making the contribution!
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-22T17:24:09Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR enhances the unused node removal functionality by introducing a configurable option to remove unused initialized inputs. Key changes include:
- Updating the optimizer’s remove_unused_nodes function to accept a new remove_initialized_inputs parameter.
- Modifying tests in unused_removal_test.py to verify that unused initializer inputs are removed when requested.
- Updating the RemoveUnusedNodesPass to conditionally remove graph inputs matching unused initializers.

### Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

| File                                                | Description                                                           |
| --------------------------------------------------- | --------------------------------------------------------------------- |
| onnxscript/optimizer/__init__.py                    | Modified call signature to pass remove_initialized_inputs parameter     |
| onnxscript/ir/passes/common/unused_removal_test.py    | Added tests to verify removal of unused initialized inputs              |
| onnxscript/ir/passes/common/unused_removal.py         | Updated pass implementation to remove corresponding graph inputs        |


<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**onnxscript/ir/passes/common/unused_removal.py:113**
* [nitpick] Consider renaming the variable 'input' to avoid shadowing the built-in function 'input'.
```
for i, input in reversed(list(enumerate(graph_inputs))):
```
</details>


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T17:25:51Z
user: login: justinchuby
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-23T00:34:37Z

Review comments:
diff_hunk: @@ -97,6 +97,11 @@ def call(self, model: ir.Model) -> ir.passes.PassResult:
         count = _remove_unused_nodes_in_graph_like(model.graph)
         graph_outputs = frozenset(model.graph.outputs)
         initializers = model.graph.initializers
+        graph_inputs = model.graph.inputs
+        for num, input in list( enumerate( graph_inputs ) )[::-1]:
path: onnxscript/ir/passes/common/unused_removal.py
user: login: justinchuby
body: Nit: use reversed instead of [::-1]
created_at: 2025-04-17T15:24:08Z
author_association: COLLABORATOR
diff_hunk: @@ -97,6 +97,11 @@
         count = _remove_unused_nodes_in_graph_like(model.graph)
         graph_outputs = frozenset(model.graph.outputs)
         initializers = model.graph.initializers
+        graph_inputs = model.graph.inputs
+        for num, input in list( enumerate( graph_inputs ) )[::-1]:
+            if input.name in initializers and not (input in graph_outputs or input.uses()):
+                del graph_inputs[num]
+                count += 1                
path: onnxscript/ir/passes/common/unused_removal.py
user: login: github-advanced-security[bot]
body: ## EDITORCONFIG-CHECKER/editorconfig

Trailing whitespace

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16746)
created_at: 2025-04-17T15:30:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -97,6 +97,11 @@
         count = _remove_unused_nodes_in_graph_like(model.graph)
         graph_outputs = frozenset(model.graph.outputs)
         initializers = model.graph.initializers
+        graph_inputs = model.graph.inputs
+        for num, input in list( enumerate( graph_inputs ) )[::-1]:
+            if input.name in initializers and not (input in graph_outputs or input.uses()):
+                del graph_inputs[num]
+                count += 1                
path: onnxscript/ir/passes/common/unused_removal.py
user: login: github-advanced-security[bot]
body: ## RUFF/W291

Trailing whitespace.
See https://docs.astral.sh/ruff/rules/trailing-whitespace

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16747)
created_at: 2025-04-17T15:30:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -97,6 +97,11 @@ def call(self, model: ir.Model) -> ir.passes.PassResult:
         count = _remove_unused_nodes_in_graph_like(model.graph)
         graph_outputs = frozenset(model.graph.outputs)
         initializers = model.graph.initializers
+        graph_inputs = model.graph.inputs
+        for num, input in list( enumerate( graph_inputs ) )[::-1]:
path: onnxscript/ir/passes/common/unused_removal.py
user: login: leshabirukov
body: Done
created_at: 2025-04-17T15:36:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -97,6 +97,11 @@ def call(self, model: ir.Model) -> ir.passes.PassResult:
         count = _remove_unused_nodes_in_graph_like(model.graph)
         graph_outputs = frozenset(model.graph.outputs)
         initializers = model.graph.initializers
+        graph_inputs = model.graph.inputs
+        for num, input in reversed(list(enumerate(graph_inputs))):
+            if input.name in initializers and not (input in graph_outputs or input.uses()):
+                del graph_inputs[num]
+                count += 1
path: onnxscript/ir/passes/common/unused_removal.py
user: login: justinchuby
body: ```suggestion
        for i, input in reversed(list(enumerate(graph_inputs))):
            if input.name in initializers and not (input in graph_outputs or input.uses()):
                del graph_inputs[i]
                count += 1
```
created_at: 2025-04-17T15:48:46Z
author_association: COLLABORATOR
path: onnxscript/ir/passes/common/unused_removal.py
user: login: justinchuby
body: Could you help add a test in https://github.com/leshabirukov/onnxscript/blob/patch-1/onnxscript/ir/passes/common/unused_removal_test.py? Thanks
created_at: 2025-04-17T16:18:43Z
author_association: COLLABORATOR
path: onnxscript/ir/passes/common/unused_removal.py
user: login: leshabirukov
body: Done
created_at: 2025-04-18T15:35:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -93,10 +93,26 @@ def _remove_unused_nodes_in_graph_like(function_or_graph: ir.Function | ir.Graph
 
 
 class RemoveUnusedNodesPass(ir.passes.InPlacePass):
+    def __init__(self, remove_initialized_inputs: bool =True ):
+        """
+        :param remove_initialized_inputs: if `True` (default) remove unused inputs, in case
+            where is corresponding initializer, (those are typically rather initializers than inputs)
+            if changed to `False`, unused inputs remain, even if it has default initializer
+            Note: usual inputs will remain anyhow
+        """
path: onnxscript/ir/passes/common/unused_removal.py
user: login: justinchuby
body: ```suggestion
class RemoveUnusedNodesPass(ir.passes.InPlacePass):
    """Pass for removing unused nodes and initializers.

    Attributes:
        remove_initialized_inputs: When an unused initializer is simultaneously a graph input,
            remove that input as well. Note that this will change the model input signature. 
    """
    def __init__(self, remove_initialized_inputs: bool =True ):
```

Use google style docstring. nit: wording.
created_at: 2025-04-18T17:43:50Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0602

Undefined variable 'remove_unused_nodes' (undefined-variable)
See [undefined-variable](https://pylint.pycqa.org/en/latest/user_guide/messages/error/undefined-variable.html).

To disable, use `  # pylint: disable=undefined-variable`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16753)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## RUFF/F821

Undefined name `remove_unused_nodes`.
See https://docs.astral.sh/ruff/rules/undefined-name

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16769)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
+    assert (len(ir_model.graph._nodes)== 1)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/W0212

Access to a protected member _nodes of a client class (protected-access)
See [protected-access](https://pylint.pycqa.org/en/latest/user_guide/messages/warning/protected-access.html).

To disable, use `  # pylint: disable=protected-access`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16754)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 1)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs_initializers():
+    # supress remove inputs in case they are initializers
+    # if explicitly said
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model,False)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0602

Undefined variable 'remove_unused_nodes' (undefined-variable)
See [undefined-variable](https://pylint.pycqa.org/en/latest/user_guide/messages/error/undefined-variable.html).

To disable, use `  # pylint: disable=undefined-variable`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16755)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 1)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs_initializers():
+    # supress remove inputs in case they are initializers
+    # if explicitly said
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model,False)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## RUFF/F821

Undefined name `remove_unused_nodes`.
See https://docs.astral.sh/ruff/rules/undefined-name

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16770)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 1)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs_initializers():
+    # supress remove inputs in case they are initializers
+    # if explicitly said
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model,False)
+    assert (len(ir_model.graph._nodes)== 1)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/W0212

Access to a protected member _nodes of a client class (protected-access)
See [protected-access](https://pylint.pycqa.org/en/latest/user_guide/messages/warning/protected-access.html).

To disable, use `  # pylint: disable=protected-access`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16756)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 1)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs_initializers():
+    # supress remove inputs in case they are initializers
+    # if explicitly said
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model,False)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 2)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs():
+    # preserve inputs as part of interface
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0602

Undefined variable 'remove_unused_nodes' (undefined-variable)
See [undefined-variable](https://pylint.pycqa.org/en/latest/user_guide/messages/error/undefined-variable.html).

To disable, use `  # pylint: disable=undefined-variable`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16757)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 1)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs_initializers():
+    # supress remove inputs in case they are initializers
+    # if explicitly said
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model,False)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 2)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs():
+    # preserve inputs as part of interface
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## RUFF/F821

Undefined name `remove_unused_nodes`.
See https://docs.astral.sh/ruff/rules/undefined-name

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16771)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+def test_remove_unused_inputs_initializers():
+    # remove inputs in case they are initializers
+    # https://github.com/microsoft/onnxscript/issues/2211
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 1)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs_initializers():
+    # supress remove inputs in case they are initializers
+    # if explicitly said
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        <float two = {2.0,2.0}> {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model,False)
+    assert (len(ir_model.graph._nodes)== 1)
+    assert (len(ir_model.graph.inputs)== 2)
+    assert (ir_model.graph.node(0).op_type== "Mul")
+
+def test_avoid_remove_unused_inputs():
+    # preserve inputs as part of interface
+    model = onnx.parser.parse_model(
+        """
+        <ir_version: 10, opset_import: [ "" : 17]>
+        agraph (float[N] x, float[N] two) => (float[N] z)
+        {
+            four = Add(two, two)
+            z = Mul(x, x)
+        }
+    """
+    )
+    ir_model = onnxscript.ir.serde.deserialize_model(model)
+    remove_unused_nodes(ir_model)
+    assert (len(ir_model.graph._nodes)== 1)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/W0212

Access to a protected member _nodes of a client class (protected-access)
See [protected-access](https://pylint.pycqa.org/en/latest/user_guide/messages/warning/protected-access.html).

To disable, use `  # pylint: disable=protected-access`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16758)
created_at: 2025-04-18T17:45:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -93,10 +93,26 @@ def _remove_unused_nodes_in_graph_like(function_or_graph: ir.Function | ir.Graph
 
 
 class RemoveUnusedNodesPass(ir.passes.InPlacePass):
+    def __init__(self, remove_initialized_inputs: bool =True ):
+        """
+        :param remove_initialized_inputs: if `True` (default) remove unused inputs, in case
+            where is corresponding initializer, (those are typically rather initializers than inputs)
+            if changed to `False`, unused inputs remain, even if it has default initializer
+            Note: usual inputs will remain anyhow
+        """
path: onnxscript/ir/passes/common/unused_removal.py
user: login: leshabirukov
body: ok
created_at: 2025-04-21T09:24:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## First parameter of a method is not named 'self'

Normal methods should have at least one parameter (the first of which should be 'self').

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16776)
created_at: 2025-04-21T15:20:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## First parameter of a method is not named 'self'

Normal methods should have at least one parameter (the first of which should be 'self').

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16777)
created_at: 2025-04-21T15:20:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 2)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs():
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## First parameter of a method is not named 'self'

Normal methods should have at least one parameter (the first of which should be 'self').

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16778)
created_at: 2025-04-21T15:20:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -93,10 +93,26 @@
 
 
 class RemoveUnusedNodesPass(ir.passes.InPlacePass):
+    """Pass for removing unused nodes and initializers.
+
+    Attributes:
+        remove_initialized_inputs: When an unused initializer is simultaneously a graph input,
+            remove that input as well. Note that this will change the model input signature. 
path: onnxscript/ir/passes/common/unused_removal.py
user: login: github-advanced-security[bot]
body: ## EDITORCONFIG-CHECKER/editorconfig

Trailing whitespace

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16797)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -93,10 +93,26 @@
 
 
 class RemoveUnusedNodesPass(ir.passes.InPlacePass):
+    """Pass for removing unused nodes and initializers.
+
+    Attributes:
+        remove_initialized_inputs: When an unused initializer is simultaneously a graph input,
+            remove that input as well. Note that this will change the model input signature. 
path: onnxscript/ir/passes/common/unused_removal.py
user: login: github-advanced-security[bot]
body: ## RUFF/W291

Trailing whitespace.
See https://docs.astral.sh/ruff/rules/trailing-whitespace

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16796)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0211

Method 'test_remove_unused_inputs_initializers' has no argument (no-method-argument)
See [no-method-argument](https://pylint.pycqa.org/en/latest/user_guide/messages/error/no-method-argument.html).

To disable, use `  # pylint: disable=no-method-argument`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16787)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0602

Undefined variable 'self' (undefined-variable)
See [undefined-variable](https://pylint.pycqa.org/en/latest/user_guide/messages/error/undefined-variable.html).

To disable, use `  # pylint: disable=undefined-variable`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16788)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## RUFF/F821

Undefined name `self`.
See https://docs.astral.sh/ruff/rules/undefined-name

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16779)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/W0212

Access to a protected member _nodes of a client class (protected-access)
See [protected-access](https://pylint.pycqa.org/en/latest/user_guide/messages/warning/protected-access.html).

To disable, use `  # pylint: disable=protected-access`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16789)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## RUFF/W293

Blank line contains whitespace.
See https://docs.astral.sh/ruff/rules/blank-line-with-whitespace

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16780)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## EDITORCONFIG-CHECKER/editorconfig

Trailing whitespace

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16785)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0211

Method 'test_avoid_remove_unused_inputs_initializers' has no argument (no-method-argument)
See [no-method-argument](https://pylint.pycqa.org/en/latest/user_guide/messages/error/no-method-argument.html).

To disable, use `  # pylint: disable=no-method-argument`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16790)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0602

Undefined variable 'self' (undefined-variable)
See [undefined-variable](https://pylint.pycqa.org/en/latest/user_guide/messages/error/undefined-variable.html).

To disable, use `  # pylint: disable=undefined-variable`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16791)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## RUFF/F821

Undefined name `self`.
See https://docs.astral.sh/ruff/rules/undefined-name

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16781)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
+        assert (len(ir_model.graph._nodes)== 1)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/W0212

Access to a protected member _nodes of a client class (protected-access)
See [protected-access](https://pylint.pycqa.org/en/latest/user_guide/messages/warning/protected-access.html).

To disable, use `  # pylint: disable=protected-access`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16792)
created_at: 2025-04-21T15:23:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 2)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## RUFF/W293

Blank line contains whitespace.
See https://docs.astral.sh/ruff/rules/blank-line-with-whitespace

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16782)
created_at: 2025-04-21T15:23:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 2)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## EDITORCONFIG-CHECKER/editorconfig

Trailing whitespace

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16786)
created_at: 2025-04-21T15:23:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 2)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs():
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0211

Method 'test_avoid_remove_unused_inputs' has no argument (no-method-argument)
See [no-method-argument](https://pylint.pycqa.org/en/latest/user_guide/messages/error/no-method-argument.html).

To disable, use `  # pylint: disable=no-method-argument`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16793)
created_at: 2025-04-21T15:23:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 2)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs():
+        # preserve inputs as part of interface
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/E0602

Undefined variable 'self' (undefined-variable)
See [undefined-variable](https://pylint.pycqa.org/en/latest/user_guide/messages/error/undefined-variable.html).

To disable, use `  # pylint: disable=undefined-variable`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16794)
created_at: 2025-04-21T15:23:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 2)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs():
+        # preserve inputs as part of interface
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## RUFF/F821

Undefined name `self`.
See https://docs.astral.sh/ruff/rules/undefined-name

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16783)
created_at: 2025-04-21T15:23:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 1)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs_initializers():
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model)
+        assert (len(ir_model.graph._nodes)== 1)
+        assert (len(ir_model.graph.inputs)== 2)
+        assert (ir_model.graph.node(0).op_type== "Mul")
+    
+    def test_avoid_remove_unused_inputs():
+        # preserve inputs as part of interface
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        ir_model = onnxscript.ir.serde.deserialize_model(model)
+        ir_model = self.remove_unused_nodes(ir_model,True)
+        assert (len(ir_model.graph._nodes)== 1)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: github-advanced-security[bot]
body: ## PYLINT/W0212

Access to a protected member _nodes of a client class (protected-access)
See [protected-access](https://pylint.pycqa.org/en/latest/user_guide/messages/warning/protected-access.html).

To disable, use `  # pylint: disable=protected-access`

[Show more details](https://github.com/microsoft/onnxscript/security/code-scanning/16795)
created_at: 2025-04-21T15:23:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +54,62 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers():
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
    def test_remove_unused_inputs_initializers(self):
```

here and below. 
created_at: 2025-04-21T22:36:36Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 1)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
        self.assertEqual(len(model.graph.input), 1)
```
since model is a ModelProto
created_at: 2025-04-22T15:32:09Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 1)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: leshabirukov
body: Yes, done
created_at: 2025-04-22T15:32:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
        model = self.remove_unused_nodes(model, remove_initialized_inputs=True)
```

Specify `remove_initialized_inputs` as keyword arg for redability
created_at: 2025-04-22T15:33:28Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
```
created_at: 2025-04-22T15:33:39Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
    def test_unused_initialized_inputs_are_removed_when_requested(self):
```
created_at: 2025-04-22T15:34:27Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 1)
+
+    def test_avoid_remove_unused_inputs_initializers(self):
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
    def test_unused_initialized_inputs_are_kept_by_default(self):
```
created_at: 2025-04-22T15:35:01Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 1)
+
+    def test_avoid_remove_unused_inputs_initializers(self):
+        # supress remove inputs in case they are initializers until explicitly said
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
```
created_at: 2025-04-22T15:35:08Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 1)
+
+    def test_avoid_remove_unused_inputs_initializers(self):
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 2)
+
+    def test_avoid_remove_unused_inputs(self):
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
    @parameterized.parameterized.expand([True, False])
    def test_unused_inputs_are_not_removed(self, remove_initialized_inputs: bool):
```
created_at: 2025-04-22T15:35:27Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 1)
+
+    def test_avoid_remove_unused_inputs_initializers(self):
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 2)
+
+    def test_avoid_remove_unused_inputs(self):
+        # preserve inputs as part of interface
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
        model = self.remove_unused_nodes(model, remove_initialized_inputs=remove_initialized_inputs)
```

I would parameterize this test
created_at: 2025-04-22T15:35:49Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 1)
+
+    def test_avoid_remove_unused_inputs_initializers(self):
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 2)
+
+    def test_avoid_remove_unused_inputs(self):
+        # preserve inputs as part of interface
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 2)
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: ```suggestion
        self.assertEqual(len(model.graph.input), 2)
```

here and elsewhere
created_at: 2025-04-22T15:37:17Z
author_association: COLLABORATOR
diff_hunk: @@ -54,6 +56,59 @@ def test_remove_unused_initializers(self):
         self.assertEqual(model.graph.node[0].op_type, "Mul")
         self.assertEqual(len(model.graph.initializer), 0)
 
+    def test_remove_unused_inputs_initializers(self):
+        # remove inputs in case they are initializers
+        # if explicitly said
+        # https://github.com/microsoft/onnxscript/issues/2211
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model, True)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 1)
+
+    def test_avoid_remove_unused_inputs_initializers(self):
+        # supress remove inputs in case they are initializers until explicitly said
+        model = onnx.parser.parse_model(
+            """
+            <ir_version: 10, opset_import: [ "" : 17]>
+            agraph (float[N] x, float[N] two) => (float[N] z)
+            <float two = {2.0,2.0}> {
+                four = Add(two, two)
+                z = Mul(x, x)
+            }
+        """
+        )
+        model = self.remove_unused_nodes(model)
+        self.assertEqual(len(model.graph.node), 1)
+        self.assertEqual(model.graph.node[0].op_type, "Mul")
+        self.assertEqual(len(model.graph.inputs), 2)
+
+    def test_avoid_remove_unused_inputs(self):
path: onnxscript/ir/passes/common/unused_removal_test.py
user: login: justinchuby
body: Could you add import parameterized at the top? Thanks
created_at: 2025-04-22T15:38:34Z
author_association: COLLABORATOR

PR comments:
user: login: justinchuby
created_at: 2025-04-17T15:26:20Z
body: Looks good to me, thanks! Maybe create an option in the initializer so users can control?
author_association: COLLABORATOR
user: login: leshabirukov
created_at: 2025-04-17T15:47:48Z
body: > Looks good to me, thanks! Maybe create an option in the initializer so users can control?

May be. There are three alternatives:  
do not remove inputs
remove all unused inputs
remove unused inputs-initializers

How about parameter `remove_unused_inputs` with default=False
True -> remove unused inputs-initializers
author_association: CONTRIBUTOR
user: login: codecov[bot]
created_at: 2025-04-17T15:49:26Z
body: ## [Codecov](https://app.codecov.io/gh/microsoft/onnxscript/pull/2212?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=microsoft) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 73.62%. Comparing base [(`feb20f1`)](https://app.codecov.io/gh/microsoft/onnxscript/commit/feb20f1378899de476980bef8ddf1843d7b720e0?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=microsoft) to head [(`d12b6b2`)](https://app.codecov.io/gh/microsoft/onnxscript/commit/d12b6b24a17af8b9f8d264261e37564a73d470a2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=microsoft).
> Report is 2 commits behind head on main.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #2212      +/-   ##
==========================================
+ Coverage   73.60%   73.62%   +0.02%     
==========================================
  Files         227      227              
  Lines       29052    29080      +28     
  Branches     3327     3330       +3     
==========================================
+ Hits        21383    21411      +28     
  Misses       6544     6544              
  Partials     1125     1125              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/microsoft/onnxscript/pull/2212?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=microsoft).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=microsoft).

author_association: NONE
user: login: justinchuby
created_at: 2025-04-17T15:55:12Z
body: I think option three (remove unused inputs-initializers) is a good balance between correctness and user expectation. It sounds like a good idea to default it to True.
author_association: COLLABORATOR
user: login: justinchuby
created_at: 2025-04-17T18:07:23Z
body: Maybe call the option `remove_initialized_inputs`?
author_association: COLLABORATOR
user: login: leshabirukov
created_at: 2025-04-18T15:48:54Z
body: @microsoft-github-policy-service agree
author_association: CONTRIBUTOR
user: login: justinchuby
created_at: 2025-04-18T18:37:15Z
body: https://github.com/onnx/onnx/blob/baae427e70c27d740f5bea859111e3b63d33512b/onnx/onnx.proto#L72C1-L74C25 mentions initializers does not have to follow inputs. I need to think about the default behavior a little more
author_association: COLLABORATOR
user: login: justinchuby
created_at: 2025-04-19T03:36:50Z
body: > [onnx/onnx@`baae427`/onnx/onnx.proto#L72C1-L74C25](https://github.com/onnx/onnx/blob/baae427e70c27d740f5bea859111e3b63d33512b/onnx/onnx.proto#L72C1-L74C25) mentions initializers does not have to follow inputs. I need to think about the default behavior a little more

After thinking about this, I suggest to keep the same logic, but default the option to False.
author_association: COLLABORATOR
user: login: justinchuby
created_at: 2025-04-22T15:37:51Z
body: Final round of suggestions. Thanks!
author_association: COLLABORATOR
user: login: justinchuby
created_at: 2025-04-22T17:32:28Z
body: I think there is a format change required you can fix by running `lintrunner f`
author_association: COLLABORATOR
user: login: leshabirukov
created_at: 2025-04-22T17:38:36Z
body: Errr...
What about this one?
=================================== FAILURES ===================================
_ TestOnnxBackEnd.test_export2python_produces_correct_onnx_script_model_0397_test_ai_onnx_ml_tree_ensemble_set_membership _
[gw0] linux -- Python 3.11.12 /home/runner/work/onnxscript/onnxscript/.nox/test_ort_nightly/bin/python
onnxscript/converter.py:460: in _eval_constant_expr
    return eval(cpl, self.globals, locals)  # pylint: disable=eval-used
E   NameError: name 'nan' is not defined

The above exception was the direct cause of the following exception:
.nox/test_ort_nightly/lib/python3.11/site-packages/parameterized/parameterized.py:620: in standalone_func
    return func(*(a + p.args), **p.kwargs, **kw)
author_association: CONTRIBUTOR
user: login: justinchuby
created_at: 2025-04-22T17:43:44Z
body: Those checks are fine. They are unrelated. 
author_association: COLLABORATOR
user: login: leshabirukov
created_at: 2025-04-23T11:58:37Z
body: Hurray!
author_association: CONTRIBUTOR