-
Notifications
You must be signed in to change notification settings - Fork 53
Namespace based customop registration #204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Namespace based customop registration #204
Conversation
Replace dictionary-based registration with module namespace lookup: - Direct attribute lookup in module namespace (preferred) - Legacy custom_op dictionary support (backward compatibility) - Domain aliasing (e.g., onnx.brevitas -> qonnx.custom_op.general) - Runtime op registration via add_op_to_domain() - No expensive fallbacks (removed case-insensitive matching, full module scan) - Clear, actionable error messages Benefits: - Better IDE support with direct imports - Cleaner, more Pythonic API - O(1) lookup performance - Predictable behavior - External packages work via domain-based imports (e.g., finn.custom_op.fpgadataflow)
|
How are these ops in the custom domain suppose to execute? However, the function I also tested the code on chisel4ml, with a custom defined in the chisel4ml.preprocess namespace. I then called Am I missing something? should I be calling something else? Honestly, I just don't see how this can execute. |
src/qonnx/custom_op/registry.py
Outdated
| Example: | ||
| add_op_to_domain("qonnx.custom_op.general", "TestOp", TestOp) | ||
| """ | ||
| if not inspect.isclass(op_class) or not issubclass(op_class, CustomOp): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is not correct. It evaluates to True even if op_class is not subclass of CustomOp. This code will evaluate to True on anything, even if op_class is a float.
Regardless of that, I do not see the point of using inspect here. If the intention is to prevent the user from using an object instance here, issubclass will raise a TypeError. Which is a more appropriate error than ValueError.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is from an old iteration where it cached all CustomOps at a given domain on first access, this avoided illegal comparisons. Thanks for catching this, will remove in next commit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is still not resolved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tafk7 could you address this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Apologies for the delay, resolved in latest commit
@jurevreca12 Could you clarify the expected behavior of the "Brevitas exception?" Since it was only applied in the registry, I assumed similar aliasing should only be used for path resolution when the node's domain doesn't match its import path. Therefore, it's proper for |
- Replace eager module inspection with on-demand discovery - Add RLock for thread-safe registry access - Cache discovered ops in _OP_REGISTRY dict - Remove get_ops_in_domain() and add_op_to_domain() - Privatize registry state variables
Replace hard-coded domain string checks with registry-based lookups. Add is_custom_op() function to registry.py that checks if a domain or specific (domain, op_type) combination exists via registry lookup and module import fallback. Changes: - Add is_custom_op(domain, op_type=None) to custom_op/registry.py - Deprecate hasCustomOp in favor of is_custom_op - Replace is_finn_op implementation with deprecation wrapper - Update all internal uses across 7 files: * modelwrapper.py: get_finn_nodes/get_non_finn_nodes * onnx_exec.py: execute_node domain check * infer_shapes.py: _hide_finn_ops and assertion * infer_data_layouts.py: _dims_to_layout and _infer_node_data_layout * infer_datatypes.py: _infer_node_datatype is_custom_op handles empty domains (standard ONNX) and provides precise op checking when op_type is specified.
|
I think the easiest path forward for the Brevitas exception is there for legacy reasons, from an (ancient) time when QONNX hadn't become its own standalone thing and Brevitas exported custom ops with its own domain string. then we decided to make QONNX its own standard. as a side note: today everything will be exported with |
|
Sorry, for the slow responses. @maltanar What is the problem with importing the registry module? Although the simple |
|
So, I've tested this latest commit on my chisel4ml code base and it works fine. I think everything looks good, with the exception of that one if statement. I think the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
|
This looks ready to go, merging now. Thanks @tafk7 and @jurevreca12 ! |
Summary
This PR enables CustomOp registration through Python module namespaces, eliminating the need for manual dictionary registration. CustomOps can now be discovered automatically by importing the module corresponding to the ONNX domain name. The system maintains full backward compatibility with legacy
custom_opdictionaries while providing better IDE support and a more Pythonic API.Key Changes
Core Registry (
src/qonnx/custom_op/registry.py)getattr(module, op_type))custom_opdictionariesadd_domain_alias())resolve_domain()helper for alias resolutionPerformance & Safety Improvements
_OP_REGISTRY)RLock_OP_REGISTRY,_DOMAIN_ALIASES)Cleanup
add_op_to_domain()andget_ops_in_domain()functions__all__exports from modulesBenefits
custom_opdictionaries continue to workMigration Path
No code changes required. The system automatically discovers ops from both module namespaces and legacy
custom_opdictionaries. New CustomOps can be registered by:custom_opdictionaries (backward compatibility)add_domain_alias()to map custom domains to module paths