Support decomposition to/from Adjoint Ops to the GraphSolver - #3001
Support decomposition to/from Adjoint Ops to the GraphSolver#3001maliasadi wants to merge 8 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3001 +/- ##
=======================================
Coverage 96.99% 96.99%
=======================================
Files 165 165
Lines 19091 19091
Branches 1816 1816
=======================================
Hits 18518 18518
Misses 420 420
Partials 153 153 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
| * - AdjointGenerated: A rule synthesized by adjointing a base decomposition rule. | ||
| */ | ||
| enum class RuleOrigin : uint8_t { Default = 0, Fixed = 1, Alternative = 2 }; | ||
| enum class RuleOrigin : uint8_t { Default = 0, Fixed = 1, Alternative = 2, AdjointGenerated = 3 }; |
There was a problem hiding this comment.
Should we differentiate between adjoint genereated from default, adjoint generated from fixed and adjoint generated from alternative?
There was a problem hiding this comment.
No we don't need to consider those cases. Fixed/Alternative are consumed by the builder to enforced which rules exist for an op -- the solver works without knowing about the origin of these rule! AdjointGenerated is also consumed by the builder but it's needed in the solver as the solver needs to consider both pathways (in the ADR) and to propagate rules that are built by the solver using makeAdjointRule (Pathway 2 in the ADR).
| { | ||
| RuleNode adj; | ||
| adj.name = base.name + "_adjoint"; | ||
| adj.output = makeAdjoint(base.output); |
There was a problem hiding this comment.
Do we have to worry about this in-place mutating base.output?
There was a problem hiding this comment.
No. makeAdjoint takes operators by "value"; it operates on a copy and returns it so the original op remains untouched.
180d5cb to
44cdf32
Compare
|
Rebased to include #3046 changes in the solver |
Context:
The decomposition graph solver could not decompose Adjoint ops. This PR adds this support to the graph solver to handle them, mirroring the two decomposition families used by PL:
Decompose
Adjoint(Op)directly: Rules registered against the adjoint operator itself:self_adjointandadjoint_rotationfuncs in PL. and other custom rules like_adjoint_rot, etc. These were already supported by the graph. ARuleNodewhose output hasadjoint = trueis registered and solved like any other rule; so no new logic was needed to the solver; they just need to be provided as rules. I'll take care of this in a follow-up PR to integrate the support with the graph-decomposition pass.Decompose Op, then adjoint each produced gate: From each base rule
Op, the graph builder should synthesizeAdjoint(Op)with the same multiplicities. I assumed that gate-order reversal doesn't affect resources, so the graph only needs theAdjoint(Op)counts; the actual reversal happens in the follow-up PR when integrating with the pass.Both pathways coexist in the graph, and the solver picks the cheapest decomp pathway! 🎉
Benefits:
Possible Drawbacks:
Adjoint(Op)and track them all throughout multiple decomp pathways.Related GitHub Issues:
[sc-125400]