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 +}