Skip to content

Commit eb732f0

Browse files
authored
Add adjoint lowering pattern for PPR op (#2227)
**Context:** When a user places a PPR op in an adjoint qfunc (see #2145 ), the `--adjoint-lowering` pass will not correctly distribute the adjoint into the region onto PPRs, because the pass does not recognize PPRs. **Description of the Change:** Add pattern for PPR ops in adjoint lowering pass. **Benefits:** Correct behavior **Possible Drawbacks:** None
1 parent c86dbe9 commit eb732f0

File tree

4 files changed

+60
-0
lines changed

4 files changed

+60
-0
lines changed

doc/releases/changelog-dev.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
* Dynamically allocated wires can now be passed into control flow and subroutines.
5151
[(#2130)](https://github.com/PennyLaneAI/catalyst/pull/2130)
5252

53+
* The `--adjoint-lowering` pass can now handle PPR operations.
54+
[(#2227)](https://github.com/PennyLaneAI/catalyst/pull/2227)
55+
5356
<h3>Breaking changes 💔</h3>
5457

5558
* The plxpr transform `pl_map_wires` has been removed along with its test.

mlir/lib/Quantum/Transforms/AdjointPatterns.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "mlir/Transforms/DialectConversion.h"
3535

3636
#include "Catalyst/IR/CatalystOps.h"
37+
#include "QEC/IR/QECDialect.h"
3738
#include "Quantum/IR/QuantumInterfaces.h"
3839
#include "Quantum/IR/QuantumOps.h"
3940
#include "Quantum/Transforms/Patterns.h"
@@ -116,6 +117,9 @@ class AdjointGenerator {
116117
else if (auto gate = dyn_cast<quantum::QuantumGate>(op)) {
117118
visitOperation(gate, builder);
118119
}
120+
else if (auto ppr = dyn_cast<qec::PPRotationOp>(op)) {
121+
visitOperation(ppr, builder);
122+
}
119123
else if (auto adjointOp = dyn_cast<quantum::AdjointOp>(&op)) {
120124
BlockArgument regionArg = adjointOp.getRegion().getArgument(0);
121125
Value result = adjointOp.getResult();
@@ -273,6 +277,22 @@ class AdjointGenerator {
273277
}
274278
}
275279

280+
void visitOperation(qec::PPRotationOp ppr, OpBuilder &builder)
281+
{
282+
for (const auto &[qubitResult, qubitOperand] :
283+
llvm::zip(ppr.getOutQubits(), ppr.getInQubits())) {
284+
remappedValues.map(qubitOperand, remappedValues.lookup(qubitResult));
285+
}
286+
287+
auto clone = cast<qec::PPRotationOp>(builder.clone(*ppr, remappedValues));
288+
clone.setRotationKind(ppr.getRotationKind() * (-1));
289+
290+
for (const auto &[qubitResult, qubitOperand] :
291+
llvm::zip(clone.getOutQubits(), ppr.getInQubits())) {
292+
remappedValues.map(qubitOperand, qubitResult);
293+
}
294+
}
295+
276296
void visitOperation(func::CallOp callOp, OpBuilder &builder)
277297
{
278298
// Get the the original function

mlir/lib/Quantum/Utils/QuantumSplitting.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "mlir/Transforms/DialectConversion.h"
2323

2424
#include "Catalyst/IR/CatalystOps.h"
25+
#include "QEC/IR/QECDialect.h"
2526
#include "Quantum/IR/QuantumOps.h"
2627
#include "Quantum/Utils/QuantumSplitting.h"
2728

@@ -204,6 +205,9 @@ void AugmentedCircuitGenerator::generate(Region &region, OpBuilder &builder)
204205
else if (isa<QuantumDialect>(op.getDialect())) {
205206
// Any quantum op other than a parametrized gate/insert/extract is ignored.
206207
}
208+
else if (isa<qec::PPRotationOp>(op)) {
209+
// PPRs are ignored
210+
}
207211
else if (isClassicalSCFOp(op)) {
208212
// Purely classical SCF ops should be treated as any other purely classical op, but
209213
// quantum SCF ops need to be recursively visited.

mlir/test/QEC/AdjointTest.mlir

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2025 Xanadu Quantum Technologies Inc.
2+
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// RUN: quantum-opt --adjoint-lowering --split-input-file -verify-diagnostics %s | FileCheck %s
16+
17+
// CHECK-LABEL: @workflow
18+
func.func private @workflow(%r: !quantum.reg) -> !quantum.reg attributes {} {
19+
// CHECK-NOT: quantum.adjoint
20+
%r_out = quantum.adjoint(%r) : !quantum.reg {
21+
^bb0(%arg0: !quantum.reg):
22+
%0 = quantum.extract %arg0[0] : !quantum.reg -> !quantum.bit
23+
24+
// CHECK: qec.ppr ["Y"](4)
25+
// CHECK: qec.ppr ["X"](-2)
26+
%1 = qec.ppr ["X"](2) %0 : !quantum.bit
27+
%2 = qec.ppr ["Y"](-4) %1 : !quantum.bit
28+
29+
%3 = quantum.insert %arg0[0], %2 : !quantum.reg, !quantum.bit
30+
quantum.yield %3 : !quantum.reg
31+
}
32+
return %r_out : !quantum.reg
33+
}

0 commit comments

Comments
 (0)