We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Proposal: Op Registry
Once we start getting into more complicated passes having knowledge about operators becomes incredibly useful. What I propose is to maintain a registry of operators mapping to a set of descriptors. 

For example let's take a look at the [eliminate_nop_monotone_argmax](https://github.com/onnx/onnx/blob/master/onnx/optimizer/passes/eliminate_nop_monotone_argmax.h) pass. Within the pass we have to maintain an unordered set of ops that are monotone. The correct design would be to have a registry which we can ask whether or not if a certain op is monotone. Once such a registry is implemented, passes can be generalized through descriptors, therefore as we add new ops and their respective descriptors our passes naturally scale out.

Some possible descriptors we could have are:
- monotonic
- elementwise independent (op works on an individual element without knowledge of other elements, such as log, but not softmax since it requires calculating a sum over an axis)
- elementwise dependent (softmax, logsoftmax, softmin, softplus)
- reduction
- shape transform (e.g. transpose, view)

@houseroad @bddppq What do you guys think?

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `onnx/optimizer.py`
Content:
```
1 # ATTENTION: The code in this file is highly EXPERIMENTAL.
2 # Adventurous users should note that the APIs will probably change.
3 
4 """onnx optimizer
5 
6 This enables users to optimize their models.
7 """
8 from __future__ import absolute_import
9 from __future__ import division
10 from __future__ import print_function
11 from __future__ import unicode_literals
12 
13 import onnx
14 import onnx.onnx_cpp2py_export.optimizer as C
15 from onnx import ModelProto
16 from typing import Text, Sequence, Optional
17 
18 """Apply the optimization on the serialized ModelProto.
19 
20 Arguments:
21     input (ModelProto): model
22     names (list of string): list of optimization names
23 
24 Return:
25     return (ModelProto) optimized model
26 
27 Supported pass names:
28     -- nop
29     -- eliminate_identity
30     -- eliminate_nop_transpose
31     -- eliminate_nop_pad
32     -- eliminate_unused_initializer
33     -- fuse_consecutive_squeezes
34     -- fuse_consecutive_transposes
35     -- fuse_add_bias_into_conv
36     -- fuse_transpose_into_gemm
37 """
38 
39 get_available_passes = C.get_available_passes
40 
41 
42 def optimize(model, passes=None, fixed_point=False):  # type: (ModelProto, Optional[Sequence[Text]], bool) -> ModelProto
43     if passes is None:
44         passes = ['eliminate_nop_transpose',
45                   'eliminate_nop_pad',
46                   'fuse_consecutive_transposes',
47                   'fuse_transpose_into_gemm']
48     if not isinstance(model, ModelProto):
49         raise ValueError('Optimizer only accepts ModelProto, incorrect type: {}'.format(type(model)))
50 
51     model_str = model.SerializeToString()
52     if fixed_point:
53         optimized_model_str = C.optimize_fixedpoint(model_str, passes)
54     else:
55         optimized_model_str = C.optimize(model_str, passes)
56 
57     return onnx.load_from_string(optimized_model_str)
58
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/onnx/optimizer.py b/onnx/optimizer.py
--- a/onnx/optimizer.py
+++ b/onnx/optimizer.py
@@ -26,13 +26,23 @@
 
 Supported pass names:
     -- nop
+    -- eliminate_deadend
     -- eliminate_identity
-    -- eliminate_nop_transpose
+    -- eliminate_nop_dropout
+    -- eliminate_nop_monotone_argmax
     -- eliminate_nop_pad
+    -- eliminate_nop_transpose
     -- eliminate_unused_initializer
+    -- extract_constant_to_initializer
+    -- fuse_add_bias_into_conv
+    -- fuse_bn_into_conv
+    -- fuse_consecutive_concats
+    -- fuse_consecutive_log_softmax
+    -- fuse_consecutive_reduce_unsqueeze
     -- fuse_consecutive_squeezes
     -- fuse_consecutive_transposes
-    -- fuse_add_bias_into_conv
+    -- fuse_matmul_add_bias_into_gemm
+    -- fuse_pad_into_conv
     -- fuse_transpose_into_gemm
 """
 
@@ -45,8 +55,10 @@
                   'eliminate_nop_pad',
                   'fuse_consecutive_transposes',
                   'fuse_transpose_into_gemm']
+
     if not isinstance(model, ModelProto):
-        raise ValueError('Optimizer only accepts ModelProto, incorrect type: {}'.format(type(model)))
+        raise ValueError(
+            'Optimizer only accepts ModelProto, incorrect type: {}'.format(type(model)))
 
     model_str = model.SerializeToString()
     if fixed_point:

{"golden_diff": "diff --git a/onnx/optimizer.py b/onnx/optimizer.py\n--- a/onnx/optimizer.py\n+++ b/onnx/optimizer.py\n@@ -26,13 +26,23 @@\n \n Supported pass names:\n     -- nop\n+    -- eliminate_deadend\n     -- eliminate_identity\n-    -- eliminate_nop_transpose\n+    -- eliminate_nop_dropout\n+    -- eliminate_nop_monotone_argmax\n     -- eliminate_nop_pad\n+    -- eliminate_nop_transpose\n     -- eliminate_unused_initializer\n+    -- extract_constant_to_initializer\n+    -- fuse_add_bias_into_conv\n+    -- fuse_bn_into_conv\n+    -- fuse_consecutive_concats\n+    -- fuse_consecutive_log_softmax\n+    -- fuse_consecutive_reduce_unsqueeze\n     -- fuse_consecutive_squeezes\n     -- fuse_consecutive_transposes\n-    -- fuse_add_bias_into_conv\n+    -- fuse_matmul_add_bias_into_gemm\n+    -- fuse_pad_into_conv\n     -- fuse_transpose_into_gemm\n \"\"\"\n \n@@ -45,8 +55,10 @@\n                   'eliminate_nop_pad',\n                   'fuse_consecutive_transposes',\n                   'fuse_transpose_into_gemm']\n+\n     if not isinstance(model, ModelProto):\n-        raise ValueError('Optimizer only accepts ModelProto, incorrect type: {}'.format(type(model)))\n+        raise ValueError(\n+            'Optimizer only accepts ModelProto, incorrect type: {}'.format(type(model)))\n \n     model_str = model.SerializeToString()\n     if fixed_point:\n", "issue": "Proposal: Op Registry\nOnce we start getting into more complicated passes having knowledge about operators becomes incredibly useful. What I propose is to maintain a registry of operators mapping to a set of descriptors. \r\n\r\nFor example let's take a look at the [eliminate_nop_monotone_argmax](https://github.com/onnx/onnx/blob/master/onnx/optimizer/passes/eliminate_nop_monotone_argmax.h) pass. Within the pass we have to maintain an unordered set of ops that are monotone. The correct design would be to have a registry which we can ask whether or not if a certain op is monotone. Once such a registry is implemented, passes can be generalized through descriptors, therefore as we add new ops and their respective descriptors our passes naturally scale out.\r\n\r\nSome possible descriptors we could have are:\r\n- monotonic\r\n- elementwise independent (op works on an individual element without knowledge of other elements, such as log, but not softmax since it requires calculating a sum over an axis)\r\n- elementwise dependent (softmax, logsoftmax, softmin, softplus)\r\n- reduction\r\n- shape transform (e.g. transpose, view)\r\n\r\n@houseroad @bddppq What do you guys think?\n", "before_files": [{"content": "# ATTENTION: The code in this file is highly EXPERIMENTAL.\n# Adventurous users should note that the APIs will probably change.\n\n\"\"\"onnx optimizer\n\nThis enables users to optimize their models.\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport onnx\nimport onnx.onnx_cpp2py_export.optimizer as C\nfrom onnx import ModelProto\nfrom typing import Text, Sequence, Optional\n\n\"\"\"Apply the optimization on the serialized ModelProto.\n\nArguments:\n    input (ModelProto): model\n    names (list of string): list of optimization names\n\nReturn:\n    return (ModelProto) optimized model\n\nSupported pass names:\n    -- nop\n    -- eliminate_identity\n    -- eliminate_nop_transpose\n    -- eliminate_nop_pad\n    -- eliminate_unused_initializer\n    -- fuse_consecutive_squeezes\n    -- fuse_consecutive_transposes\n    -- fuse_add_bias_into_conv\n    -- fuse_transpose_into_gemm\n\"\"\"\n\nget_available_passes = C.get_available_passes\n\n\ndef optimize(model, passes=None, fixed_point=False):  # type: (ModelProto, Optional[Sequence[Text]], bool) -> ModelProto\n    if passes is None:\n        passes = ['eliminate_nop_transpose',\n                  'eliminate_nop_pad',\n                  'fuse_consecutive_transposes',\n                  'fuse_transpose_into_gemm']\n    if not isinstance(model, ModelProto):\n        raise ValueError('Optimizer only accepts ModelProto, incorrect type: {}'.format(type(model)))\n\n    model_str = model.SerializeToString()\n    if fixed_point:\n        optimized_model_str = C.optimize_fixedpoint(model_str, passes)\n    else:\n        optimized_model_str = C.optimize(model_str, passes)\n\n    return onnx.load_from_string(optimized_model_str)\n", "path": "onnx/optimizer.py"}], "after_files": [{"content": "# ATTENTION: The code in this file is highly EXPERIMENTAL.\n# Adventurous users should note that the APIs will probably change.\n\n\"\"\"onnx optimizer\n\nThis enables users to optimize their models.\n\"\"\"\nfrom __future__ import absolute_import\nfrom __future__ import division\nfrom __future__ import print_function\nfrom __future__ import unicode_literals\n\nimport onnx\nimport onnx.onnx_cpp2py_export.optimizer as C\nfrom onnx import ModelProto\nfrom typing import Text, Sequence, Optional\n\n\"\"\"Apply the optimization on the serialized ModelProto.\n\nArguments:\n    input (ModelProto): model\n    names (list of string): list of optimization names\n\nReturn:\n    return (ModelProto) optimized model\n\nSupported pass names:\n    -- nop\n    -- eliminate_deadend\n    -- eliminate_identity\n    -- eliminate_nop_dropout\n    -- eliminate_nop_monotone_argmax\n    -- eliminate_nop_pad\n    -- eliminate_nop_transpose\n    -- eliminate_unused_initializer\n    -- extract_constant_to_initializer\n    -- fuse_add_bias_into_conv\n    -- fuse_bn_into_conv\n    -- fuse_consecutive_concats\n    -- fuse_consecutive_log_softmax\n    -- fuse_consecutive_reduce_unsqueeze\n    -- fuse_consecutive_squeezes\n    -- fuse_consecutive_transposes\n    -- fuse_matmul_add_bias_into_gemm\n    -- fuse_pad_into_conv\n    -- fuse_transpose_into_gemm\n\"\"\"\n\nget_available_passes = C.get_available_passes\n\n\ndef optimize(model, passes=None, fixed_point=False):  # type: (ModelProto, Optional[Sequence[Text]], bool) -> ModelProto\n    if passes is None:\n        passes = ['eliminate_nop_transpose',\n                  'eliminate_nop_pad',\n                  'fuse_consecutive_transposes',\n                  'fuse_transpose_into_gemm']\n\n    if not isinstance(model, ModelProto):\n        raise ValueError(\n            'Optimizer only accepts ModelProto, incorrect type: {}'.format(type(model)))\n\n    model_str = model.SerializeToString()\n    if fixed_point:\n        optimized_model_str = C.optimize_fixedpoint(model_str, passes)\n    else:\n        optimized_model_str = C.optimize(model_str, passes)\n\n    return onnx.load_from_string(optimized_model_str)\n", "path": "onnx/optimizer.py"}]}