diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 20f2e46918..6045a8900a 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -178,6 +178,17 @@
Bug fixes 🐛
+* The `adjoint-lowering` pass now raises a clear error when a `quantum.adjoint` region contains an
+ unsupported operation that produces or consumes quantum values, including operations from
+ non-Quantum dialects, instead of failing with a raw compiler assertion.
+ [(#2934)](https://github.com/PennyLaneAI/catalyst/issues/2934)
+
+* The `ppr-to-ppm` pass now raises a clear error when its input contains a `quantum.adjoint`
+ region, instead of failing later with a raw compiler assertion. The generated Pauli product
+ measurements cannot be reversed by the `adjoint-lowering` pass, so this input is now rejected
+ up front.
+ [(#2934)](https://github.com/PennyLaneAI/catalyst/pull/2934)
+
* Fixed a bug where the `ResourceAnalysis` pass only analyzed functions directly contained in
the top-level module. Functions inside nested modules, such as kernels called through
`catalyst.launch_kernel`, are now included in the output.
diff --git a/mlir/lib/PBC/Transforms/ppr_to_ppm.cpp b/mlir/lib/PBC/Transforms/ppr_to_ppm.cpp
index d7d75e4ca6..e93ad32a9f 100644
--- a/mlir/lib/PBC/Transforms/ppr_to_ppm.cpp
+++ b/mlir/lib/PBC/Transforms/ppr_to_ppm.cpp
@@ -20,6 +20,7 @@
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
#include "PBC/Transforms/Patterns.h"
+#include "Quantum/IR/QuantumOps.h"
using namespace llvm;
using namespace mlir;
@@ -41,6 +42,20 @@ struct PPRToPPMPass : public impl::PPRToPPMPassBase {
auto ctx = &getContext();
auto module = getOperation();
+ // The ppr-to-ppm conversion generates Pauli product measurement
+ // (pbc.ppm) ops, which cannot be reversed by the adjoint-lowering pass.
+ // Reject input inside a quantum.adjoint region up front with a clear
+ // error, rather than failing later with a raw IRMapping assertion.
+ WalkResult adjointCheck = module->walk([&](catalyst::quantum::AdjointOp adjointOp) {
+ adjointOp.emitError() << "ppr-to-ppm cannot be applied to operations inside a "
+ "'quantum.adjoint' region, because the resulting Pauli "
+ "product measurements cannot be adjoint-lowered";
+ return WalkResult::interrupt();
+ });
+ if (adjointCheck.wasInterrupted()) {
+ return signalPassFailure();
+ }
+
RewritePatternSet non_clifford_patterns(ctx);
populateDecomposeNonCliffordPPRPatterns(non_clifford_patterns, decomposeMethod,
avoidYMeasure);
diff --git a/mlir/lib/Quantum/Transforms/AdjointLowering/ReversePass.cpp b/mlir/lib/Quantum/Transforms/AdjointLowering/ReversePass.cpp
index bee112f332..34cac2869f 100644
--- a/mlir/lib/Quantum/Transforms/AdjointLowering/ReversePass.cpp
+++ b/mlir/lib/Quantum/Transforms/AdjointLowering/ReversePass.cpp
@@ -184,10 +184,13 @@ class AdjointGenerator {
remappedValues.map(operand, reversedResult);
}
}
- else if (isa(op.getDialect())) {
- op.emitError("Unhandled operation in adjoint region");
- generationFailed = true;
- return;
+ else {
+ if (!getQuantumValues(op.getOperands()).empty() ||
+ !getQuantumValues(op.getResults()).empty()) {
+ op.emitError("cannot be adjoint-lowered inside a quantum.adjoint region");
+ generationFailed = true;
+ return;
+ }
}
}
}
diff --git a/mlir/test/PBC/AdjointTest.mlir b/mlir/test/PBC/AdjointTest.mlir
index c94bdc47e2..5389075cb6 100644
--- a/mlir/test/PBC/AdjointTest.mlir
+++ b/mlir/test/PBC/AdjointTest.mlir
@@ -31,3 +31,19 @@ func.func private @workflow(%r: !quantum.reg) -> !quantum.reg attributes {} {
}
return %r_out : !quantum.reg
}
+
+// -----
+
+func.func @workflow_unhandled_ppm() {
+ %0 = quantum.alloc(1) : !quantum.reg
+ %1 = quantum.adjoint (%0) : !quantum.reg {
+ ^bb0(%arg0: !quantum.reg):
+ %qb = quantum.extract %arg0[0] : !quantum.reg -> !quantum.bit
+ // expected-error@+1 {{'pbc.ppm' op cannot be adjoint-lowered inside a quantum.adjoint region}}
+ %mres, %out = pbc.ppm ["Z"] %qb : i1, !quantum.bit
+ %inserted = quantum.insert %arg0[0], %out : !quantum.reg, !quantum.bit
+ quantum.yield %inserted : !quantum.reg
+ }
+ quantum.dealloc %1 : !quantum.reg
+ return
+}
diff --git a/mlir/test/PBC/PPRToPPM.mlir b/mlir/test/PBC/PPRToPPM.mlir
index cefb8e3977..505df8284e 100644
--- a/mlir/test/PBC/PPRToPPM.mlir
+++ b/mlir/test/PBC/PPRToPPM.mlir
@@ -104,3 +104,20 @@ func.func @test_ppr_to_ppm_with_condition(%q0 : !quantum.bit, %q1 : !quantum.bit
// CHECK: scf.yield [[arg1]], [[arg2]], [[arg3]]
// CHECK: }
}
+
+// -----
+
+// ppr-to-ppm generates Pauli product measurements, which cannot be reversed by
+// the adjoint-lowering pass, so a quantum.adjoint region in the input must be
+// rejected up front with a clear error instead of asserting later.
+func.func @test_ppr_to_ppm_rejects_adjoint(%r: !quantum.reg) -> !quantum.reg {
+ // expected-error@+1 {{ppr-to-ppm cannot be applied to operations inside a 'quantum.adjoint' region}}
+ %r_out = quantum.adjoint(%r) : !quantum.reg {
+ ^bb0(%arg0: !quantum.reg):
+ %0 = quantum.extract %arg0[0] : !quantum.reg -> !quantum.bit
+ %1 = pbc.ppr ["Z"](4) %0 : !quantum.bit
+ %2 = quantum.insert %arg0[0], %1 : !quantum.reg, !quantum.bit
+ quantum.yield %2 : !quantum.reg
+ }
+ return %r_out : !quantum.reg
+}