0
# Operator Definitions
1
2
Access to ONNX operator schemas, type definitions, and version compatibility information for all supported operators across different domains. This module provides programmatic access to the complete ONNX operator specification.
3
4
## Capabilities
5
6
### Schema Access
7
8
Retrieve operator schemas and metadata for validation and code generation.
9
10
```python { .api }
11
def get_schema(op_type, max_inclusive_version=None, domain=""):
12
"""
13
Get operator schema for a specific operator.
14
15
Parameters:
16
- op_type: Name of the operator (e.g., 'Conv', 'Relu', 'Add')
17
- max_inclusive_version: Maximum opset version to consider
18
- domain: Operator domain (empty string for ONNX domain)
19
20
Returns:
21
OpSchema: Schema object with operator definition and constraints
22
23
Raises:
24
SchemaError: If operator is not found or version is invalid
25
"""
26
27
def has(op_type, domain=""):
28
"""
29
Check if an operator schema exists.
30
31
Parameters:
32
- op_type: Name of the operator to check
33
- domain: Operator domain (empty string for ONNX domain)
34
35
Returns:
36
bool: True if operator schema exists, False otherwise
37
"""
38
39
def get_all_schemas():
40
"""
41
Get all operator schemas for the current opset version.
42
43
Returns:
44
list[OpSchema]: List of all available operator schemas
45
"""
46
47
def get_all_schemas_with_history():
48
"""
49
Get all operator schemas including historical versions.
50
51
Returns:
52
list[OpSchema]: List of all schemas across all versions
53
"""
54
```
55
56
### Function Operations
57
58
Access to operators that are defined as functions rather than primitives.
59
60
```python { .api }
61
def get_function_ops():
62
"""
63
Get list of operators that are defined as functions.
64
65
Returns:
66
list[str]: Names of operators defined as functions
67
"""
68
```
69
70
### Version Information
71
72
Get current and supported opset versions.
73
74
```python { .api }
75
def onnx_opset_version():
76
"""
77
Get the current ONNX opset version.
78
79
Returns:
80
int: Current opset version number
81
"""
82
```
83
84
### Domain Constants
85
86
Standard domain identifiers for ONNX operators.
87
88
```python { .api }
89
ONNX_DOMAIN = "" # Standard ONNX domain (empty string)
90
ONNX_ML_DOMAIN = "ai.onnx.ml" # ONNX ML domain for traditional ML operators
91
AI_ONNX_PREVIEW_TRAINING_DOMAIN = "ai.onnx.preview.training" # Training operators domain
92
```
93
94
### Schema Classes
95
96
Classes for working with operator schemas and handling errors.
97
98
```python { .api }
99
class OpSchema:
100
"""
101
Operator schema containing definition, constraints, and metadata.
102
103
Key properties:
104
- name: Operator name
105
- domain: Operator domain
106
- since_version: Minimum opset version
107
- doc: Documentation string
108
- attributes: Dict of attribute schemas
109
- inputs: List of input specifications
110
- outputs: List of output specifications
111
- type_constraints: List of type constraints for inputs/outputs
112
"""
113
114
class SchemaError(Exception):
115
"""
116
Exception raised when operator schema operations fail.
117
118
Thrown when requesting schemas for unknown operators,
119
unsupported versions, or invalid operator definitions.
120
"""
121
122
# C++ implementation reference
123
C = ... # Module containing C++ operator definitions implementation
124
```
125
126
## Usage Examples
127
128
### Basic Schema Access
129
130
```python
131
import onnx
132
from onnx import defs
133
134
# Check if an operator exists
135
if defs.has('Conv'):
136
print("Conv operator is available")
137
138
# Get schema for a specific operator
139
conv_schema = defs.get_schema('Conv')
140
print(f"Conv operator documentation: {conv_schema.doc}")
141
print(f"Conv available since opset version: {conv_schema.since_version}")
142
143
# Get schema for a specific version
144
relu_schema = defs.get_schema('Relu', max_inclusive_version=6)
145
print(f"Relu schema for opset <= 6: {relu_schema.name}")
146
147
# Check ML domain operators
148
if defs.has('LinearRegressor', domain=defs.ONNX_ML_DOMAIN):
149
ml_schema = defs.get_schema('LinearRegressor', domain=defs.ONNX_ML_DOMAIN)
150
print(f"ML operator: {ml_schema.name}")
151
```
152
153
### Exploring Available Operators
154
155
```python
156
import onnx
157
from onnx import defs
158
159
# Get current opset version
160
current_opset = defs.onnx_opset_version()
161
print(f"Current ONNX opset version: {current_opset}")
162
163
# List all available operators
164
all_schemas = defs.get_all_schemas()
165
print(f"Total operators available: {len(all_schemas)}")
166
167
# Group operators by domain
168
operators_by_domain = {}
169
for schema in all_schemas:
170
domain = schema.domain or "ONNX"
171
if domain not in operators_by_domain:
172
operators_by_domain[domain] = []
173
operators_by_domain[domain].append(schema.name)
174
175
for domain, ops in operators_by_domain.items():
176
print(f"{domain} domain: {len(ops)} operators")
177
print(f" Examples: {', '.join(sorted(ops)[:5])}")
178
179
# Find function-based operators
180
function_ops = defs.get_function_ops()
181
print(f"Function-based operators: {function_ops}")
182
```
183
184
### Operator Schema Analysis
185
186
```python
187
import onnx
188
from onnx import defs
189
190
def analyze_operator(op_name, domain=""):
191
"""Analyze an operator's schema in detail."""
192
193
try:
194
schema = defs.get_schema(op_name, domain=domain)
195
196
print(f"Operator: {schema.name}")
197
print(f"Domain: {schema.domain or 'ONNX'}")
198
print(f"Since version: {schema.since_version}")
199
print(f"Documentation: {schema.doc[:200]}...")
200
201
# Analyze inputs
202
print(f"\nInputs ({len(schema.inputs)}):")
203
for i, input_spec in enumerate(schema.inputs):
204
print(f" {i}: {input_spec.name} - {input_spec.description}")
205
print(f" Type constraints: {input_spec.type_str}")
206
207
# Analyze outputs
208
print(f"\nOutputs ({len(schema.outputs)}):")
209
for i, output_spec in enumerate(schema.outputs):
210
print(f" {i}: {output_spec.name} - {output_spec.description}")
211
print(f" Type constraints: {output_spec.type_str}")
212
213
# Analyze attributes
214
print(f"\nAttributes ({len(schema.attributes)}):")
215
for attr_name, attr_spec in schema.attributes.items():
216
required = "required" if attr_spec.required else "optional"
217
print(f" {attr_name} ({required}): {attr_spec.description}")
218
print(f" Type: {attr_spec.type}")
219
if hasattr(attr_spec, 'default_value') and attr_spec.default_value:
220
print(f" Default: {attr_spec.default_value}")
221
222
# Check if it's a function
223
if hasattr(schema, 'function_body') and schema.function_body:
224
print(f"\nFunction-based operator with {len(schema.function_body.node)} nodes")
225
226
except defs.SchemaError as e:
227
print(f"Schema error for {op_name}: {e}")
228
229
# Analyze some common operators
230
analyze_operator('Conv')
231
print("\n" + "="*50 + "\n")
232
analyze_operator('BatchNormalization')
233
print("\n" + "="*50 + "\n")
234
analyze_operator('LinearRegressor', domain=defs.ONNX_ML_DOMAIN)
235
```
236
237
### Version Compatibility Checking
238
239
```python
240
import onnx
241
from onnx import defs
242
243
def check_opset_compatibility(model_path):
244
"""Check if a model's operators are compatible with current opset."""
245
246
model = onnx.load_model(model_path)
247
current_opset = defs.onnx_opset_version()
248
249
# Get model's opset imports
250
model_opsets = {}
251
for opset_import in model.opset_import:
252
model_opsets[opset_import.domain] = opset_import.version
253
254
print(f"Model opset versions: {model_opsets}")
255
print(f"Current ONNX opset: {current_opset}")
256
257
# Check each node's operator
258
incompatible_nodes = []
259
for node in model.graph.node:
260
op_type = node.op_type
261
domain = node.domain or ""
262
263
try:
264
# Get schema for model's opset version
265
model_opset_version = model_opsets.get(domain, model_opsets.get("", 1))
266
schema = defs.get_schema(op_type,
267
max_inclusive_version=model_opset_version,
268
domain=domain)
269
270
# Check if operator exists in current version
271
if not defs.has(op_type, domain=domain):
272
incompatible_nodes.append((node.name or f"node_{node.op_type}",
273
op_type, domain, "not available"))
274
elif schema.since_version > current_opset:
275
incompatible_nodes.append((node.name or f"node_{node.op_type}",
276
op_type, domain, "version too high"))
277
278
except defs.SchemaError:
279
incompatible_nodes.append((node.name or f"node_{node.op_type}",
280
op_type, domain, "schema not found"))
281
282
if incompatible_nodes:
283
print(f"\nFound {len(incompatible_nodes)} incompatible nodes:")
284
for node_name, op_type, domain, reason in incompatible_nodes:
285
print(f" {node_name}: {op_type}@{domain} - {reason}")
286
else:
287
print("\nAll operators are compatible with current opset!")
288
289
# Example usage (commented out)
290
# check_opset_compatibility("model.onnx")
291
```
292
293
### Custom Operator Detection
294
295
```python
296
import onnx
297
from onnx import defs
298
299
def find_custom_operators(model_path):
300
"""Find custom (non-standard) operators in a model."""
301
302
model = onnx.load_model(model_path)
303
304
standard_domains = {"", defs.ONNX_ML_DOMAIN, defs.AI_ONNX_PREVIEW_TRAINING_DOMAIN}
305
custom_operators = []
306
307
for node in model.graph.node:
308
op_type = node.op_type
309
domain = node.domain or ""
310
311
# Check if it's a custom domain
312
if domain not in standard_domains:
313
custom_operators.append((op_type, domain, "custom domain"))
314
continue
315
316
# Check if operator exists in standard domains
317
if not defs.has(op_type, domain=domain):
318
custom_operators.append((op_type, domain, "unknown operator"))
319
320
if custom_operators:
321
print(f"Found {len(custom_operators)} custom operators:")
322
for op_type, domain, reason in custom_operators:
323
print(f" {op_type}@{domain} - {reason}")
324
325
# Group by domain
326
domains = set(domain for _, domain, _ in custom_operators)
327
print(f"\nCustom domains used: {domains}")
328
else:
329
print("No custom operators found - model uses only standard ONNX operators")
330
331
# Example usage (commented out)
332
# find_custom_operators("model_with_custom_ops.onnx")
333
```
334
335
### Operator Evolution Analysis
336
337
```python
338
import onnx
339
from onnx import defs
340
341
def analyze_operator_evolution(op_name, domain=""):
342
"""Analyze how an operator has evolved across opset versions."""
343
344
# Get all schemas with history
345
all_schemas = defs.get_all_schemas_with_history()
346
347
# Find all versions of the specified operator
348
op_versions = []
349
for schema in all_schemas:
350
if schema.name == op_name and schema.domain == domain:
351
op_versions.append(schema)
352
353
if not op_versions:
354
print(f"Operator {op_name}@{domain} not found")
355
return
356
357
# Sort by version
358
op_versions.sort(key=lambda s: s.since_version)
359
360
print(f"Evolution of {op_name}@{domain or 'ONNX'}:")
361
362
for schema in op_versions:
363
print(f"\nVersion {schema.since_version}:")
364
print(f" Inputs: {len(schema.inputs)}")
365
print(f" Outputs: {len(schema.outputs)}")
366
print(f" Attributes: {len(schema.attributes)}")
367
368
# Show what changed
369
if len(op_versions) > 1 and schema != op_versions[0]:
370
prev_schema = op_versions[op_versions.index(schema) - 1]
371
372
# Check for new attributes
373
new_attrs = set(schema.attributes.keys()) - set(prev_schema.attributes.keys())
374
if new_attrs:
375
print(f" New attributes: {list(new_attrs)}")
376
377
# Check for removed attributes
378
removed_attrs = set(prev_schema.attributes.keys()) - set(schema.attributes.keys())
379
if removed_attrs:
380
print(f" Removed attributes: {list(removed_attrs)}")
381
382
# Analyze evolution of some operators
383
analyze_operator_evolution('Conv')
384
print("\n" + "="*50 + "\n")
385
analyze_operator_evolution('BatchNormalization')
386
```