diff --git a/mlir/include/PauliFrame/CMakeLists.txt b/mlir/include/PauliFrame/CMakeLists.txt index f33061b2d8..9f57627c32 100644 --- a/mlir/include/PauliFrame/CMakeLists.txt +++ b/mlir/include/PauliFrame/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(IR) +add_subdirectory(Transforms) diff --git a/mlir/include/PauliFrame/Transforms/Passes.h b/mlir/include/PauliFrame/Transforms/Passes.h new file mode 100644 index 0000000000..05497cb721 --- /dev/null +++ b/mlir/include/PauliFrame/Transforms/Passes.h @@ -0,0 +1,27 @@ +// Copyright 2025 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "mlir/Pass/Pass.h" + +namespace catalyst { +namespace pauli_frame { + +#define GEN_PASS_DECL +#define GEN_PASS_REGISTRATION +#include "PauliFrame/Transforms/Passes.h.inc" + +} // namespace pauli_frame +} // namespace catalyst diff --git a/mlir/include/PauliFrame/Transforms/Passes.td b/mlir/include/PauliFrame/Transforms/Passes.td new file mode 100644 index 0000000000..b0f2b4ceb5 --- /dev/null +++ b/mlir/include/PauliFrame/Transforms/Passes.td @@ -0,0 +1,30 @@ +// Copyright 2025 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef PAULI_FRAME_PASSES +#define PAULI_FRAME_PASSES + +include "mlir/Pass/PassBase.td" + +def CliffordTToPauliFramePass : Pass<"to-pauli-frame"> { + let summary = "Apply the Pauli frame tracking protocols to a Clifford+T quantum program."; + + let dependentDialects = [ + "scf::SCFDialect", + "catalyst::quantum::QuantumDialect", + "catalyst::pauli_frame::PauliFrameDialect" + ]; +} + +#endif // PAULI_FRAME_PASSES diff --git a/mlir/include/PauliFrame/Transforms/Patterns.h b/mlir/include/PauliFrame/Transforms/Patterns.h new file mode 100644 index 0000000000..6f27154a07 --- /dev/null +++ b/mlir/include/PauliFrame/Transforms/Patterns.h @@ -0,0 +1,25 @@ +// Copyright 2025 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "mlir/IR/PatternMatch.h" + +namespace catalyst { +namespace pauli_frame { + +void populateCliffordTToPauliFramePatterns(mlir::RewritePatternSet &patterns); + +} // namespace pauli_frame +} // namespace catalyst diff --git a/mlir/include/RegisterAllPasses.h b/mlir/include/RegisterAllPasses.h index 496cc2f593..6980b36e09 100644 --- a/mlir/include/RegisterAllPasses.h +++ b/mlir/include/RegisterAllPasses.h @@ -19,6 +19,7 @@ #include "Ion/Transforms/Passes.h" #include "MBQC/Transforms/Passes.h" #include "Mitigation/Transforms/Passes.h" +#include "PauliFrame/Transforms/Passes.h" #include "QEC/Transforms/Passes.h" #include "Quantum/Transforms/Passes.h" #include "Test/Transforms/Passes.h" @@ -34,6 +35,7 @@ inline void registerAllPasses() ion::registerIonPasses(); mbqc::registerMBQCPasses(); mitigation::registerMitigationPasses(); + pauli_frame::registerPauliFramePasses(); qec::registerQECPasses(); quantum::registerQuantumPasses(); test::registerTestPasses(); diff --git a/mlir/lib/Driver/CMakeLists.txt b/mlir/lib/Driver/CMakeLists.txt index 3ffd467987..644828e4bd 100644 --- a/mlir/lib/Driver/CMakeLists.txt +++ b/mlir/lib/Driver/CMakeLists.txt @@ -42,6 +42,8 @@ set(LIBS mbqc-transforms MLIRMitigation mitigation-transforms + MLIRPauliFrame + pauli-frame-transforms MLIRIon ion-transforms MLIRRTIO diff --git a/mlir/lib/Driver/CompilerDriver.cpp b/mlir/lib/Driver/CompilerDriver.cpp index 6b53fec856..9c264fbe67 100644 --- a/mlir/lib/Driver/CompilerDriver.cpp +++ b/mlir/lib/Driver/CompilerDriver.cpp @@ -72,6 +72,7 @@ #include "Ion/IR/IonDialect.h" #include "MBQC/IR/MBQCDialect.h" #include "Mitigation/IR/MitigationDialect.h" +#include "PauliFrame/IR/PauliFrameDialect.h" #include "QEC/IR/QECDialect.h" #include "Quantum/IR/QuantumDialect.h" #include "Quantum/Transforms/BufferizableOpInterfaceImpl.h" @@ -380,6 +381,7 @@ void registerAllCatalystDialects(DialectRegistry ®istry) registry.insert(); registry.insert(); registry.insert(); + registry.insert(); } } // namespace diff --git a/mlir/lib/PauliFrame/CMakeLists.txt b/mlir/lib/PauliFrame/CMakeLists.txt index f33061b2d8..9f57627c32 100644 --- a/mlir/lib/PauliFrame/CMakeLists.txt +++ b/mlir/lib/PauliFrame/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(IR) +add_subdirectory(Transforms) diff --git a/mlir/lib/PauliFrame/Transforms/CMakeLists.txt b/mlir/lib/PauliFrame/Transforms/CMakeLists.txt new file mode 100644 index 0000000000..7556802b3c --- /dev/null +++ b/mlir/lib/PauliFrame/Transforms/CMakeLists.txt @@ -0,0 +1,27 @@ +set(LIBRARY_NAME pauli-frame-transforms) + + +file(GLOB SRC + CliffordTToPauliFramePatterns.cpp + to_pauli_frame.cpp +) + +get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) +get_property(conversion_libs GLOBAL PROPERTY MLIR_CONVERSION_LIBS) +set(LIBS + ${dialect_libs} + ${conversion_libs} + MLIRPauliFrame +) + +set(DEPENDS + MLIRPauliFramePassIncGen +) + +add_mlir_library(${LIBRARY_NAME} STATIC ${SRC} LINK_LIBS PRIVATE ${LIBS} DEPENDS ${DEPENDS}) +target_compile_features(${LIBRARY_NAME} PUBLIC cxx_std_20) + +target_include_directories(${LIBRARY_NAME} PUBLIC + . + ${PROJECT_SOURCE_DIR}/include + ${CMAKE_BINARY_DIR}/include) diff --git a/mlir/lib/PauliFrame/Transforms/CliffordTToPauliFramePatterns.cpp b/mlir/lib/PauliFrame/Transforms/CliffordTToPauliFramePatterns.cpp new file mode 100644 index 0000000000..cd6d900259 --- /dev/null +++ b/mlir/lib/PauliFrame/Transforms/CliffordTToPauliFramePatterns.cpp @@ -0,0 +1,350 @@ +// Copyright 2025 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "Quantum/IR/QuantumDialect.h" +#include "mlir/Dialect/SCF/IR/SCF.h" +#include "mlir/IR/BuiltinTypes.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/IR/SymbolTable.h" + +// #include "Catalyst/Utils/EnsureFunctionDeclaration.h" +#include "PauliFrame/IR/PauliFrameOps.h" +#include "PauliFrame/Transforms/Patterns.h" +#include "Quantum/IR/QuantumOps.h" +#include +#include + +using namespace mlir; + +namespace { + +using namespace catalyst::pauli_frame; +using namespace catalyst::quantum; + +//===----------------------------------------------------------------------===// +// Helper functions +//===----------------------------------------------------------------------===// + +enum class GateEnum { I, X, Y, Z, H, S, T, CNOT, Unknown }; + +// Hash gate name to GateEnum +GateEnum hashGate(CustomOp op) +{ + auto gateName = op.getGateName(); + if (gateName == "Identity" || gateName == "I") + return GateEnum::I; + else if (gateName == "PauliX" || gateName == "X") + return GateEnum::X; + else if (gateName == "PauliY" || gateName == "Y") + return GateEnum::Y; + else if (gateName == "PauliZ" || gateName == "Z") + return GateEnum::Z; + else if (gateName == "H" || gateName == "Hadamard") + return GateEnum::H; + else if (gateName == "S") + return GateEnum::S; + else if (gateName == "T") + return GateEnum::T; + else if (gateName == "CNOT") + return GateEnum::CNOT; + else + return GateEnum::Unknown; +} + +//===----------------------------------------------------------------------===// +// Gate-conversion functions +//===----------------------------------------------------------------------===// + +/** + * @brief Helper function to the Clifford+T -> PauliFrame pattern for Pauli gates (I, X, Y, Z). + * + * Performs the following rewrite, for example, from: + * + * %0 = ... : !quantum.bit + * %1 = quantum.custom "X"() %0 : !quantum.bit // or "I", "Y", "Z" + * %2 = %1 : ... + * + * to: + * + * %0 = ... : !quantum.bit + * %1 = pauli_frame.update[true, false] %0 : !quantum.bit // and similar for other Pauli gates + * %2 = %1 : ... + */ +LogicalResult convertPauliGate(CustomOp op, PatternRewriter &rewriter, bool x_parity, bool z_parity) +{ + auto loc = op->getLoc(); + auto outQubitTypes = op.getOutQubits().getTypes(); + auto inQubits = op.getInQubits(); + + UpdateOp updateOp = + rewriter.create(loc, outQubitTypes, rewriter.getBoolAttr(x_parity), + rewriter.getBoolAttr(z_parity), inQubits); + + rewriter.replaceOp(op, updateOp.getOutQubits()); + return success(); +} + +/** + * @brief Helper function to the Clifford+T -> PauliFrame pattern for Clifford gates (H, S, CNOT). + * + * Performs the following rewrite, for example, from: + * + * %0 = ... : !quantum.bit + * %1 = quantum.custom "Hadamard"() %0 : !quantum.bit + * %2 = %1 : ... + * + * to: + * + * %0 = ... : !quantum.bit + * %1 = pauli_frame.update_with_clifford[Hadamard] %0 : !quantum.bit + * %2 = quantum.custom "Hadamard"() %1 : !quantum.bit + * %3 = %2 : ... + */ +LogicalResult convertCliffordGate(CustomOp op, PatternRewriter &rewriter, CliffordGate gate) +{ + auto loc = op->getLoc(); + auto outQubitTypes = op.getOutQubits().getTypes(); + auto inQubits = op.getInQubits(); + + UpdateWithCliffordOp updateOp = + rewriter.create(loc, outQubitTypes, gate, inQubits); + + op->setOperands(updateOp->getResults()); + return success(); +} + +/** + * @brief Helper function to the Clifford+T -> PauliFrame pattern for non-Clifford gates (T). + * + * Performs the following rewrite, for example, from: + * + * %0 = ... : !quantum.bit + * %1 = quantum.custom "T"() %0 : !quantum.bit + * %2 = %1 : ... + * + * to: + * + * %0 = ... : !quantum.bit + * %x_parity, %z_parity, %out_qubit = pauli_frame.flush %0 : i1, i1, !quantum.bit + * %1 = scf.if %x_parity -> (!quantum.bit) { + * %out_qubits_0 = quantum.custom "X"() %out_qubit : !quantum.bit + * scf.yield %out_qubits_0 : !quantum.bit + * } else { + * scf.yield %out_qubit : !quantum.bit + * } + * %2 = scf.if %z_parity -> (!quantum.bit) { + * %out_qubits_0 = quantum.custom "Z"() %1 : !quantum.bit + * scf.yield %out_qubits_0 : !quantum.bit + * } else { + * scf.yield %1 : !quantum.bit + * } + * %3 = quantum.custom "T"() %2 : !quantum.bit + * %4 = %3 : ... + */ +LogicalResult convertNonCliffordGate(CustomOp op, PatternRewriter &rewriter) +{ + auto loc = op->getLoc(); + auto outQubitTypes = op.getOutQubits().getTypes(); + + if (outQubitTypes.size() > 1) { + op->emitError("Only single-qubit non-Clifford gates are supported"); + return failure(); + } + + auto outQubitType = outQubitTypes[0]; + auto inQubits = op.getInQubits(); + + FlushOp flushOp = rewriter.create(loc, rewriter.getI1Type(), rewriter.getI1Type(), + outQubitType, inQubits[0]); + + auto pauliXIfOp = rewriter.create( + loc, flushOp.getXParity(), + [&](OpBuilder &builder, Location loc) { // then + auto pauliX = rewriter.create(loc, "X", flushOp.getOutQubit()); + builder.create(loc, pauliX.getOutQubits()); + }, + [&](OpBuilder &builder, Location loc) { // else + builder.create(loc, flushOp.getOutQubit()); + }); + + auto pauliXOutQubit = pauliXIfOp->getResult(0); + + auto pauliZIfOp = rewriter.create( + loc, flushOp.getZParity(), + [&](OpBuilder &builder, Location loc) { // then + auto pauliZ = rewriter.create(loc, "Z", pauliXOutQubit); + builder.create(loc, pauliZ.getOutQubits()); + }, + [&](OpBuilder &builder, Location loc) { // else + builder.create(loc, pauliXOutQubit); + }); + + auto pauliZOutQubit = pauliZIfOp->getResult(0); + + op->setOperands(pauliZOutQubit); + + return success(); +} + +//===----------------------------------------------------------------------===// +// Clifford+T to Pauli Frame Patterns +//===----------------------------------------------------------------------===// + +/** + * @brief Rewrite pattern for Clifford+T ops -> PauliFrame + */ +struct CliffordTToPauliFramePattern : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(CustomOp op, PatternRewriter &rewriter) const override + { + auto op_enum = hashGate(op); + switch (op_enum) { + case GateEnum::I: + return convertPauliGate(op, rewriter, false, false); + case GateEnum::X: + return convertPauliGate(op, rewriter, true, false); + case GateEnum::Y: + return convertPauliGate(op, rewriter, true, true); + case GateEnum::Z: + return convertPauliGate(op, rewriter, false, true); + case GateEnum::H: + return convertCliffordGate(op, rewriter, CliffordGate::Hadamard); + case GateEnum::S: + return convertCliffordGate(op, rewriter, CliffordGate::S); + case GateEnum::CNOT: + return convertCliffordGate(op, rewriter, CliffordGate::CNOT); + case GateEnum::T: + return convertNonCliffordGate(op, rewriter); + case GateEnum::Unknown: { + op->emitError( + "Unsupported gate. Supported gates: I, X, Y, Z, H, S, S†, T, T†, and CNOT"); + return failure(); + } + } + return success(); + } +}; + +/** + * @brief Rewrite pattern for Pauli record initialization of a single qubit + * + * The Pauli records are initialized by inserting `pauli_frame.init` ops immediately after each + * single-qubit allocation op, `quantum.alloc_qb`, as follows, from: + * + * %0 = quantum.alloc_qb : !quantum.bit + * %1 = %0 : ... + * + * to: + * + * %0 = quantum.alloc_qb : !quantum.bit + * %1 = pauli_frame.init %0 + * %2 = %1 : ... + */ +struct InitPauliRecordQbitPattern : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(AllocQubitOp op, PatternRewriter &rewriter) const override + { + auto loc = op->getLoc(); + auto qubit = op.getQubit(); + + rewriter.setInsertionPointAfter(op); + InitOp initOp = rewriter.create(loc, qubit.getType(), qubit); + + qubit.replaceAllUsesExcept(initOp.getOutQubits()[0], initOp); + return success(); + } +}; + +/** + * @brief Rewrite pattern for Pauli record initialization of a quantum register + * + * The Pauli records are initialized by inserting `pauli_frame.init_qreg` ops immediately after each + * register allocation op, `quantum.alloc`, as follows, from: + * + * %0 = quantum.alloc( 1) : !quantum.reg + * %1 = %0 : ... + * + * to: + * + * %0 = quantum.alloc( 1) : !quantum.reg + * %1 = pauli_frame.init_qreg %0 : !quantum.reg + * %2 = %1 : ... + */ +struct InitPauliRecordQregPattern : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(AllocOp op, PatternRewriter &rewriter) const override + { + auto loc = op->getLoc(); + auto qreg = op.getQreg(); + + rewriter.setInsertionPointAfter(op); + InitQregOp initQregOp = rewriter.create(loc, qreg.getType(), qreg); + + qreg.replaceAllUsesExcept(initQregOp.getOutQreg(), initQregOp); + return success(); + } +}; + +/** + * @brief Rewrite pattern for measurement corrections + * + * Measurement results are corrected by inserting `pauli_frame.correct_measurement` ops immediately + * after each computational-basis mid-circuit measurement op, `quantum.measure`, as follows, from: + * + * %0 = ... : !quantum.bit + * %mres, %1 = quantum.measure %0 : i1, !quantum.bit + * + * to: + * + * %0 = ... : !quantum.bit + * %mres, %1 = quantum.measure %0 : i1, !quantum.bit + * %mres_1, %2 = pauli_frame.correct_measurement %mres, %1 : i1, !quantum.bit + */ +struct CorrectMeasurementPattern : public OpRewritePattern { + using OpRewritePattern::OpRewritePattern; + + LogicalResult matchAndRewrite(MeasureOp op, PatternRewriter &rewriter) const override + { + auto loc = op->getLoc(); + auto mres = op.getMres(); + auto outQubit = op.getOutQubit(); + + rewriter.setInsertionPointAfter(op); + CorrectMeasurementOp correctMeasOp = rewriter.create( + loc, mres.getType(), outQubit.getType(), mres, outQubit); + + mres.replaceAllUsesExcept(correctMeasOp.getOutMres(), correctMeasOp); + outQubit.replaceAllUsesExcept(correctMeasOp.getOutQubit(), correctMeasOp); + return success(); + } +}; + +} // namespace + +namespace catalyst { +namespace pauli_frame { + +void populateCliffordTToPauliFramePatterns(RewritePatternSet &patterns) +{ + patterns.add(patterns.getContext()); + patterns.add(patterns.getContext()); + patterns.add(patterns.getContext()); + patterns.add(patterns.getContext()); +} + +} // namespace pauli_frame +} // namespace catalyst diff --git a/mlir/lib/PauliFrame/Transforms/to_pauli_frame.cpp b/mlir/lib/PauliFrame/Transforms/to_pauli_frame.cpp new file mode 100644 index 0000000000..13fb34dd31 --- /dev/null +++ b/mlir/lib/PauliFrame/Transforms/to_pauli_frame.cpp @@ -0,0 +1,58 @@ +// Copyright 2025 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#define DEBUG_TYPE "to-pauli-frame" + +#include "mlir/Dialect/SCF/IR/SCF.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/WalkPatternRewriteDriver.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/LogicalResult.h" + +#include "PauliFrame/IR/PauliFrameOps.h" +#include "PauliFrame/Transforms/Patterns.h" + +using namespace llvm; +using namespace mlir; + +namespace catalyst { +namespace pauli_frame { + +#define GEN_PASS_DECL_CLIFFORDTTOPAULIFRAMEPASS +#define GEN_PASS_DEF_CLIFFORDTTOPAULIFRAMEPASS +#include "PauliFrame/Transforms/Passes.h.inc" + +struct CliffordTToPauliFramePass : impl::CliffordTToPauliFramePassBase { + using CliffordTToPauliFramePassBase::CliffordTToPauliFramePassBase; + + void runOnOperation() final + { + LLVM_DEBUG(dbgs() << "Clifford+T to Pauli frame pass\n"); + + Operation *module = getOperation(); + + RewritePatternSet patterns(&getContext()); + populateCliffordTToPauliFramePatterns(patterns); + + // NOTE: We want the walk-based pattern rewrite driver here, and not a greedy rewriter like + // applyPatternsGreedily, since we match on ops and do not replace them, therefore a greedy + // rewriter would loop infinitely. + walkAndApplyPatterns(module, std::move(patterns)); + } +}; + +} // namespace pauli_frame +} // namespace catalyst diff --git a/mlir/test/PauliFrame/CliffordTToPauliFrame.mlir b/mlir/test/PauliFrame/CliffordTToPauliFrame.mlir new file mode 100644 index 0000000000..7eef095ae5 --- /dev/null +++ b/mlir/test/PauliFrame/CliffordTToPauliFrame.mlir @@ -0,0 +1,162 @@ +// Copyright 2025 Xanadu Quantum Technologies Inc. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// RUN: quantum-opt --to-pauli-frame --split-input-file --verify-diagnostics %s | FileCheck %s + +// CHECK-LABEL: test_to_pauli_frame_pauli_gates +func.func @test_to_pauli_frame_pauli_gates(%arg0 : !quantum.bit) -> !quantum.bit { + // CHECK: [[q1:%.+]] = pauli_frame.update{{\s*}}[false, false] %arg0 + %q1 = quantum.custom "I"() %arg0 : !quantum.bit + // CHECK: [[q2:%.+]] = pauli_frame.update{{\s*}}[true, false] [[q1]] + %q2 = quantum.custom "X"() %q1 : !quantum.bit + // CHECK: [[q3:%.+]] = pauli_frame.update{{\s*}}[true, true] [[q2]] + %q3 = quantum.custom "Y"() %q2 : !quantum.bit + // CHECK: [[q4:%.+]] = pauli_frame.update{{\s*}}[false, true] [[q3]] + %q4 = quantum.custom "Z"() %q3 : !quantum.bit + // CHECK-NOT: quantum.custom + // CHECK: return [[q4]] + func.return %q4 : !quantum.bit +} + +// ----- + +// CHECK-LABEL: test_to_pauli_frame_clifford_gates_single_qubit +func.func @test_to_pauli_frame_clifford_gates_single_qubit(%arg0 : !quantum.bit) -> !quantum.bit { + // CHECK: [[q1a:%.+]] = pauli_frame.update_with_clifford[ Hadamard] %arg0 + // CHECK-NEXT: [[q1b:%.+]] = quantum.custom "Hadamard"() [[q1a]] + %q1 = quantum.custom "Hadamard"() %arg0 : !quantum.bit + // CHECK: [[q2a:%.+]] = pauli_frame.update_with_clifford[ S] [[q1b]] + // CHECK-NEXT: [[q2b:%.+]] = quantum.custom "S"() [[q2a]] + %q2 = quantum.custom "S"() %q1 : !quantum.bit + // CHECK: return [[q2b]] + func.return %q2 : !quantum.bit +} + +// ----- + +// CHECK-LABEL: test_to_pauli_frame_clifford_gates_two_qubit +func.func @test_to_pauli_frame_clifford_gates_two_qubit(%arg0 : !quantum.bit, %arg1 : !quantum.bit) -> (!quantum.bit, !quantum.bit) { + // CHECK: [[q1a:%.+]]:2 = pauli_frame.update_with_clifford[ CNOT] %arg0, %arg1 + // CHECK-NEXT: [[q1b:%.+]]:2 = quantum.custom "CNOT"() [[q1a]]#0, [[q1a]]#1 + %q1:2 = quantum.custom "CNOT"() %arg0, %arg1 : !quantum.bit, !quantum.bit + // CHECK: return [[q1b]]#0, [[q1b]]#1 + func.return %q1#0, %q1#1 : !quantum.bit, !quantum.bit +} + +// ----- + +// CHECK-LABEL: test_to_pauli_frame_non_clifford_gate +func.func @test_to_pauli_frame_non_clifford_gate(%arg0 : !quantum.bit) -> (!quantum.bit) { + // CHECK: [[xbit:%.+]], [[zbit:%.+]], [[q1:%.+]] = pauli_frame.flush %arg0 + // CHECK: [[q2:%.+]] = scf.if [[xbit]] {{.*}} { + // CHECK: [[x_outq:%.+]] = quantum.custom "X"() [[q1]] + // CHECK: yield [[x_outq]] + // CHECK: } else { + // CHECK: yield [[q1]] + // CHECK: } + // CHECK: [[q3:%.+]] = scf.if [[zbit]] {{.*}} { + // CHECK: [[z_outq:%.+]] = quantum.custom "Z"() [[q2]] + // CHECK: yield [[z_outq]] + // CHECK: } else { + // CHECK: yield [[q2]] + // CHECK: } + // CHECK: [[q4:%.+]] = quantum.custom "T"() [[q3]] + %q1 = quantum.custom "T"() %arg0 : !quantum.bit + // CHECK: return [[q4]] + func.return %q1 : !quantum.bit +} + +// ----- + +func.func @test_to_pauli_frame_init_qubit() -> !quantum.bit { + // CHECK: [[q1:%.+]] = quantum.alloc_qb + // CHECK-NEXT: [[q2:%.+]] = pauli_frame.init [[q1]] + %q = quantum.alloc_qb : !quantum.bit + // return [[q2]] + func.return %q : !quantum.bit +} + +// ----- + +func.func @test_to_pauli_frame_init_qreg() { + // CHECK: [[qreg1:%.+]] = quantum.alloc( 1) + // CHECK-NEXT: [[qreg2:%.+]] = pauli_frame.init_qreg [[qreg1]] + // CHECK-NEXT: quantum.extract [[qreg2]][ 0] + %qreg = quantum.alloc( 1) : !quantum.reg + %q = quantum.extract %qreg[ 0] : !quantum.reg -> !quantum.bit + func.return +} + +// ----- + +func.func @test_to_pauli_frame_correct_meas(%arg0 : !quantum.bit) -> (i1, !quantum.bit) { + // CHECK: [[mres1:%.+]], [[q1:%.+]] = quantum.measure %arg0 + // CHECK-NEXT: [[mres2:%.+]], [[q2:%.+]] = pauli_frame.correct_measurement [[mres1]], [[q1]] + %mres, %q = quantum.measure %arg0 : i1, !quantum.bit + // CHECK: return [[mres2]], [[q2]] + func.return %mres, %q : i1, !quantum.bit +} + +// ----- + +// COM: This program represents the following circuit: +// COM: 0: ──H──X─╭●──T──┤↗├─┤ +// COM: 1: ──S──Z─╰X──Y──┤↗├─┤ +func.func @test_to_pauli_frame_integration() -> (i1, i1) { + // CHECK: quantum.alloc( 2) : !quantum.reg + // CHECK: pauli_frame.init_qreg {{%.+}} : !quantum.reg + %qreg = quantum.alloc( 2) : !quantum.reg + // CHECK: quantum.extract {{%.+}}[ 0] : !quantum.reg -> !quantum.bit + // CHECK: quantum.extract {{%.+}}[ 1] : !quantum.reg -> !quantum.bit + %q00 = quantum.extract %qreg[ 0] : !quantum.reg -> !quantum.bit + %q10 = quantum.extract %qreg[ 1] : !quantum.reg -> !quantum.bit + // CHECK: pauli_frame.update_with_clifford[ Hadamard] {{%.+}} : !quantum.bit + // CHECK: quantum.custom "Hadamard"() {{%.+}} : !quantum.bit + %q01 = quantum.custom "Hadamard"() %q00 : !quantum.bit + // CHECK: pauli_frame.update_with_clifford[ S] {{%.+}} : !quantum.bit + // CHECK: quantum.custom "S"() {{%.+}} : !quantum.bit + %q11 = quantum.custom "S"() %q10 : !quantum.bit + // CHECK: pauli_frame.update[true, false] {{%.+}} : !quantum.bit + %q02 = quantum.custom "X"() %q01 : !quantum.bit + // CHECK: pauli_frame.update[false, true] {{%.+}} : !quantum.bit + %q12 = quantum.custom "Z"() %q11 : !quantum.bit + // CHECK: pauli_frame.update_with_clifford[ CNOT] {{%.+}}, {{%.+}} : !quantum.bit, !quantum.bit + // CHECK: quantum.custom "CNOT"() {{%.+}}, {{%.+}} : !quantum.bit, !quantum.bit + %q03, %q13 = quantum.custom "CNOT"() %q02, %q12 : !quantum.bit, !quantum.bit + // CHECK: pauli_frame.flush {{%.+}} : i1, i1, !quantum.bit + // CHECK: scf.if {{%.+}} -> (!quantum.bit) { + // CHECK: quantum.custom "X"() {{%.+}} : !quantum.bit + // CHECK: scf.yield {{%.+}} : !quantum.bit + // CHECK: } else { + // CHECK: scf.yield {{%.+}} : !quantum.bit + // CHECK: } + // CHECK: scf.if {{%.+}} -> (!quantum.bit) { + // CHECK: quantum.custom "Z"() {{%.+}} : !quantum.bit + // CHECK: scf.yield {{%.+}} : !quantum.bit + // CHECK: } else { + // CHECK: scf.yield {{%.+}} : !quantum.bit + // CHECK: } + // CHECK: quantum.custom "T"() {{%.+}} : !quantum.bit + %q04 = quantum.custom "T"() %q03 : !quantum.bit + // CHECK: pauli_frame.update[true, true] {{%.+}} : !quantum.bit + %q14 = quantum.custom "Y"() %q13 : !quantum.bit + // CHECK: quantum.measure {{%.+}} : i1, !quantum.bit + // CHECK: pauli_frame.correct_measurement {{%.+}}, {{%.+}} : i1, !quantum.bit + %mres0, %q05 = quantum.measure %q04 : i1, !quantum.bit + // CHECK: quantum.measure {{%.+}} : i1, !quantum.bit + // CHECK: pauli_frame.correct_measurement {{%.+}}, {{%.+}} : i1, !quantum.bit + %mres1, %q15 = quantum.measure %q14 : i1, !quantum.bit + // CHECK: return {{%.+}}, {{%.+}} : i1, i1 + func.return %mres0, %mres1 : i1, i1 +} \ No newline at end of file diff --git a/mlir/tools/catalyst-cli/CMakeLists.txt b/mlir/tools/catalyst-cli/CMakeLists.txt index caa6754285..39bd44ecf4 100644 --- a/mlir/tools/catalyst-cli/CMakeLists.txt +++ b/mlir/tools/catalyst-cli/CMakeLists.txt @@ -39,6 +39,7 @@ set(LIBS MLIRMitigation mitigation-transforms MLIRPauliFrame + pauli-frame-transforms MLIRIon ion-transforms MLIRRTIO diff --git a/mlir/tools/quantum-opt/CMakeLists.txt b/mlir/tools/quantum-opt/CMakeLists.txt index f9d08252a1..3fc81bda36 100644 --- a/mlir/tools/quantum-opt/CMakeLists.txt +++ b/mlir/tools/quantum-opt/CMakeLists.txt @@ -23,6 +23,7 @@ set(LIBS MLIRMitigation mitigation-transforms MLIRPauliFrame + pauli-frame-transforms MLIRIon ion-transforms MLIRRTIO