[custom op] Drive unitary-synthesis with the dialect-conversion framework#4751
Open
thedaemon-wizard wants to merge 1 commit into
Open
[custom op] Drive unitary-synthesis with the dialect-conversion framework#4751thedaemon-wizard wants to merge 1 commit into
thedaemon-wizard wants to merge 1 commit into
Conversation
…work Follow-up to NVIDIA#4693 (recursive QSD for 3-5 qubit custom operations): rework the unitary-synthesis pass from a greedy rewrite driver (applyPatternsGreedily + OpRewritePattern) to the dialect-conversion framework (ConversionTarget + applyPartialConversion + OpConversionPattern), as discussed during that review. With a conversion target the "do not commit unless legal" guarantee is enforced by the legality predicate instead of by validating-before-emitting inside the pattern: a quake.custom_unitary_constant is illegal (must be rewritten) exactly when a decomposed replacement kernel exists, and every other custom op stays legal and is left untouched. The pass therefore remains composable -- unsupported dimensions, non-unitary matrices, and the rare reconstruction miss are left unchanged with a warning / LLVM_DEBUG note, never a signalPassFailure or crash. The synthesized circuit is unchanged. The decomposed replacement kernels are pre-generated once per generator (walking the custom ops in reverse so the kernels keep their original module order regardless of the conversion driver's traversal order) and each generator matrix is classified/decomposed at most once via a small cache. emitDecomposedFuncOp now takes an OpBuilder so the kernels can be built outside the conversion process. No behavioral change to the decomposition math or emitted gates; all UnitarySynthesis FileCheck and CircuitCheck tests pass unchanged. Signed-off-by: thedaemon-wizard <amon.koike@daemons.jp>
1 task
Collaborator
Command Bot: Processing... |
CI Summary (
|
| Job | Result |
|---|---|
binaries |
⏩ skipped |
build_and_test |
✅ success |
config_devdeps |
✅ success |
config_source_build |
⏩ skipped |
config_wheeldeps |
✅ success |
devdeps |
✅ success |
docker_image |
⏩ skipped |
gen_code_coverage |
⏩ skipped |
metadata |
✅ success |
python_metapackages |
⏩ skipped |
python_wheels |
⏩ skipped |
source_build |
⏩ skipped |
wheeldeps |
✅ success |
⏩ Skipped jobs (7) — intentionally skipped on PR builds; run on merge_group / workflow_dispatch
| Job |
|---|
binaries |
config_source_build |
docker_image |
gen_code_coverage |
python_metapackages |
python_wheels |
source_build |
All sub-jobs (42) — every matrix leg, with links
| Job | Status | Link |
|---|---|---|
| Build and test (amd64, gcc12, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| CI Summary | ❔ in_progress | view |
| Configure build (devdeps) | ✅ success | view |
| Configure build (source_build) | ⏩ skipped | view |
| Configure build (wheeldeps) | ✅ success | view |
| Create CUDA Quantum installer | ⏩ skipped | view |
| Create Docker images | ⏩ skipped | view |
| Create Python metapackages | ⏩ skipped | view |
| Create Python wheels | ⏩ skipped | view |
| Gen code coverage | ⏩ skipped | view |
| Load dependencies (amd64, gcc12) / Caching | ✅ success | view |
| Load dependencies (amd64, gcc12) / Finalize | ✅ success | view |
| Load dependencies (amd64, gcc12) / Metadata | ✅ success | view |
| Load dependencies (amd64, llvm) / Caching | ✅ success | view |
| Load dependencies (amd64, llvm) / Finalize | ✅ success | view |
| Load dependencies (amd64, llvm) / Metadata | ✅ success | view |
| Load dependencies (arm64, gcc12) / Caching | ✅ success | view |
| Load dependencies (arm64, gcc12) / Finalize | ✅ success | view |
| Load dependencies (arm64, gcc12) / Metadata | ✅ success | view |
| Load dependencies (arm64, llvm) / Caching | ✅ success | view |
| Load dependencies (arm64, llvm) / Finalize | ✅ success | view |
| Load dependencies (arm64, llvm) / Metadata | ✅ success | view |
| Load source build cache | ⏩ skipped | view |
| Load wheel dependencies (amd64, 12.6) / Caching | ✅ success | view |
| Load wheel dependencies (amd64, 12.6) / Finalize | ✅ success | view |
| Load wheel dependencies (amd64, 12.6) / Metadata | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Caching | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Finalize | ✅ success | view |
| Load wheel dependencies (amd64, 13.0) / Metadata | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Caching | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Finalize | ✅ success | view |
| Load wheel dependencies (arm64, 12.6) / Metadata | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Caching | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Finalize | ✅ success | view |
| Load wheel dependencies (arm64, 13.0) / Metadata | ✅ success | view |
| Prepare cache clean-up | ❔ in_progress | view |
| Retrieve PR info | ✅ success | view |
✅ Required checks (6/6) — declared in .github/required-checks.yml for push
| Required check | Status | Link |
|---|---|---|
| Build and test (amd64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (arm64, llvm, openmpi) / Dev environment (Python) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Debug) | ✅ success | view |
| Build and test (amd64, gcc12, openmpi) / Dev environment (Python) | ✅ success | view |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
[custom op] Drive unitary-synthesis with the dialect-conversion framework
Follow-up to #4693 (recursive Quantum Shannon Decomposition for 3–5 qubit custom
operations, which closed #2242). As discussed there
(#4693 (comment)), the
conversion-rewriter improvement was deferred to a separate PR — this is that PR.
Summary
The
unitary-synthesispass(
lib/Optimizer/Transforms/UnitarySynthesis.cpp) is reworked from a greedy rewritedriver (
applyPatternsGreedily+OpRewritePattern) to the dialect-conversionframework (
ConversionTarget+applyPartialConversion+OpConversionPattern).This is a driver-only change: the decomposition math and the emitted gate sequences
are unchanged, so every existing
UnitarySynthesistest passes as-is.Motivation
With a conversion target, the "do not commit a rewrite unless the legality constraints are
met" guarantee is expressed by the framework rather than by hand. Previously the pass
enforced composability by validating the whole decomposition before emitting and
returning
failure()to leave the IR untouched. With dialect conversion, aquake.custom_unitary_constantis simply marked illegal (must be rewritten) exactlywhen a decomposed replacement kernel can be produced for it; everything else stays
legal and is left untouched. The pass remains composable — unsupported dimensions,
non-unitary matrices, and the rare reconstruction miss are left unchanged with a warning /
LLVM_DEBUGnote, never asignalPassFailureor a crash.What changed
runOnOperationnow builds aConversionTargetand runs a singlemodule-level
applyPartialConversion.CustomUnitaryPatternis anOpConversionPattern<quake::CustomUnitaryConstantOp>whose body is a pure structuralreplacement to
quake.apply.generator; this is what enforces composability.
generator with a plain
OpBuilder, walking the custom ops in reverse so the kernels keeptheir original module order independent of the conversion driver's traversal order. Each
generator matrix is classified/decomposed at most once via a small cache.
emitDecomposedFuncOp/ the gray-codeemitMuxnow take anOpBuilder &so thekernels are built outside the conversion process.
The design intentionally does not rely on conversion rollback: kernels are
pre-generated and a custom op is only rewritten once it is known-legalizable, so there is
nothing to undo. This matches the direction of MLIR's One-Shot Dialect Conversion driver,
where
ConversionConfig::allowPatternRollbacknow defaults tofalse(llvm/llvm-project#151865).
Verification
Built
cudaq-optagainst LLVM and ran everytest/Transforms/UnitarySynthesis/*.qkewith its RUN lines:
pre-existing ZYZ/KAK tests, the 3-qubit
random_unitary-5, and the CircuitCheck-only4-qubit/5-qubit
random_unitary-6/-7. Because the emitted IR is unchanged, everyFileCheck still matches and CircuitCheck still confirms unitary equivalence.
dim = 64(6-qubit) op and a non-unitarysupported-dimension op through
cudaq-opt --unitary-synthesisleaves thequake.custom_unitary_constantunchanged withreturncode 0(the dim-64 case prints theLLVM_DEBUGcap note; the non-unitary case prints the "matrix must be unitary" warning).No
signalPassFailure, no crash.clang-formatclean.AI usage disclosure
This change was prepared with AI assistance (Claude) for code refactoring, test execution,
and drafting; all changes were reviewed and verified locally by the author.