From eacfe864cb5abe4064a81d268487eebcbdbbb797 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 30 May 2025 16:38:18 -0400 Subject: [PATCH 01/30] test new print spec pass --- frontend/catalyst/passes/__init__.py | 2 + frontend/catalyst/passes/builtin_passes.py | 4 ++ mlir/include/QEC/Transforms/Passes.h | 1 + mlir/include/QEC/Transforms/Passes.td | 10 +++ .../Catalyst/Transforms/RegisterAllPasses.cpp | 1 + mlir/lib/QEC/Transforms/CMakeLists.txt | 1 + mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 61 +++++++++++++++++++ mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp | 1 + 8 files changed, 81 insertions(+) create mode 100644 mlir/lib/QEC/Transforms/CountPPMSpecs.cpp diff --git a/frontend/catalyst/passes/__init__.py b/frontend/catalyst/passes/__init__.py index 120e1cea86..7fcb5dbd66 100644 --- a/frontend/catalyst/passes/__init__.py +++ b/frontend/catalyst/passes/__init__.py @@ -42,6 +42,7 @@ ppm_compilation, ppr_to_ppm, to_ppr, + ppm_specs ) from catalyst.passes.pass_api import Pass, PassPlugin, apply_pass, apply_pass_plugin @@ -58,4 +59,5 @@ "merge_ppr_ppm", "ppr_to_ppm", "ppm_compilation", + "ppm_specs" ) diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index eef3cb625a..93d759789f 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -781,3 +781,7 @@ def circuit(): ) return PassPipelineWrapper(qnode, passes) + + +def ppm_specs(qnode): + return PassPipelineWrapper(qnode, "ppm_specs") diff --git a/mlir/include/QEC/Transforms/Passes.h b/mlir/include/QEC/Transforms/Passes.h index 64ca33dbbe..29c95a8ad8 100644 --- a/mlir/include/QEC/Transforms/Passes.h +++ b/mlir/include/QEC/Transforms/Passes.h @@ -29,5 +29,6 @@ std::unique_ptr createCommuteCliffordPastPPMPass(); std::unique_ptr createDecomposeNonCliffordPPRPass(); std::unique_ptr createDecomposeCliffordPPRPass(); std::unique_ptr createCliffordTToPPMPass(); +std::unique_ptr createCountPPMSpecsPass(); } // namespace catalyst diff --git a/mlir/include/QEC/Transforms/Passes.td b/mlir/include/QEC/Transforms/Passes.td index 894963a32f..ab2b14d58b 100644 --- a/mlir/include/QEC/Transforms/Passes.td +++ b/mlir/include/QEC/Transforms/Passes.td @@ -121,4 +121,14 @@ def CliffordTToPPMPass : Pass<"ppm_compilation"> { let options = [MaxPauliSizeOption, DecomposeMethodOption, AvoidYMeasureOption]; } +def createCountPPMSpecsPass : Pass<"ppm_specs"> { + let summary = "Convert CliffordT operations to Pauli Product Measurement operations."; + + let dependentDialects = [ + "catalyst::qec::QECDialect", + ]; + + let constructor = "catalyst::createCountPPMSpecsPass()"; +} + #endif // QEC_PASSES diff --git a/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp b/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp index 56f11d9ff3..24062f84f5 100644 --- a/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp +++ b/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp @@ -37,6 +37,7 @@ void catalyst::registerAllCatalystPasses() mlir::registerPass(catalyst::createCliffordTToPPMPass); mlir::registerPass(catalyst::createDecomposeNonCliffordPPRPass); mlir::registerPass(catalyst::createDecomposeCliffordPPRPass); + mlir::registerPass(catalyst::createCountPPMSpecsPass) mlir::registerPass(catalyst::createDetensorizeSCFPass); mlir::registerPass(catalyst::createDisableAssertionPass); mlir::registerPass(catalyst::createDisentangleCNOTPass); diff --git a/mlir/lib/QEC/Transforms/CMakeLists.txt b/mlir/lib/QEC/Transforms/CMakeLists.txt index 15ace379a1..56febea807 100644 --- a/mlir/lib/QEC/Transforms/CMakeLists.txt +++ b/mlir/lib/QEC/Transforms/CMakeLists.txt @@ -8,6 +8,7 @@ file(GLOB SRC commute_clifford_t_ppr.cpp CommuteCliffordPastPPM.cpp commute_clifford_past_ppm.cpp + CountPPMSpecs.cpp decompose_non_clifford_ppr.cpp DecomposeNonCliffordPPR.cpp decompose_clifford_ppr.cpp diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp new file mode 100644 index 0000000000..dab65d2218 --- /dev/null +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -0,0 +1,61 @@ +#define DEBUG_TYPE "to_ppr" + +#include "mlir/IR/PatternMatch.h" +#include "mlir/Pass/Pass.h" + +#include "QEC/IR/QECDialect.h" +#include "QEC/Transforms/Patterns.h" +#include "Quantum/IR/QuantumOps.h" + +using namespace mlir; +using namespace catalyst::quantum; +using namespace catalyst::qec; + +namespace catalyst { +namespace qec { + +#define GEN_PASS_DEF_COUNTPPMSPECSPASS +#define GEN_PASS_DECL_COUNTPPMSPECSPASS +#include "QEC/Transforms/Passes.h.inc" + + +struct CountPPMSpecsPass : impl::CountPPMSpecsPassBase { + using CountPPMSpecsPassBase::CountPPMSpecsPassBase; + + void runOnOperation() final + { + auto ctx = &getContext(); + auto module = getOperation(); + + // Phase 1: Convert Clifford+T to PPR representation + { + ConversionTarget target(*ctx); + target.addIllegalDialect(); + target.addLegalOp(); + target.addLegalOp(); + target.addLegalOp(); + target.addLegalOp(); + target.addLegalDialect(); + + RewritePatternSet patterns(ctx); + populateCliffordTToPPRPatterns(patterns); + + if (failed(applyPartialConversion(module, target, std::move(patterns)))) { + return signalPassFailure(); + } + else { + llvm::outs() << "TEST for Count PPM Specs\n"; + } + } + } +}; + +} // namespace qec + +/// Create a pass for lowering operations in the `QECDialect`. +std::unique_ptr createCountPPMSpecsPass() +{ + return std::make_unique(); +} + +} // namespace catalyst diff --git a/mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp b/mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp index 9fe01dafce..3586211a77 100644 --- a/mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp +++ b/mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp @@ -50,6 +50,7 @@ struct CliffordTToPPRPass : impl::CliffordTToPPRPassBase { RewritePatternSet patterns(ctx); populateCliffordTToPPRPatterns(patterns); + llvm::outs() << "TEST\n"; if (failed(applyPartialConversion(getOperation(), target, std::move(patterns)))) { return signalPassFailure(); From 24e0afdf1d3a0cef64336d822396addca39d29cf Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 30 May 2025 17:50:33 -0400 Subject: [PATCH 02/30] fix some descriptions --- frontend/catalyst/passes/pass_api.py | 1 + mlir/include/QEC/Transforms/Passes.td | 2 +- .../Catalyst/Transforms/RegisterAllPasses.cpp | 2 +- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 30 +++++++++++++++---- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/frontend/catalyst/passes/pass_api.py b/frontend/catalyst/passes/pass_api.py index 5a69045c49..9dd8a5873e 100644 --- a/frontend/catalyst/passes/pass_api.py +++ b/frontend/catalyst/passes/pass_api.py @@ -379,4 +379,5 @@ def _API_name_to_pass_name(): "merge_ppr_ppm": "merge_ppr_ppm", "ppr_to_ppm": "ppr_to_ppm", "ppm_compilation": "ppm_compilation", + "ppm_specs": "ppm_specs", } diff --git a/mlir/include/QEC/Transforms/Passes.td b/mlir/include/QEC/Transforms/Passes.td index ab2b14d58b..d236d8c4c0 100644 --- a/mlir/include/QEC/Transforms/Passes.td +++ b/mlir/include/QEC/Transforms/Passes.td @@ -122,7 +122,7 @@ def CliffordTToPPMPass : Pass<"ppm_compilation"> { } def createCountPPMSpecsPass : Pass<"ppm_specs"> { - let summary = "Convert CliffordT operations to Pauli Product Measurement operations."; + let summary = "Count specs in Pauli Product Measurement operations."; let dependentDialects = [ "catalyst::qec::QECDialect", diff --git a/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp b/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp index 24062f84f5..057a505a2a 100644 --- a/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp +++ b/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp @@ -37,7 +37,7 @@ void catalyst::registerAllCatalystPasses() mlir::registerPass(catalyst::createCliffordTToPPMPass); mlir::registerPass(catalyst::createDecomposeNonCliffordPPRPass); mlir::registerPass(catalyst::createDecomposeCliffordPPRPass); - mlir::registerPass(catalyst::createCountPPMSpecsPass) + mlir::registerPass(catalyst::createCountPPMSpecsPass); mlir::registerPass(catalyst::createDetensorizeSCFPass); mlir::registerPass(catalyst::createDisableAssertionPass); mlir::registerPass(catalyst::createDisentangleCNOTPass); diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index dab65d2218..b8b5a506b4 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -1,12 +1,33 @@ -#define DEBUG_TYPE "to_ppr" +// 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 "ppm_specs" + +#include "llvm/Support/Debug.h" #include "mlir/IR/PatternMatch.h" +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/IR/BuiltinOps.h" #include "mlir/Pass/Pass.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "llvm/Support/Debug.h" #include "QEC/IR/QECDialect.h" #include "QEC/Transforms/Patterns.h" #include "Quantum/IR/QuantumOps.h" +using namespace llvm; using namespace mlir; using namespace catalyst::quantum; using namespace catalyst::qec; @@ -19,7 +40,7 @@ namespace qec { #include "QEC/Transforms/Passes.h.inc" -struct CountPPMSpecsPass : impl::CountPPMSpecsPassBase { +struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase { using CountPPMSpecsPassBase::CountPPMSpecsPassBase; void runOnOperation() final @@ -53,9 +74,6 @@ struct CountPPMSpecsPass : impl::CountPPMSpecsPassBase { } // namespace qec /// Create a pass for lowering operations in the `QECDialect`. -std::unique_ptr createCountPPMSpecsPass() -{ - return std::make_unique(); -} +std::unique_ptr createCountPPMSpecsPass() { return std::make_unique(); } } // namespace catalyst From ca994e4ead961dc041bfe7916a4d54ba597f613e Mon Sep 17 00:00:00 2001 From: Ritu Thombre Date: Sat, 31 May 2025 23:08:02 -0400 Subject: [PATCH 03/30] fix passes.td file --- mlir/include/QEC/Transforms/Passes.td | 2 +- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 24 +------------------ mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp | 1 - 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/mlir/include/QEC/Transforms/Passes.td b/mlir/include/QEC/Transforms/Passes.td index d236d8c4c0..d8342b00ec 100644 --- a/mlir/include/QEC/Transforms/Passes.td +++ b/mlir/include/QEC/Transforms/Passes.td @@ -121,7 +121,7 @@ def CliffordTToPPMPass : Pass<"ppm_compilation"> { let options = [MaxPauliSizeOption, DecomposeMethodOption, AvoidYMeasureOption]; } -def createCountPPMSpecsPass : Pass<"ppm_specs"> { +def CountPPMSpecsPass : Pass<"ppm_specs"> { let summary = "Count specs in Pauli Product Measurement operations."; let dependentDialects = [ diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index b8b5a506b4..424c094a43 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -45,29 +45,7 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase void runOnOperation() final { - auto ctx = &getContext(); - auto module = getOperation(); - - // Phase 1: Convert Clifford+T to PPR representation - { - ConversionTarget target(*ctx); - target.addIllegalDialect(); - target.addLegalOp(); - target.addLegalOp(); - target.addLegalOp(); - target.addLegalOp(); - target.addLegalDialect(); - - RewritePatternSet patterns(ctx); - populateCliffordTToPPRPatterns(patterns); - - if (failed(applyPartialConversion(module, target, std::move(patterns)))) { - return signalPassFailure(); - } - else { - llvm::outs() << "TEST for Count PPM Specs\n"; - } - } + llvm::outs() << "TEST for Count PPM Specs\n"; } }; diff --git a/mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp b/mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp index 3586211a77..9fe01dafce 100644 --- a/mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp +++ b/mlir/lib/QEC/Transforms/clifford_t_to_ppr.cpp @@ -50,7 +50,6 @@ struct CliffordTToPPRPass : impl::CliffordTToPPRPassBase { RewritePatternSet patterns(ctx); populateCliffordTToPPRPatterns(patterns); - llvm::outs() << "TEST\n"; if (failed(applyPartialConversion(getOperation(), target, std::move(patterns)))) { return signalPassFailure(); From 54c2460370e91ed4469680fa8535186d5ca3e594 Mon Sep 17 00:00:00 2001 From: Ritu Thombre Date: Sun, 1 Jun 2025 22:48:43 -0400 Subject: [PATCH 04/30] 4 stats recorded --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 74 ++++++++++++++++++++--- 1 file changed, 67 insertions(+), 7 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 424c094a43..d172ff0a0b 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -14,22 +14,22 @@ #define DEBUG_TYPE "ppm_specs" -#include "llvm/Support/Debug.h" - #include "mlir/IR/PatternMatch.h" -#include "mlir/Dialect/Func/IR/FuncOps.h" -#include "mlir/IR/BuiltinOps.h" #include "mlir/Pass/Pass.h" -#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +// #include "mlir/Transforms/GreedyPatternRewriteDriver.h" + +#include "mlir/Analysis/TopologicalSortUtils.h" #include "llvm/Support/Debug.h" #include "QEC/IR/QECDialect.h" #include "QEC/Transforms/Patterns.h" #include "Quantum/IR/QuantumOps.h" +#include "QEC/Utils/PauliStringWrapper.h" +#include using namespace llvm; using namespace mlir; -using namespace catalyst::quantum; +using namespace catalyst; using namespace catalyst::qec; namespace catalyst { @@ -43,9 +43,69 @@ namespace qec { struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase { using CountPPMSpecsPassBase::CountPPMSpecsPassBase; + void print_specs(StringRef pass_name) + { + llvm::outs() << pass_name <<"\n"; + llvm::DenseMap PPM_Specs; + PPM_Specs["num_pi4_gates"] = 0; + PPM_Specs["num_pi8_gates"] = 0; + PPM_Specs["max_weight_pi4"] = 0; + PPM_Specs["max_weight_pi8"] = 0; + + // Walk over all operations in the IR (could be ModuleOp or FuncOp) + getOperation()->walk([&](Operation *op) { + // Skip top-level container ops if desired + if (isa(op)) return; + + StringRef gate_name = op->getName().getStringRef(); + if (gate_name != "qec.ppr" && gate_name != "qec.ppm") return; + + auto rotation_attr = op->getAttrOfType("rotation_kind"); + int16_t rotation_kind = rotation_attr ? static_cast(rotation_attr.getInt()) : 0; + if (rotation_kind) { + auto pauli_product_attr = op->getAttrOfType("pauli_product"); + if (rotation_kind == 4 || rotation_kind == -4) { + PPM_Specs["num_pi4_gates"] = PPM_Specs["num_pi4_gates"] + 1; + PPM_Specs["max_wight_pi4"] = std::max(PPM_Specs["max_wight_pi4"], static_cast(pauli_product_attr.size())); + } + if (rotation_kind == 8 || rotation_kind == -8) { + PPM_Specs["num_pi8_gates"] = PPM_Specs["num_pi8_gates"] + 1; + PPM_Specs["max_wight_pi8"] = std::max(PPM_Specs["max_wight_pi8"], static_cast(pauli_product_attr.size())); + } + } + }); + + for (const auto &entry : PPM_Specs) { + llvm::outs() << " " << entry.first << ": " << entry.second << "\n"; + } + return; + } + void runOnOperation() final { - llvm::outs() << "TEST for Count PPM Specs\n"; + auto ctx = &getContext(); + auto module = getOperation(); + + // Phase 1: Convert Clifford+T to PPR representation + { + ConversionTarget target(*ctx); + target.addIllegalDialect(); + target.addLegalOp(); + target.addLegalOp(); + target.addLegalOp(); + target.addLegalOp(); + target.addLegalDialect(); + + RewritePatternSet patterns(ctx); + populateCliffordTToPPRPatterns(patterns); + + if (failed(applyPartialConversion(module, target, std::move(patterns)))) { + return signalPassFailure(); + } + print_specs("Clifford+T to PPR"); + } + + } }; From 1f6ac67112853be3ee03799ce1cad0e68f0bd4dc Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Wed, 4 Jun 2025 13:23:28 -0400 Subject: [PATCH 05/30] implement ppm_spec logic --- mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp b/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp index f46c8ab809..146b67f16d 100644 --- a/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp +++ b/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp @@ -21,6 +21,8 @@ #include "QEC/IR/QECDialect.h" #include "QEC/Transforms/Patterns.h" #include "Quantum/IR/QuantumOps.h" +#include +#include using namespace llvm; using namespace mlir; @@ -37,6 +39,63 @@ namespace qec { struct CliffordTToPPMPass : public impl::CliffordTToPPMPassBase { using CliffordTToPPMPassBase::CliffordTToPPMPassBase; + void print_specs(StringRef pass_name) + { + llvm::outs() << pass_name <<"\n"; + llvm::BumpPtrAllocator string_allocator; + llvm::DenseMap PPM_Specs; + PPM_Specs["num_logical_qubits"] = 0; + PPM_Specs["num_of_ppm"] = 0; + + // Walk over all operations in the IR (could be ModuleOp or FuncOp) + getOperation()->walk([&](Operation *op) { + // Skip top-level container ops if desired + if (isa(op)) return; + + llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; + op->print(llvm::outs()); + llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; + + StringRef gate_name = op->getName().getStringRef(); + + if (gate_name == "quantum.alloc") { + auto num_qubits_attr = op->getAttrOfType("nqubits_attr"); + u_int64_t num_qubits = num_qubits_attr ? static_cast(num_qubits_attr.getInt()) : 0; + PPM_Specs["num_logical_qubits"] = num_qubits; + } + + if (gate_name == "qec.ppm") { + PPM_Specs["num_of_ppm"] = PPM_Specs["num_of_ppm"] + 1; + } + + if (gate_name == "qec.ppr") { + auto rotation_attr = op->getAttrOfType("rotation_kind"); + auto pauli_product_attr = op->getAttrOfType("pauli_product"); + int16_t rotation_kind = rotation_attr ? static_cast(rotation_attr.getInt()) : 0; + if (rotation_kind) { + llvm::StringSaver saver(string_allocator); + StringRef num_pi_key = saver.save("num_pi"+std::to_string(abs(rotation_kind))+"_gates"); + StringRef max_weight_pi_key = saver.save("max_weight_pi"+std::to_string(abs(rotation_kind))); + + if (PPM_Specs.find(llvm::StringRef(num_pi_key)) == PPM_Specs.end()) { + PPM_Specs[num_pi_key] = 1; + PPM_Specs[max_weight_pi_key] = static_cast(pauli_product_attr.size()); + } + else { + PPM_Specs[num_pi_key] = PPM_Specs[num_pi_key] + 1; + PPM_Specs[max_weight_pi_key] = std::max(PPM_Specs[max_weight_pi_key], static_cast(pauli_product_attr.size())); + } + } + } + }); + + for (const auto &entry : PPM_Specs) { + llvm::outs() << " " << entry.first << ": " << entry.second << "\n"; + } + llvm::outs()<<"\n=====================================================================\n"; + return; + } + void runOnOperation() final { auto ctx = &getContext(); @@ -58,6 +117,7 @@ struct CliffordTToPPMPass : public impl::CliffordTToPPMPassBase Date: Wed, 4 Jun 2025 22:19:13 -0400 Subject: [PATCH 06/30] slicing working --- mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp b/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp index 146b67f16d..4ee58faea7 100644 --- a/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp +++ b/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp @@ -14,6 +14,7 @@ #define DEBUG_TYPE "ppm_compilation" +#include "mlir/Analysis/SliceAnalysis.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" @@ -29,6 +30,10 @@ using namespace mlir; using namespace catalyst; using namespace catalyst::qec; +// bool qsimFilter(mlir::Operation *op) { +// return op->getName().getStringRef() == "quantum.extract"; +// } + namespace catalyst { namespace qec { @@ -52,9 +57,9 @@ struct CliffordTToPPMPass : public impl::CliffordTToPPMPassBase(op)) return; - llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; - op->print(llvm::outs()); - llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; + // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; + // op->print(llvm::outs()); + // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; StringRef gate_name = op->getName().getStringRef(); @@ -87,6 +92,16 @@ struct CliffordTToPPMPass : public impl::CliffordTToPPMPassBase backwardSlice; + getBackwardSlice(op, &backwardSlice); + llvm::outs()<<"\n-----------------------------SLICE-----------------------------\n"; + llvm::outs()<<"Backward slicing\n"; + for (Operation *o : backwardSlice) { + if (o->getName().getStringRef() == "quantum.extract") { + llvm::outs() << *o << "\n"; + } + } + llvm::outs()<<"\n-----------------------------SLICE------------------------------\n"; }); for (const auto &entry : PPM_Specs) { From 940a16ab24523c7676d57ddc1b5deeb79bc25d27 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Thu, 5 Jun 2025 13:46:04 -0400 Subject: [PATCH 07/30] revert ppm_compilation --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 111 +++++++++++------- mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp | 79 ------------- 2 files changed, 70 insertions(+), 120 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index d172ff0a0b..2c63a6f72c 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -14,18 +14,18 @@ #define DEBUG_TYPE "ppm_specs" +#include "mlir/Analysis/SliceAnalysis.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" -// #include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" -#include "mlir/Analysis/TopologicalSortUtils.h" -#include "llvm/Support/Debug.h" #include "QEC/IR/QECDialect.h" #include "QEC/Transforms/Patterns.h" #include "Quantum/IR/QuantumOps.h" #include "QEC/Utils/PauliStringWrapper.h" #include +#include using namespace llvm; using namespace mlir; @@ -46,65 +46,94 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase void print_specs(StringRef pass_name) { llvm::outs() << pass_name <<"\n"; + llvm::BumpPtrAllocator string_allocator; llvm::DenseMap PPM_Specs; - PPM_Specs["num_pi4_gates"] = 0; - PPM_Specs["num_pi8_gates"] = 0; - PPM_Specs["max_weight_pi4"] = 0; - PPM_Specs["max_weight_pi8"] = 0; + PPM_Specs["num_logical_qubits"] = 0; + PPM_Specs["num_of_ppm"] = 0; // Walk over all operations in the IR (could be ModuleOp or FuncOp) getOperation()->walk([&](Operation *op) { // Skip top-level container ops if desired if (isa(op)) return; + // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; + // op->print(llvm::outs()); + // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; + StringRef gate_name = op->getName().getStringRef(); - if (gate_name != "qec.ppr" && gate_name != "qec.ppm") return; - auto rotation_attr = op->getAttrOfType("rotation_kind"); - int16_t rotation_kind = rotation_attr ? static_cast(rotation_attr.getInt()) : 0; - if (rotation_kind) { + if (gate_name == "quantum.alloc") { + auto num_qubits_attr = op->getAttrOfType("nqubits_attr"); + u_int64_t num_qubits = num_qubits_attr ? static_cast(num_qubits_attr.getInt()) : 0; + PPM_Specs["num_logical_qubits"] = num_qubits; + } + + if (gate_name == "qec.ppm") { + PPM_Specs["num_of_ppm"] = PPM_Specs["num_of_ppm"] + 1; + } + + if (gate_name == "qec.ppr") { + auto rotation_attr = op->getAttrOfType("rotation_kind"); auto pauli_product_attr = op->getAttrOfType("pauli_product"); - if (rotation_kind == 4 || rotation_kind == -4) { - PPM_Specs["num_pi4_gates"] = PPM_Specs["num_pi4_gates"] + 1; - PPM_Specs["max_wight_pi4"] = std::max(PPM_Specs["max_wight_pi4"], static_cast(pauli_product_attr.size())); - } - if (rotation_kind == 8 || rotation_kind == -8) { - PPM_Specs["num_pi8_gates"] = PPM_Specs["num_pi8_gates"] + 1; - PPM_Specs["max_wight_pi8"] = std::max(PPM_Specs["max_wight_pi8"], static_cast(pauli_product_attr.size())); + int16_t rotation_kind = rotation_attr ? static_cast(rotation_attr.getInt()) : 0; + if (rotation_kind) { + llvm::StringSaver saver(string_allocator); + StringRef num_pi_key = saver.save("num_pi"+std::to_string(abs(rotation_kind))+"_gates"); + StringRef max_weight_pi_key = saver.save("max_weight_pi"+std::to_string(abs(rotation_kind))); + + if (PPM_Specs.find(llvm::StringRef(num_pi_key)) == PPM_Specs.end()) { + PPM_Specs[num_pi_key] = 1; + PPM_Specs[max_weight_pi_key] = static_cast(pauli_product_attr.size()); + } + else { + PPM_Specs[num_pi_key] = PPM_Specs[num_pi_key] + 1; + PPM_Specs[max_weight_pi_key] = std::max(PPM_Specs[max_weight_pi_key], static_cast(pauli_product_attr.size())); + } } } + // mlir::SetVector backwardSlice; + // getBackwardSlice(op, &backwardSlice); + // llvm::outs()<<"\n-----------------------------SLICE-----------------------------\n"; + // llvm::outs()<<"Backward slicing\n"; + // for (Operation *o : backwardSlice) { + // if (o->getName().getStringRef() == "quantum.extract") { + // llvm::outs() << *o << "\n"; + // } + // } + // llvm::outs()<<"\n-----------------------------SLICE------------------------------\n"; }); - + for (const auto &entry : PPM_Specs) { llvm::outs() << " " << entry.first << ": " << entry.second << "\n"; } + llvm::outs()<<"\n=====================================================================\n"; return; } void runOnOperation() final { - auto ctx = &getContext(); - auto module = getOperation(); - - // Phase 1: Convert Clifford+T to PPR representation - { - ConversionTarget target(*ctx); - target.addIllegalDialect(); - target.addLegalOp(); - target.addLegalOp(); - target.addLegalOp(); - target.addLegalOp(); - target.addLegalDialect(); - - RewritePatternSet patterns(ctx); - populateCliffordTToPPRPatterns(patterns); - - if (failed(applyPartialConversion(module, target, std::move(patterns)))) { - return signalPassFailure(); - } - print_specs("Clifford+T to PPR"); - } - + // auto ctx = &getContext(); + // auto module = getOperation(); + + // // Phase 1: Convert Clifford+T to PPR representation + // { + // ConversionTarget target(*ctx); + // target.addIllegalDialect(); + // target.addLegalOp(); + // target.addLegalOp(); + // target.addLegalOp(); + // target.addLegalOp(); + // target.addLegalDialect(); + + // RewritePatternSet patterns(ctx); + // populateCliffordTToPPRPatterns(patterns); + + // if (failed(applyPartialConversion(module, target, std::move(patterns)))) { + // return signalPassFailure(); + // } + // print_specs("Clifford+T to PPR"); + // } + print_specs("Clifford+T to PPR"); } }; diff --git a/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp b/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp index 4ee58faea7..f46c8ab809 100644 --- a/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp +++ b/mlir/lib/QEC/Transforms/clifford_t_to_ppm.cpp @@ -14,7 +14,6 @@ #define DEBUG_TYPE "ppm_compilation" -#include "mlir/Analysis/SliceAnalysis.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" @@ -22,18 +21,12 @@ #include "QEC/IR/QECDialect.h" #include "QEC/Transforms/Patterns.h" #include "Quantum/IR/QuantumOps.h" -#include -#include using namespace llvm; using namespace mlir; using namespace catalyst; using namespace catalyst::qec; -// bool qsimFilter(mlir::Operation *op) { -// return op->getName().getStringRef() == "quantum.extract"; -// } - namespace catalyst { namespace qec { @@ -44,73 +37,6 @@ namespace qec { struct CliffordTToPPMPass : public impl::CliffordTToPPMPassBase { using CliffordTToPPMPassBase::CliffordTToPPMPassBase; - void print_specs(StringRef pass_name) - { - llvm::outs() << pass_name <<"\n"; - llvm::BumpPtrAllocator string_allocator; - llvm::DenseMap PPM_Specs; - PPM_Specs["num_logical_qubits"] = 0; - PPM_Specs["num_of_ppm"] = 0; - - // Walk over all operations in the IR (could be ModuleOp or FuncOp) - getOperation()->walk([&](Operation *op) { - // Skip top-level container ops if desired - if (isa(op)) return; - - // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; - // op->print(llvm::outs()); - // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; - - StringRef gate_name = op->getName().getStringRef(); - - if (gate_name == "quantum.alloc") { - auto num_qubits_attr = op->getAttrOfType("nqubits_attr"); - u_int64_t num_qubits = num_qubits_attr ? static_cast(num_qubits_attr.getInt()) : 0; - PPM_Specs["num_logical_qubits"] = num_qubits; - } - - if (gate_name == "qec.ppm") { - PPM_Specs["num_of_ppm"] = PPM_Specs["num_of_ppm"] + 1; - } - - if (gate_name == "qec.ppr") { - auto rotation_attr = op->getAttrOfType("rotation_kind"); - auto pauli_product_attr = op->getAttrOfType("pauli_product"); - int16_t rotation_kind = rotation_attr ? static_cast(rotation_attr.getInt()) : 0; - if (rotation_kind) { - llvm::StringSaver saver(string_allocator); - StringRef num_pi_key = saver.save("num_pi"+std::to_string(abs(rotation_kind))+"_gates"); - StringRef max_weight_pi_key = saver.save("max_weight_pi"+std::to_string(abs(rotation_kind))); - - if (PPM_Specs.find(llvm::StringRef(num_pi_key)) == PPM_Specs.end()) { - PPM_Specs[num_pi_key] = 1; - PPM_Specs[max_weight_pi_key] = static_cast(pauli_product_attr.size()); - } - else { - PPM_Specs[num_pi_key] = PPM_Specs[num_pi_key] + 1; - PPM_Specs[max_weight_pi_key] = std::max(PPM_Specs[max_weight_pi_key], static_cast(pauli_product_attr.size())); - } - } - } - mlir::SetVector backwardSlice; - getBackwardSlice(op, &backwardSlice); - llvm::outs()<<"\n-----------------------------SLICE-----------------------------\n"; - llvm::outs()<<"Backward slicing\n"; - for (Operation *o : backwardSlice) { - if (o->getName().getStringRef() == "quantum.extract") { - llvm::outs() << *o << "\n"; - } - } - llvm::outs()<<"\n-----------------------------SLICE------------------------------\n"; - }); - - for (const auto &entry : PPM_Specs) { - llvm::outs() << " " << entry.first << ": " << entry.second << "\n"; - } - llvm::outs()<<"\n=====================================================================\n"; - return; - } - void runOnOperation() final { auto ctx = &getContext(); @@ -132,7 +58,6 @@ struct CliffordTToPPMPass : public impl::CliffordTToPPMPassBase Date: Fri, 6 Jun 2025 17:23:27 -0400 Subject: [PATCH 08/30] working in frontend --- frontend/catalyst/compiler.py | 21 +++++++++++++- frontend/catalyst/jit.py | 9 ++++++ mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 34 +++++------------------ 3 files changed, 36 insertions(+), 28 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index 5dcc98a5c8..78407fbd78 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -25,6 +25,8 @@ import sys import tempfile import warnings +import json +import re from os import path from typing import List, Optional @@ -371,8 +373,25 @@ def to_mlir_opt(*args, stdin=None, options: Optional[CompileOptions] = None): return _quantum_opt(*args, stdin=stdin) opts = _options_to_cli_flags(options) - return _quantum_opt(*opts, *args, stdin=stdin) + raw_result = _quantum_opt(*opts, *args, stdin=stdin) + regex_search_for_json = re.search(r'\{[a-zA-Z0-9_\":\{\},\n]+\}', raw_result) + raw_result = raw_result.replace(regex_search_for_json.group(0), "") + return raw_result +def to_ppm_spec(*args, stdin=None, options: Optional[CompileOptions] = None): + """echo ${input} | catalyst --tool=opt *args *opts -""" + # These are the options that may affect compilation + if not options: + return _quantum_opt(*args, stdin=stdin) + + opts = _options_to_cli_flags(options) + + raw_json_format = _quantum_opt(*opts, *args, stdin=stdin) + regex_search_for_json = re.search(r'\{[a-zA-Z0-9_\":\{\},\n]+\}', raw_json_format) + json_ppm_specs = regex_search_for_json.group(0) + json_ppm_specs = json_ppm_specs.replace(",\n}","\n}") + json_ppm_specs = json.loads(json_ppm_specs) + return json_ppm_specs class Compiler: """Compiles MLIR modules to shared objects by executing the Catalyst compiler driver library.""" diff --git a/frontend/catalyst/jit.py b/frontend/catalyst/jit.py index 7c260ceacb..c39846f536 100644 --- a/frontend/catalyst/jit.py +++ b/frontend/catalyst/jit.py @@ -39,6 +39,7 @@ canonicalize, to_llvmir, to_mlir_opt, + to_ppm_spec ) from catalyst.debug.instruments import instrument from catalyst.from_plxpr import trace_from_pennylane @@ -577,6 +578,14 @@ def mlir_opt(self): return None return to_mlir_opt(stdin=str(self.mlir_module), options=self.compile_options) + + def get_ppm_spec(self): + """obtain the PPM specs after optimization""" + if not self.mlir_module: + return None + + return to_ppm_spec(stdin=str(self.mlir_module), options=self.compile_options) + @debug_logger def __call__(self, *args, **kwargs): diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 2c63a6f72c..e1b3b8839c 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -43,9 +43,8 @@ namespace qec { struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase { using CountPPMSpecsPassBase::CountPPMSpecsPassBase; - void print_specs(StringRef pass_name) + void print_specs() { - llvm::outs() << pass_name <<"\n"; llvm::BumpPtrAllocator string_allocator; llvm::DenseMap PPM_Specs; PPM_Specs["num_logical_qubits"] = 0; @@ -56,6 +55,7 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase // Skip top-level container ops if desired if (isa(op)) return; + // TODO: Remove debug in future // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; // op->print(llvm::outs()); // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; @@ -91,6 +91,7 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase } } } + // TODO: Implement depth using slicing // mlir::SetVector backwardSlice; // getBackwardSlice(op, &backwardSlice); // llvm::outs()<<"\n-----------------------------SLICE-----------------------------\n"; @@ -103,38 +104,17 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase // llvm::outs()<<"\n-----------------------------SLICE------------------------------\n"; }); + llvm::outs() << "{\n"; for (const auto &entry : PPM_Specs) { - llvm::outs() << " " << entry.first << ": " << entry.second << "\n"; + llvm::outs() << '"' << entry.first << '"' << ":" << entry.second << ",\n"; } - llvm::outs()<<"\n=====================================================================\n"; + llvm::outs() << "}\n"; return; } void runOnOperation() final { - // auto ctx = &getContext(); - // auto module = getOperation(); - - // // Phase 1: Convert Clifford+T to PPR representation - // { - // ConversionTarget target(*ctx); - // target.addIllegalDialect(); - // target.addLegalOp(); - // target.addLegalOp(); - // target.addLegalOp(); - // target.addLegalOp(); - // target.addLegalDialect(); - - // RewritePatternSet patterns(ctx); - // populateCliffordTToPPRPatterns(patterns); - - // if (failed(applyPartialConversion(module, target, std::move(patterns)))) { - // return signalPassFailure(); - // } - // print_specs("Clifford+T to PPR"); - // } - print_specs("Clifford+T to PPR"); - + print_specs(); } }; From 09512cfc2588f2f55a1d0ec0f1f4348874a7ef94 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 6 Jun 2025 18:34:26 -0400 Subject: [PATCH 09/30] format and comment --- frontend/catalyst/compiler.py | 16 +++++++++------- frontend/catalyst/jit.py | 7 +++---- frontend/catalyst/passes/__init__.py | 4 ++-- frontend/catalyst/passes/builtin_passes.py | 1 + 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index 78407fbd78..d17c1e87aa 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -16,17 +16,17 @@ """ import glob import importlib +import json import logging import os import pathlib import platform +import re import shutil import subprocess import sys import tempfile import warnings -import json -import re from os import path from typing import List, Optional @@ -374,25 +374,27 @@ def to_mlir_opt(*args, stdin=None, options: Optional[CompileOptions] = None): opts = _options_to_cli_flags(options) raw_result = _quantum_opt(*opts, *args, stdin=stdin) - regex_search_for_json = re.search(r'\{[a-zA-Z0-9_\":\{\},\n]+\}', raw_result) + regex_search_for_json = re.search(r"\{[a-zA-Z0-9_\":\{\},\n]+\}", raw_result) raw_result = raw_result.replace(regex_search_for_json.group(0), "") return raw_result + def to_ppm_spec(*args, stdin=None, options: Optional[CompileOptions] = None): """echo ${input} | catalyst --tool=opt *args *opts -""" # These are the options that may affect compilation if not options: return _quantum_opt(*args, stdin=stdin) - + opts = _options_to_cli_flags(options) - + raw_json_format = _quantum_opt(*opts, *args, stdin=stdin) - regex_search_for_json = re.search(r'\{[a-zA-Z0-9_\":\{\},\n]+\}', raw_json_format) + regex_search_for_json = re.search(r"\{[a-zA-Z0-9_\":\{\},\n]+\}", raw_json_format) json_ppm_specs = regex_search_for_json.group(0) - json_ppm_specs = json_ppm_specs.replace(",\n}","\n}") + json_ppm_specs = json_ppm_specs.replace(",\n}", "\n}") json_ppm_specs = json.loads(json_ppm_specs) return json_ppm_specs + class Compiler: """Compiles MLIR modules to shared objects by executing the Catalyst compiler driver library.""" diff --git a/frontend/catalyst/jit.py b/frontend/catalyst/jit.py index c39846f536..79f94bfaac 100644 --- a/frontend/catalyst/jit.py +++ b/frontend/catalyst/jit.py @@ -39,7 +39,7 @@ canonicalize, to_llvmir, to_mlir_opt, - to_ppm_spec + to_ppm_spec, ) from catalyst.debug.instruments import instrument from catalyst.from_plxpr import trace_from_pennylane @@ -578,14 +578,13 @@ def mlir_opt(self): return None return to_mlir_opt(stdin=str(self.mlir_module), options=self.compile_options) - + def get_ppm_spec(self): """obtain the PPM specs after optimization""" if not self.mlir_module: return None - - return to_ppm_spec(stdin=str(self.mlir_module), options=self.compile_options) + return to_ppm_spec(stdin=str(self.mlir_module), options=self.compile_options) @debug_logger def __call__(self, *args, **kwargs): diff --git a/frontend/catalyst/passes/__init__.py b/frontend/catalyst/passes/__init__.py index 7fcb5dbd66..a8acdc8b42 100644 --- a/frontend/catalyst/passes/__init__.py +++ b/frontend/catalyst/passes/__init__.py @@ -40,9 +40,9 @@ merge_ppr_ppm, merge_rotations, ppm_compilation, + ppm_specs, ppr_to_ppm, to_ppr, - ppm_specs ) from catalyst.passes.pass_api import Pass, PassPlugin, apply_pass, apply_pass_plugin @@ -59,5 +59,5 @@ "merge_ppr_ppm", "ppr_to_ppm", "ppm_compilation", - "ppm_specs" + "ppm_specs", ) diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index 93d759789f..ffd15f2a33 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -784,4 +784,5 @@ def circuit(): def ppm_specs(qnode): + # TODO: Add docstring return PassPipelineWrapper(qnode, "ppm_specs") From aa16ead170398e0211924ca10fe88257c41abf4e Mon Sep 17 00:00:00 2001 From: Ritu Thombre Date: Sun, 8 Jun 2025 18:39:13 -0400 Subject: [PATCH 10/30] check if opt contains json --- frontend/catalyst/compiler.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index d17c1e87aa..904260d6d1 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -389,12 +389,18 @@ def to_ppm_spec(*args, stdin=None, options: Optional[CompileOptions] = None): raw_json_format = _quantum_opt(*opts, *args, stdin=stdin) regex_search_for_json = re.search(r"\{[a-zA-Z0-9_\":\{\},\n]+\}", raw_json_format) + + # No ppm_specs json is found + if regex_search_for_json is None: + return raw_json_format + json_ppm_specs = regex_search_for_json.group(0) json_ppm_specs = json_ppm_specs.replace(",\n}", "\n}") json_ppm_specs = json.loads(json_ppm_specs) return json_ppm_specs + class Compiler: """Compiles MLIR modules to shared objects by executing the Catalyst compiler driver library.""" From ef174bf75a7babab2d3fd7d61acac7b77651ca3b Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Wed, 11 Jun 2025 19:10:38 -0400 Subject: [PATCH 11/30] resolve PR comments --- frontend/catalyst/compiler.py | 14 ++++---- mlir/include/QEC/Transforms/Passes.td | 4 --- mlir/lib/QEC/Transforms/CMakeLists.txt | 22 +++++++++++- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 42 ++++++----------------- 4 files changed, 38 insertions(+), 44 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index 904260d6d1..c1ab6ac336 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -374,7 +374,12 @@ def to_mlir_opt(*args, stdin=None, options: Optional[CompileOptions] = None): opts = _options_to_cli_flags(options) raw_result = _quantum_opt(*opts, *args, stdin=stdin) - regex_search_for_json = re.search(r"\{[a-zA-Z0-9_\":\{\},\n]+\}", raw_result) + regex_search_for_json = re.search(r"\{.*?\}", raw_result, re.DOTALL) + + # No ppm_specs json is found + if regex_search_for_json is None: + return raw_result + raw_result = raw_result.replace(regex_search_for_json.group(0), "") return raw_result @@ -388,12 +393,7 @@ def to_ppm_spec(*args, stdin=None, options: Optional[CompileOptions] = None): opts = _options_to_cli_flags(options) raw_json_format = _quantum_opt(*opts, *args, stdin=stdin) - regex_search_for_json = re.search(r"\{[a-zA-Z0-9_\":\{\},\n]+\}", raw_json_format) - - # No ppm_specs json is found - if regex_search_for_json is None: - return raw_json_format - + regex_search_for_json = re.search(r"\{.*?\}", raw_json_format, re.DOTALL) json_ppm_specs = regex_search_for_json.group(0) json_ppm_specs = json_ppm_specs.replace(",\n}", "\n}") json_ppm_specs = json.loads(json_ppm_specs) diff --git a/mlir/include/QEC/Transforms/Passes.td b/mlir/include/QEC/Transforms/Passes.td index d8342b00ec..86e28a8c51 100644 --- a/mlir/include/QEC/Transforms/Passes.td +++ b/mlir/include/QEC/Transforms/Passes.td @@ -124,10 +124,6 @@ def CliffordTToPPMPass : Pass<"ppm_compilation"> { def CountPPMSpecsPass : Pass<"ppm_specs"> { let summary = "Count specs in Pauli Product Measurement operations."; - let dependentDialects = [ - "catalyst::qec::QECDialect", - ]; - let constructor = "catalyst::createCountPPMSpecsPass()"; } diff --git a/mlir/lib/QEC/Transforms/CMakeLists.txt b/mlir/lib/QEC/Transforms/CMakeLists.txt index 56febea807..cb418d0cb4 100644 --- a/mlir/lib/QEC/Transforms/CMakeLists.txt +++ b/mlir/lib/QEC/Transforms/CMakeLists.txt @@ -32,7 +32,27 @@ set(DEPENDS add_mlir_library(${LIBRARY_NAME} STATIC ${SRC} LINK_LIBS PRIVATE ${LIBS} DEPENDS ${DEPENDS}) target_compile_features(${LIBRARY_NAME} PUBLIC cxx_std_20) + +include(FetchContent) +FetchContent_Declare(json + URL https://github.com/nlohmann/json/releases/download/v3.11.3/json.tar.xz + URL_HASH SHA256=d6c65aca6b1ed68e7a182f4757257b107ae403032760ed6ef121c9d55e81757d + DOWNLOAD_EXTRACT_TIMESTAMP true + SYSTEM +) +FetchContent_MakeAvailable(json) +target_link_libraries(${LIBRARY_NAME} + PRIVATE nlohmann_json::nlohmann_json +) + +if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") +set_source_files_properties( + CountPPMSpecs.cpp + COMPILE_FLAGS "-Wno-covered-switch-default" +) +endif() + target_include_directories(${LIBRARY_NAME} PUBLIC . ${PROJECT_SOURCE_DIR}/include - ${CMAKE_BINARY_DIR}/include) + ${CMAKE_BINARY_DIR}/include) \ No newline at end of file diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index e1b3b8839c..989c2f947b 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -14,23 +14,21 @@ #define DEBUG_TYPE "ppm_specs" -#include "mlir/Analysis/SliceAnalysis.h" +#include +#include + +#include + #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" -#include "mlir/Transforms/GreedyPatternRewriteDriver.h" - #include "QEC/IR/QECDialect.h" -#include "QEC/Transforms/Patterns.h" -#include "Quantum/IR/QuantumOps.h" -#include "QEC/Utils/PauliStringWrapper.h" -#include -#include using namespace llvm; using namespace mlir; using namespace catalyst; using namespace catalyst::qec; +using json = nlohmann::json; namespace catalyst { namespace qec { @@ -39,7 +37,6 @@ namespace qec { #define GEN_PASS_DECL_COUNTPPMSPECSPASS #include "QEC/Transforms/Passes.h.inc" - struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase { using CountPPMSpecsPassBase::CountPPMSpecsPassBase; @@ -55,11 +52,6 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase // Skip top-level container ops if desired if (isa(op)) return; - // TODO: Remove debug in future - // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; - // op->print(llvm::outs()); - // llvm::outs()<<"\n-----------------------------MLIR------------------------------\n"; - StringRef gate_name = op->getName().getStringRef(); if (gate_name == "quantum.alloc") { @@ -69,7 +61,7 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase } if (gate_name == "qec.ppm") { - PPM_Specs["num_of_ppm"] = PPM_Specs["num_of_ppm"] + 1; + PPM_Specs["num_of_ppm"]++; } if (gate_name == "qec.ppr") { @@ -86,29 +78,15 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase PPM_Specs[max_weight_pi_key] = static_cast(pauli_product_attr.size()); } else { - PPM_Specs[num_pi_key] = PPM_Specs[num_pi_key] + 1; + PPM_Specs[num_pi_key]++; PPM_Specs[max_weight_pi_key] = std::max(PPM_Specs[max_weight_pi_key], static_cast(pauli_product_attr.size())); } } } - // TODO: Implement depth using slicing - // mlir::SetVector backwardSlice; - // getBackwardSlice(op, &backwardSlice); - // llvm::outs()<<"\n-----------------------------SLICE-----------------------------\n"; - // llvm::outs()<<"Backward slicing\n"; - // for (Operation *o : backwardSlice) { - // if (o->getName().getStringRef() == "quantum.extract") { - // llvm::outs() << *o << "\n"; - // } - // } - // llvm::outs()<<"\n-----------------------------SLICE------------------------------\n"; }); - llvm::outs() << "{\n"; - for (const auto &entry : PPM_Specs) { - llvm::outs() << '"' << entry.first << '"' << ":" << entry.second << ",\n"; - } - llvm::outs() << "}\n"; + json PPM_Specs_Json = PPM_Specs; + llvm::outs() << PPM_Specs_Json.dump(4) << "\n"; return; } From 3304c10706e07752e679341e97f46ab989ea7863 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Wed, 11 Jun 2025 19:16:31 -0400 Subject: [PATCH 12/30] fix formatting --- frontend/catalyst/compiler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index c1ab6ac336..3a04372157 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -379,7 +379,7 @@ def to_mlir_opt(*args, stdin=None, options: Optional[CompileOptions] = None): # No ppm_specs json is found if regex_search_for_json is None: return raw_result - + raw_result = raw_result.replace(regex_search_for_json.group(0), "") return raw_result @@ -400,7 +400,6 @@ def to_ppm_spec(*args, stdin=None, options: Optional[CompileOptions] = None): return json_ppm_specs - class Compiler: """Compiles MLIR modules to shared objects by executing the Catalyst compiler driver library.""" From ddc30f12363967e25bf4215303c22a33024da2f6 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Wed, 11 Jun 2025 19:32:25 -0400 Subject: [PATCH 13/30] check nullptr for num_qubit --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 27 ++++++++++++++--------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 989c2f947b..0f54763793 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -50,13 +50,16 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase // Walk over all operations in the IR (could be ModuleOp or FuncOp) getOperation()->walk([&](Operation *op) { // Skip top-level container ops if desired - if (isa(op)) return; + if (isa(op)) + return; StringRef gate_name = op->getName().getStringRef(); if (gate_name == "quantum.alloc") { auto num_qubits_attr = op->getAttrOfType("nqubits_attr"); - u_int64_t num_qubits = num_qubits_attr ? static_cast(num_qubits_attr.getInt()) : 0; + assert(num_qubits_attr == nullptr && "num_qubits_attr is a dynamic SSA value"); + u_int64_t num_qubits = + num_qubits_attr ? static_cast(num_qubits_attr.getInt()) : 0; PPM_Specs["num_logical_qubits"] = num_qubits; } @@ -67,11 +70,14 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase if (gate_name == "qec.ppr") { auto rotation_attr = op->getAttrOfType("rotation_kind"); auto pauli_product_attr = op->getAttrOfType("pauli_product"); - int16_t rotation_kind = rotation_attr ? static_cast(rotation_attr.getInt()) : 0; + int16_t rotation_kind = + rotation_attr ? static_cast(rotation_attr.getInt()) : 0; if (rotation_kind) { llvm::StringSaver saver(string_allocator); - StringRef num_pi_key = saver.save("num_pi"+std::to_string(abs(rotation_kind))+"_gates"); - StringRef max_weight_pi_key = saver.save("max_weight_pi"+std::to_string(abs(rotation_kind))); + StringRef num_pi_key = + saver.save("num_pi" + std::to_string(abs(rotation_kind)) + "_gates"); + StringRef max_weight_pi_key = + saver.save("max_weight_pi" + std::to_string(abs(rotation_kind))); if (PPM_Specs.find(llvm::StringRef(num_pi_key)) == PPM_Specs.end()) { PPM_Specs[num_pi_key] = 1; @@ -79,21 +85,20 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase } else { PPM_Specs[num_pi_key]++; - PPM_Specs[max_weight_pi_key] = std::max(PPM_Specs[max_weight_pi_key], static_cast(pauli_product_attr.size())); + PPM_Specs[max_weight_pi_key] = + std::max(PPM_Specs[max_weight_pi_key], + static_cast(pauli_product_attr.size())); } } } }); - + json PPM_Specs_Json = PPM_Specs; llvm::outs() << PPM_Specs_Json.dump(4) << "\n"; return; } - void runOnOperation() final - { - print_specs(); - } + void runOnOperation() final { print_specs(); } }; } // namespace qec From eeffd558d7c5a82719d5c03f3a41d361990852a3 Mon Sep 17 00:00:00 2001 From: paul0403 Date: Thu, 12 Jun 2025 14:01:40 -0400 Subject: [PATCH 14/30] move get_ppm_spec() API function out of jit.py and compiler.py; remove @ppm_specs pass decorator --- frontend/catalyst/compiler.py | 26 +--------------------- frontend/catalyst/jit.py | 8 ------- frontend/catalyst/passes/__init__.py | 4 ++-- frontend/catalyst/passes/builtin_passes.py | 24 +++++++++++++++----- 4 files changed, 22 insertions(+), 40 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index 3a04372157..a0515c8256 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -373,31 +373,7 @@ def to_mlir_opt(*args, stdin=None, options: Optional[CompileOptions] = None): return _quantum_opt(*args, stdin=stdin) opts = _options_to_cli_flags(options) - raw_result = _quantum_opt(*opts, *args, stdin=stdin) - regex_search_for_json = re.search(r"\{.*?\}", raw_result, re.DOTALL) - - # No ppm_specs json is found - if regex_search_for_json is None: - return raw_result - - raw_result = raw_result.replace(regex_search_for_json.group(0), "") - return raw_result - - -def to_ppm_spec(*args, stdin=None, options: Optional[CompileOptions] = None): - """echo ${input} | catalyst --tool=opt *args *opts -""" - # These are the options that may affect compilation - if not options: - return _quantum_opt(*args, stdin=stdin) - - opts = _options_to_cli_flags(options) - - raw_json_format = _quantum_opt(*opts, *args, stdin=stdin) - regex_search_for_json = re.search(r"\{.*?\}", raw_json_format, re.DOTALL) - json_ppm_specs = regex_search_for_json.group(0) - json_ppm_specs = json_ppm_specs.replace(",\n}", "\n}") - json_ppm_specs = json.loads(json_ppm_specs) - return json_ppm_specs + return _quantum_opt(*opts, *args, stdin=stdin) class Compiler: diff --git a/frontend/catalyst/jit.py b/frontend/catalyst/jit.py index 79f94bfaac..7c260ceacb 100644 --- a/frontend/catalyst/jit.py +++ b/frontend/catalyst/jit.py @@ -39,7 +39,6 @@ canonicalize, to_llvmir, to_mlir_opt, - to_ppm_spec, ) from catalyst.debug.instruments import instrument from catalyst.from_plxpr import trace_from_pennylane @@ -579,13 +578,6 @@ def mlir_opt(self): return to_mlir_opt(stdin=str(self.mlir_module), options=self.compile_options) - def get_ppm_spec(self): - """obtain the PPM specs after optimization""" - if not self.mlir_module: - return None - - return to_ppm_spec(stdin=str(self.mlir_module), options=self.compile_options) - @debug_logger def __call__(self, *args, **kwargs): # Transparantly call Python function in case of nested QJIT calls. diff --git a/frontend/catalyst/passes/__init__.py b/frontend/catalyst/passes/__init__.py index a8acdc8b42..abd59a15d5 100644 --- a/frontend/catalyst/passes/__init__.py +++ b/frontend/catalyst/passes/__init__.py @@ -40,9 +40,9 @@ merge_ppr_ppm, merge_rotations, ppm_compilation, - ppm_specs, ppr_to_ppm, to_ppr, + get_ppm_spec, ) from catalyst.passes.pass_api import Pass, PassPlugin, apply_pass, apply_pass_plugin @@ -59,5 +59,5 @@ "merge_ppr_ppm", "ppr_to_ppm", "ppm_compilation", - "ppm_specs", + "get_ppm_spec", ) diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index ffd15f2a33..94786fa1e2 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -15,10 +15,29 @@ """This module exposes built-in Catalyst MLIR passes to the frontend.""" import functools +import re +from catalyst.compiler import _quantum_opt from catalyst.passes.pass_api import PassPipelineWrapper +def get_ppm_spec(QJIT): + """TODO: docstring""" + + if QJIT.mlir is not None: + # aot mode + raw_result = _quantum_opt( + ("--pass-pipeline", "builtin.module(ppm_specs)"), [], stdin=QJIT.mlir + ) + + # breakpoint() + regex_search_for_json = re.search(r"\{.*?\}", raw_result, re.DOTALL) + return regex_search_for_json.group(0) + + else: + raise "ppm passes only support aot mode, please make sure ..." + + ## API ## def cancel_inverses(qnode): """ @@ -781,8 +800,3 @@ def circuit(): ) return PassPipelineWrapper(qnode, passes) - - -def ppm_specs(qnode): - # TODO: Add docstring - return PassPipelineWrapper(qnode, "ppm_specs") From bc4efce937f61571f04d539563c4fffafe0cc6c0 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Thu, 12 Jun 2025 14:56:10 -0400 Subject: [PATCH 15/30] remove ppm_specs from unnecessary places --- frontend/catalyst/compiler.py | 2 -- frontend/catalyst/passes/builtin_passes.py | 4 +++- frontend/catalyst/passes/pass_api.py | 1 - 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index a0515c8256..5dcc98a5c8 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -16,12 +16,10 @@ """ import glob import importlib -import json import logging import os import pathlib import platform -import re import shutil import subprocess import sys diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index 94786fa1e2..f36667d670 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -35,7 +35,9 @@ def get_ppm_spec(QJIT): return regex_search_for_json.group(0) else: - raise "ppm passes only support aot mode, please make sure ..." + raise NotImplementedError( + "PPM passes only support AOT (Ahead-Of-Time) compilation mode." + ) ## API ## diff --git a/frontend/catalyst/passes/pass_api.py b/frontend/catalyst/passes/pass_api.py index 2de68034e9..3f4b40ee5c 100644 --- a/frontend/catalyst/passes/pass_api.py +++ b/frontend/catalyst/passes/pass_api.py @@ -382,5 +382,4 @@ def _API_name_to_pass_name(): "merge_ppr_ppm": "merge_ppr_ppm", "ppr_to_ppm": "ppr_to_ppm", "ppm_compilation": "ppm_compilation", - "ppm_specs": "ppm_specs", } From 2dc9fc2bdaae4a684c9620f5b1e3668e1a210923 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Thu, 12 Jun 2025 14:57:23 -0400 Subject: [PATCH 16/30] code cleanup --- frontend/catalyst/passes/builtin_passes.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index f36667d670..06a93720a2 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -35,9 +35,7 @@ def get_ppm_spec(QJIT): return regex_search_for_json.group(0) else: - raise NotImplementedError( - "PPM passes only support AOT (Ahead-Of-Time) compilation mode." - ) + raise NotImplementedError("PPM passes only support AOT (Ahead-Of-Time) compilation mode.") ## API ## From f17706a0c65092a5501fa59fdf7c9817671ec814 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Thu, 12 Jun 2025 15:18:12 -0400 Subject: [PATCH 17/30] assert dynamic qubit count --- frontend/catalyst/passes/__init__.py | 2 +- frontend/catalyst/passes/builtin_passes.py | 2 +- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 7 +++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/catalyst/passes/__init__.py b/frontend/catalyst/passes/__init__.py index abd59a15d5..a74a320548 100644 --- a/frontend/catalyst/passes/__init__.py +++ b/frontend/catalyst/passes/__init__.py @@ -36,13 +36,13 @@ from catalyst.passes.builtin_passes import ( cancel_inverses, commute_ppr, + get_ppm_spec, ions_decomposition, merge_ppr_ppm, merge_rotations, ppm_compilation, ppr_to_ppm, to_ppr, - get_ppm_spec, ) from catalyst.passes.pass_api import Pass, PassPlugin, apply_pass, apply_pass_plugin diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index 06a93720a2..70616a878a 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -27,7 +27,7 @@ def get_ppm_spec(QJIT): if QJIT.mlir is not None: # aot mode raw_result = _quantum_opt( - ("--pass-pipeline", "builtin.module(ppm_specs)"), [], stdin=QJIT.mlir + ("--pass-pipeline", "builtin.module(ppm_specs)"), [], stdin=QJIT.mlir_opt ) # breakpoint() diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 0f54763793..27a9b5dd81 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -22,6 +22,7 @@ #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" +#include "Quantum/IR/QuantumOps.h" #include "QEC/IR/QECDialect.h" using namespace llvm; @@ -56,10 +57,8 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase StringRef gate_name = op->getName().getStringRef(); if (gate_name == "quantum.alloc") { - auto num_qubits_attr = op->getAttrOfType("nqubits_attr"); - assert(num_qubits_attr == nullptr && "num_qubits_attr is a dynamic SSA value"); - u_int64_t num_qubits = - num_qubits_attr ? static_cast(num_qubits_attr.getInt()) : 0; + uint64_t num_qubits = cast(op).getNqubitsAttr().value_or(0); + assert(num_qubits != 0 && "PPM specs with dynamic number of qubits is not implemented"); PPM_Specs["num_logical_qubits"] = num_qubits; } From 05ef5c31ad00e4464cfaabe54ed834f3c8a64c6a Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Thu, 12 Jun 2025 15:20:43 -0400 Subject: [PATCH 18/30] formatting --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 27a9b5dd81..dcc2548548 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -22,8 +22,8 @@ #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" -#include "Quantum/IR/QuantumOps.h" #include "QEC/IR/QECDialect.h" +#include "Quantum/IR/QuantumOps.h" using namespace llvm; using namespace mlir; @@ -57,8 +57,9 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase StringRef gate_name = op->getName().getStringRef(); if (gate_name == "quantum.alloc") { - uint64_t num_qubits = cast(op).getNqubitsAttr().value_or(0); - assert(num_qubits != 0 && "PPM specs with dynamic number of qubits is not implemented"); + uint64_t num_qubits = cast(op).getNqubitsAttr().value_or(0); + assert(num_qubits != 0 && + "PPM specs with dynamic number of qubits is not implemented"); PPM_Specs["num_logical_qubits"] = num_qubits; } From f72df331c064043e7a40cf9cd8f6b13741b12eca Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Thu, 12 Jun 2025 18:36:57 -0400 Subject: [PATCH 19/30] resolve some PR comments --- frontend/catalyst/passes/builtin_passes.py | 2 -- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index 70616a878a..35b198e85b 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -29,8 +29,6 @@ def get_ppm_spec(QJIT): raw_result = _quantum_opt( ("--pass-pipeline", "builtin.module(ppm_specs)"), [], stdin=QJIT.mlir_opt ) - - # breakpoint() regex_search_for_json = re.search(r"\{.*?\}", raw_result, re.DOTALL) return regex_search_for_json.group(0) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index dcc2548548..bc04beab39 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -68,10 +68,9 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase } if (gate_name == "qec.ppr") { - auto rotation_attr = op->getAttrOfType("rotation_kind"); - auto pauli_product_attr = op->getAttrOfType("pauli_product"); int16_t rotation_kind = - rotation_attr ? static_cast(rotation_attr.getInt()) : 0; + cast(op).getRotationKindAttr().getValue().getZExtValue(); + auto pauli_product_attr = cast(op).getPauliProductAttr(); if (rotation_kind) { llvm::StringSaver saver(string_allocator); StringRef num_pi_key = @@ -94,7 +93,8 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase }); json PPM_Specs_Json = PPM_Specs; - llvm::outs() << PPM_Specs_Json.dump(4) << "\n"; + llvm::outs() << PPM_Specs_Json.dump(4) + << "\n"; // dump(4) makes an indent with 4 spaces when printing JSON return; } From 19900c7f722e2809e65d44256234c26a6b6065e0 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Thu, 12 Jun 2025 18:44:48 -0400 Subject: [PATCH 20/30] check op type instead of name: --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index bc04beab39..2b01a7fd3e 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -54,20 +54,18 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase if (isa(op)) return; - StringRef gate_name = op->getName().getStringRef(); - - if (gate_name == "quantum.alloc") { + if (isa(op)) { uint64_t num_qubits = cast(op).getNqubitsAttr().value_or(0); assert(num_qubits != 0 && "PPM specs with dynamic number of qubits is not implemented"); PPM_Specs["num_logical_qubits"] = num_qubits; } - if (gate_name == "qec.ppm") { + else if (isa(op)) { PPM_Specs["num_of_ppm"]++; } - if (gate_name == "qec.ppr") { + else if (isa(op)) { int16_t rotation_kind = cast(op).getRotationKindAttr().getValue().getZExtValue(); auto pauli_product_attr = cast(op).getPauliProductAttr(); From 34aa5ffe8846d3f084a65468cef54958d032a7f9 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 11:04:16 -0400 Subject: [PATCH 21/30] separate count functions --- frontend/catalyst/passes/__init__.py | 2 +- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 80 +++++++++++++---------- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/frontend/catalyst/passes/__init__.py b/frontend/catalyst/passes/__init__.py index a74a320548..7fc70b51fa 100644 --- a/frontend/catalyst/passes/__init__.py +++ b/frontend/catalyst/passes/__init__.py @@ -50,6 +50,7 @@ "to_ppr", "commute_ppr", "cancel_inverses", + "get_ppm_spec", "ions_decomposition", "merge_rotations", "Pass", @@ -59,5 +60,4 @@ "merge_ppr_ppm", "ppr_to_ppm", "ppm_compilation", - "get_ppm_spec", ) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 2b01a7fd3e..a015f5945f 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#define DEBUG_TYPE "ppm_specs" +#define DEBUG_TYPE "PPMSpecs" #include #include @@ -41,12 +41,45 @@ namespace qec { struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase { using CountPPMSpecsPassBase::CountPPMSpecsPassBase; - void print_specs() + llvm::DenseMap countLogicalQubit(Operation *op, + llvm::DenseMap PPMSpecs) { - llvm::BumpPtrAllocator string_allocator; - llvm::DenseMap PPM_Specs; - PPM_Specs["num_logical_qubits"] = 0; - PPM_Specs["num_of_ppm"] = 0; + uint64_t numQubits = cast(op).getNqubitsAttr().value_or(0); + assert(numQubits != 0 && "PPM specs with dynamic number of qubits is not implemented"); + PPMSpecs["num_logical_qubits"] = numQubits; + return PPMSpecs; + } + llvm::DenseMap countPPR(Operation *op, llvm::DenseMap PPMSpecs, + llvm::BumpPtrAllocator *stringAllocator) + { + int16_t rotationKind = + cast(op).getRotationKindAttr().getValue().getZExtValue(); + auto PauliProductAttr = cast(op).getPauliProductAttr(); + if (rotationKind) { + llvm::StringSaver saver(*stringAllocator); + StringRef numRotationKindKey = + saver.save("num_pi" + std::to_string(abs(rotationKind)) + "_gates"); + StringRef maxWeightRotationKindKey = + saver.save("max_weight_pi" + std::to_string(abs(rotationKind))); + + if (PPMSpecs.find(llvm::StringRef(numRotationKindKey)) == PPMSpecs.end()) { + PPMSpecs[numRotationKindKey] = 1; + PPMSpecs[maxWeightRotationKindKey] = static_cast(PauliProductAttr.size()); + } + else { + PPMSpecs[numRotationKindKey]++; + PPMSpecs[maxWeightRotationKindKey] = std::max( + PPMSpecs[maxWeightRotationKindKey], static_cast(PauliProductAttr.size())); + } + } + return PPMSpecs; + } + void printSpecs() + { + llvm::BumpPtrAllocator stringAllocator; + llvm::DenseMap PPMSpecs; + PPMSpecs["num_logical_qubits"] = 0; + PPMSpecs["num_of_ppm"] = 0; // Walk over all operations in the IR (could be ModuleOp or FuncOp) getOperation()->walk([&](Operation *op) { @@ -55,48 +88,25 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase return; if (isa(op)) { - uint64_t num_qubits = cast(op).getNqubitsAttr().value_or(0); - assert(num_qubits != 0 && - "PPM specs with dynamic number of qubits is not implemented"); - PPM_Specs["num_logical_qubits"] = num_qubits; + PPMSpecs = countLogicalQubit(op, PPMSpecs); } else if (isa(op)) { - PPM_Specs["num_of_ppm"]++; + PPMSpecs["num_of_ppm"]++; } else if (isa(op)) { - int16_t rotation_kind = - cast(op).getRotationKindAttr().getValue().getZExtValue(); - auto pauli_product_attr = cast(op).getPauliProductAttr(); - if (rotation_kind) { - llvm::StringSaver saver(string_allocator); - StringRef num_pi_key = - saver.save("num_pi" + std::to_string(abs(rotation_kind)) + "_gates"); - StringRef max_weight_pi_key = - saver.save("max_weight_pi" + std::to_string(abs(rotation_kind))); - - if (PPM_Specs.find(llvm::StringRef(num_pi_key)) == PPM_Specs.end()) { - PPM_Specs[num_pi_key] = 1; - PPM_Specs[max_weight_pi_key] = static_cast(pauli_product_attr.size()); - } - else { - PPM_Specs[num_pi_key]++; - PPM_Specs[max_weight_pi_key] = - std::max(PPM_Specs[max_weight_pi_key], - static_cast(pauli_product_attr.size())); - } - } + PPMSpecs = countPPR(op, PPMSpecs, &stringAllocator); } }); - json PPM_Specs_Json = PPM_Specs; - llvm::outs() << PPM_Specs_Json.dump(4) + json PPMSpecsJson = PPMSpecs; + llvm::outs() << PPMSpecsJson.dump(4) << "\n"; // dump(4) makes an indent with 4 spaces when printing JSON return; } - void runOnOperation() final { print_specs(); } + void runOnOperation() final { printSpecs(); } }; } // namespace qec From ecae329b8305efe1f67a738a46a6d02fffffffd4 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 11:14:47 -0400 Subject: [PATCH 22/30] remove snake_case --- frontend/catalyst/passes/builtin_passes.py | 2 +- mlir/include/QEC/Transforms/Passes.td | 2 +- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index 0090a0df78..7809dae19d 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -27,7 +27,7 @@ def get_ppm_spec(QJIT): if QJIT.mlir is not None: # aot mode raw_result = _quantum_opt( - ("--pass-pipeline", "builtin.module(ppm_specs)"), [], stdin=QJIT.mlir_opt + ("--pass-pipeline", "builtin.module(ppm-specs)"), [], stdin=QJIT.mlir_opt ) regex_search_for_json = re.search(r"\{.*?\}", raw_result, re.DOTALL) return regex_search_for_json.group(0) diff --git a/mlir/include/QEC/Transforms/Passes.td b/mlir/include/QEC/Transforms/Passes.td index c3281d8071..02673144d9 100644 --- a/mlir/include/QEC/Transforms/Passes.td +++ b/mlir/include/QEC/Transforms/Passes.td @@ -125,7 +125,7 @@ def PPMCompilationPass : Pass<"ppm-compilation"> { let options = [MaxPauliSizeOption, DecomposeMethodOption, AvoidYMeasureOption]; } -def CountPPMSpecsPass : Pass<"ppm_specs"> { +def CountPPMSpecsPass : Pass<"ppm-specs"> { let summary = "Count specs in Pauli Product Measurement operations."; let constructor = "catalyst::createCountPPMSpecsPass()"; diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index a015f5945f..cbf706f897 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#define DEBUG_TYPE "PPMSpecs" +#define DEBUG_TYPE "ppm-specs" #include #include From 12603358b186fdce271cfd23f24ed06d09aa5311 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 17:56:31 -0400 Subject: [PATCH 23/30] add frontend pytests --- frontend/catalyst/passes/builtin_passes.py | 7 +-- .../pytest/test_peephole_optimizations.py | 50 +++++++++++++++++++ mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 49 ++++++++++-------- 3 files changed, 82 insertions(+), 24 deletions(-) diff --git a/frontend/catalyst/passes/builtin_passes.py b/frontend/catalyst/passes/builtin_passes.py index 7809dae19d..1a01dbd29e 100644 --- a/frontend/catalyst/passes/builtin_passes.py +++ b/frontend/catalyst/passes/builtin_passes.py @@ -15,7 +15,7 @@ """This module exposes built-in Catalyst MLIR passes to the frontend.""" import functools -import re +import json from catalyst.compiler import _quantum_opt from catalyst.passes.pass_api import PassPipelineWrapper @@ -29,8 +29,9 @@ def get_ppm_spec(QJIT): raw_result = _quantum_opt( ("--pass-pipeline", "builtin.module(ppm-specs)"), [], stdin=QJIT.mlir_opt ) - regex_search_for_json = re.search(r"\{.*?\}", raw_result, re.DOTALL) - return regex_search_for_json.group(0) + return json.loads( + raw_result[: raw_result.index("module")] + ) # remove MLIR starting with substring "module..." else: raise NotImplementedError("PPM passes only support AOT (Ahead-Of-Time) compilation mode.") diff --git a/frontend/test/pytest/test_peephole_optimizations.py b/frontend/test/pytest/test_peephole_optimizations.py index 596f756fcd..05457e88e2 100644 --- a/frontend/test/pytest/test_peephole_optimizations.py +++ b/frontend/test/pytest/test_peephole_optimizations.py @@ -14,6 +14,8 @@ """Test the quantum peephole passes""" +import json + import numpy as np import pennylane as qml import pytest @@ -22,6 +24,7 @@ from catalyst.passes import ( cancel_inverses, commute_ppr, + get_ppm_spec, merge_ppr_ppm, merge_rotations, ppm_compilation, @@ -209,6 +212,12 @@ def f(): assert 'transform.apply_registered_pass "to-ppr"' not in optimized_ir assert "qec.ppr" in optimized_ir + ppm_specs = get_ppm_spec(test_convert_clifford_to_ppr_workflow) + assert ppm_specs["f_0"]["num_logical_qubits"] == 2 + assert ppm_specs["f_0"]["num_pi4_gates"] == 7 + assert ppm_specs["f_0"]["max_weight_pi4"] == 2 + assert ppm_specs["f_0"]["num_pi8_gates"] == 1 + assert ppm_specs["f_0"]["max_weight_pi8"] == 1 def test_commute_ppr(): @@ -235,6 +244,13 @@ def f(): assert "qec.ppr" in optimized_ir assert "qec.ppm" in optimized_ir + ppm_specs = get_ppm_spec(test_commute_ppr_workflow) + assert ppm_specs["f_0"]["num_of_ppm"] == 2 + assert ppm_specs["f_0"]["num_logical_qubits"] == 2 + assert ppm_specs["f_0"]["num_pi4_gates"] == 7 + assert ppm_specs["f_0"]["max_weight_pi4"] == 2 + assert ppm_specs["f_0"]["num_pi8_gates"] == 1 + assert ppm_specs["f_0"]["max_weight_pi8"] == 1 def test_merge_ppr_ppm(): @@ -260,6 +276,9 @@ def f(): assert 'qec.ppm ["Z", "X"]' in optimized_ir assert 'qec.ppm ["X"]' in optimized_ir + ppm_specs = get_ppm_spec(test_merge_ppr_ppm_workflow) + assert ppm_specs["f_0"]["num_of_ppm"] == 2 + assert ppm_specs["f_0"]["num_logical_qubits"] == 2 def test_ppr_to_ppm(): @@ -295,6 +314,11 @@ def f(): assert "qec.select.ppm" in optimized_ir assert 'qec.ppr ["X"]' in optimized_ir + ppm_specs = get_ppm_spec(test_ppr_to_ppm_workflow) + assert ppm_specs["f_0"]["num_of_ppm"] == 19 + assert ppm_specs["f_0"]["num_logical_qubits"] == 2 + assert ppm_specs["f_0"]["num_pi2_gates"] == 8 + assert ppm_specs["f_0"]["max_weight_pi2"] == 2 def test_ppr_to_ppm_inject_magic_state(): @@ -326,6 +350,11 @@ def f(): assert 'transform.apply_registered_pass "decompose-non-clifford-ppr"' not in optimized_ir assert 'transform.apply_registered_pass "decompose-clifford-ppr"' not in optimized_ir + ppm_specs = get_ppm_spec(test_ppr_to_ppm_workflow) + assert ppm_specs["f_0"]["num_of_ppm"] == 20 + assert ppm_specs["f_0"]["num_logical_qubits"] == 2 + assert ppm_specs["f_0"]["num_pi2_gates"] == 9 + assert ppm_specs["f_0"]["max_weight_pi2"] == 2 def test_commute_ppr_and_merge_ppr_ppm_with_max_pauli_size(): @@ -371,6 +400,19 @@ def g(): assert 'transform.apply_registered_pass "commute-ppr"' not in optimized_ir assert 'transform.apply_registered_pass "merge-ppr-ppm"' not in optimized_ir + ppm_specs = get_ppm_spec(test_convert_clifford_to_ppr_workflow) + + ppm_specs["f_0"]["num_logical_qubits"] = 2 + ppm_specs["f_0"]["num_of_ppm"] = 2 + ppm_specs["f_0"]["num_pi8_gates"] = 1 + ppm_specs["f_0"]["max_weight_pi8"] = 1 + + ppm_specs["g_0"]["num_logical_qubits"] = 2 + ppm_specs["g_0"]["num_of_ppm"] = 2 + ppm_specs["g_0"]["num_pi4_gates"] = 2 + ppm_specs["g_0"]["max_weight_pi4"] = 3 + ppm_specs["g_0"]["num_pi8_gates"] = 2 + ppm_specs["g_0"]["max_weight_pi8"] = 1 def test_clifford_to_ppm(): @@ -409,6 +451,14 @@ def g(): assert 'qec.ppm ["Z", "Y"]' in optimized_ir assert 'qec.ppr ["X", "Z"](2)' in optimized_ir + ppm_specs = get_ppm_spec(test_clifford_to_ppm_workflow) + + ppm_specs["f_0"]["num_logical_qubits"] = 2 + ppm_specs["f_0"]["num_of_ppm"] = 7 + ppm_specs["f_0"]["num_pi2_gates"] = 2 + ppm_specs["f_0"]["max_weight_pi2"] = 2 + + ppm_specs["g_0"]["num_logical_qubits"] = 2 if __name__ == "__main__": pytest.main(["-x", __file__]) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index cbf706f897..f9cdf04e39 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -19,6 +19,7 @@ #include +#include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" @@ -41,58 +42,64 @@ namespace qec { struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase { using CountPPMSpecsPassBase::CountPPMSpecsPassBase; - llvm::DenseMap countLogicalQubit(Operation *op, - llvm::DenseMap PPMSpecs) + llvm::DenseMap> + countLogicalQubit(Operation *op, + llvm::DenseMap> PPMSpecs) { uint64_t numQubits = cast(op).getNqubitsAttr().value_or(0); assert(numQubits != 0 && "PPM specs with dynamic number of qubits is not implemented"); - PPMSpecs["num_logical_qubits"] = numQubits; + auto parentFuncOp = op->getParentOfType(); + PPMSpecs[parentFuncOp.getName()]["num_logical_qubits"] = numQubits; return PPMSpecs; } - llvm::DenseMap countPPR(Operation *op, llvm::DenseMap PPMSpecs, - llvm::BumpPtrAllocator *stringAllocator) + + llvm::DenseMap> + countPPM(Operation *op, llvm::DenseMap> PPMSpecs) + { + auto parentFuncOp = op->getParentOfType(); + PPMSpecs[parentFuncOp.getName()]["num_of_ppm"]++; + return PPMSpecs; + } + + llvm::DenseMap> + countPPR(Operation *op, llvm::DenseMap> PPMSpecs, + llvm::BumpPtrAllocator *stringAllocator) { int16_t rotationKind = cast(op).getRotationKindAttr().getValue().getZExtValue(); auto PauliProductAttr = cast(op).getPauliProductAttr(); + auto parentFuncOp = op->getParentOfType(); + StringRef funcName = parentFuncOp.getName(); if (rotationKind) { llvm::StringSaver saver(*stringAllocator); StringRef numRotationKindKey = saver.save("num_pi" + std::to_string(abs(rotationKind)) + "_gates"); StringRef maxWeightRotationKindKey = saver.save("max_weight_pi" + std::to_string(abs(rotationKind))); - - if (PPMSpecs.find(llvm::StringRef(numRotationKindKey)) == PPMSpecs.end()) { - PPMSpecs[numRotationKindKey] = 1; - PPMSpecs[maxWeightRotationKindKey] = static_cast(PauliProductAttr.size()); - } - else { - PPMSpecs[numRotationKindKey]++; - PPMSpecs[maxWeightRotationKindKey] = std::max( - PPMSpecs[maxWeightRotationKindKey], static_cast(PauliProductAttr.size())); - } + PPMSpecs[funcName][numRotationKindKey]++; + PPMSpecs[funcName][maxWeightRotationKindKey] = + std::max(PPMSpecs[funcName][maxWeightRotationKindKey], + static_cast(PauliProductAttr.size())); } return PPMSpecs; } void printSpecs() { llvm::BumpPtrAllocator stringAllocator; - llvm::DenseMap PPMSpecs; - PPMSpecs["num_logical_qubits"] = 0; - PPMSpecs["num_of_ppm"] = 0; - + llvm::DenseMap> PPMSpecs; + // StringRef funcName; // Walk over all operations in the IR (could be ModuleOp or FuncOp) getOperation()->walk([&](Operation *op) { // Skip top-level container ops if desired if (isa(op)) return; - if (isa(op)) { + else if (isa(op)) { PPMSpecs = countLogicalQubit(op, PPMSpecs); } else if (isa(op)) { - PPMSpecs["num_of_ppm"]++; + PPMSpecs = countPPM(op, PPMSpecs); } else if (isa(op)) { From 641a416c2a3ce2224cc775380fc7772cc10f95dd Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 17:59:28 -0400 Subject: [PATCH 24/30] formatting changes --- frontend/test/pytest/test_peephole_optimizations.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/frontend/test/pytest/test_peephole_optimizations.py b/frontend/test/pytest/test_peephole_optimizations.py index 05457e88e2..1b58fe801b 100644 --- a/frontend/test/pytest/test_peephole_optimizations.py +++ b/frontend/test/pytest/test_peephole_optimizations.py @@ -14,8 +14,6 @@ """Test the quantum peephole passes""" -import json - import numpy as np import pennylane as qml import pytest @@ -219,6 +217,7 @@ def f(): assert ppm_specs["f_0"]["num_pi8_gates"] == 1 assert ppm_specs["f_0"]["max_weight_pi8"] == 1 + def test_commute_ppr(): pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] @@ -252,6 +251,7 @@ def f(): assert ppm_specs["f_0"]["num_pi8_gates"] == 1 assert ppm_specs["f_0"]["max_weight_pi8"] == 1 + def test_merge_ppr_ppm(): pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] @@ -280,6 +280,7 @@ def f(): assert ppm_specs["f_0"]["num_of_ppm"] == 2 assert ppm_specs["f_0"]["num_logical_qubits"] == 2 + def test_ppr_to_ppm(): pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] @@ -320,6 +321,7 @@ def f(): assert ppm_specs["f_0"]["num_pi2_gates"] == 8 assert ppm_specs["f_0"]["max_weight_pi2"] == 2 + def test_ppr_to_ppm_inject_magic_state(): pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] @@ -356,6 +358,7 @@ def f(): assert ppm_specs["f_0"]["num_pi2_gates"] == 9 assert ppm_specs["f_0"]["max_weight_pi2"] == 2 + def test_commute_ppr_and_merge_ppr_ppm_with_max_pauli_size(): pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] @@ -414,6 +417,7 @@ def g(): ppm_specs["g_0"]["num_pi8_gates"] = 2 ppm_specs["g_0"]["max_weight_pi8"] = 1 + def test_clifford_to_ppm(): pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] @@ -460,5 +464,6 @@ def g(): ppm_specs["g_0"]["num_logical_qubits"] = 2 + if __name__ == "__main__": pytest.main(["-x", __file__]) From 341e75ba51ee83ae5f045cd26ba02feb26bd31b9 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 18:16:33 -0400 Subject: [PATCH 25/30] fix pytests --- .../pytest/test_peephole_optimizations.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/test/pytest/test_peephole_optimizations.py b/frontend/test/pytest/test_peephole_optimizations.py index 1b58fe801b..0ae100aea5 100644 --- a/frontend/test/pytest/test_peephole_optimizations.py +++ b/frontend/test/pytest/test_peephole_optimizations.py @@ -405,17 +405,17 @@ def g(): ppm_specs = get_ppm_spec(test_convert_clifford_to_ppr_workflow) - ppm_specs["f_0"]["num_logical_qubits"] = 2 - ppm_specs["f_0"]["num_of_ppm"] = 2 - ppm_specs["f_0"]["num_pi8_gates"] = 1 - ppm_specs["f_0"]["max_weight_pi8"] = 1 + ppm_specs["f_0"]["num_logical_qubits"] == 2 + ppm_specs["f_0"]["num_of_ppm"] == 2 + ppm_specs["f_0"]["num_pi8_gates"] == 1 + ppm_specs["f_0"]["max_weight_pi8"] == 1 - ppm_specs["g_0"]["num_logical_qubits"] = 2 - ppm_specs["g_0"]["num_of_ppm"] = 2 - ppm_specs["g_0"]["num_pi4_gates"] = 2 - ppm_specs["g_0"]["max_weight_pi4"] = 3 - ppm_specs["g_0"]["num_pi8_gates"] = 2 - ppm_specs["g_0"]["max_weight_pi8"] = 1 + ppm_specs["g_0"]["num_logical_qubits"] == 2 + ppm_specs["g_0"]["num_of_ppm"] == 2 + ppm_specs["g_0"]["num_pi4_gates"] == 2 + ppm_specs["g_0"]["max_weight_pi4"] == 3 + ppm_specs["g_0"]["num_pi8_gates"] == 2 + ppm_specs["g_0"]["max_weight_pi8"] == 1 def test_clifford_to_ppm(): @@ -457,12 +457,12 @@ def g(): ppm_specs = get_ppm_spec(test_clifford_to_ppm_workflow) - ppm_specs["f_0"]["num_logical_qubits"] = 2 - ppm_specs["f_0"]["num_of_ppm"] = 7 - ppm_specs["f_0"]["num_pi2_gates"] = 2 - ppm_specs["f_0"]["max_weight_pi2"] = 2 + ppm_specs["f_0"]["num_logical_qubits"] == 2 + ppm_specs["f_0"]["num_of_ppm"] == 7 + ppm_specs["f_0"]["num_pi2_gates"] == 2 + ppm_specs["f_0"]["max_weight_pi2"] == 2 - ppm_specs["g_0"]["num_logical_qubits"] = 2 + ppm_specs["g_0"]["num_logical_qubits"] == 2 if __name__ == "__main__": From 29d5b7404360ca4e7270749921c8ad2c77b527b2 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 18:55:40 -0400 Subject: [PATCH 26/30] mlir test --- mlir/test/QEC/PPMSpecsTest.mlir | 77 +++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 mlir/test/QEC/PPMSpecsTest.mlir diff --git a/mlir/test/QEC/PPMSpecsTest.mlir b/mlir/test/QEC/PPMSpecsTest.mlir new file mode 100644 index 0000000000..c28f3fb62b --- /dev/null +++ b/mlir/test/QEC/PPMSpecsTest.mlir @@ -0,0 +1,77 @@ +// 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 quantum-opt --to-ppr --ppm-specs --commute-ppr --ppm-specs --merge-ppr-ppm --ppm-specs --decompose-non-clifford-ppr --ppm-specs --decompose-clifford-ppr --ppm-specs --split-input-file -verify-diagnostics %s > %t.ppm + +func.func @test_clifford_t_to_ppm_1() -> (tensor, tensor) { + %0 = quantum.alloc( 2) : !quantum.reg + %1 = quantum.extract %0[ 1] : !quantum.reg -> !quantum.bit + %out_qubits = quantum.custom "S"() %1 : !quantum.bit + %2 = quantum.extract %0[ 0] : !quantum.reg -> !quantum.bit + %out_qubits_0 = quantum.custom "Hadamard"() %2 : !quantum.bit + %out_qubits_1 = quantum.custom "T"() %out_qubits_0 : !quantum.bit + %out_qubits_2:2 = quantum.custom "CNOT"() %out_qubits_1, %out_qubits : !quantum.bit, !quantum.bit + %mres_0, %out_qubit_0 = quantum.measure %out_qubits_2#0 : i1, !quantum.bit + %from_elements_0 = tensor.from_elements %mres_0 : tensor + %mres_1, %out_qubit_1 = quantum.measure %out_qubits_2#1 : i1, !quantum.bit + %from_elements_1 = tensor.from_elements %mres_1 : tensor + %3 = quantum.insert %0[ 0], %out_qubit_0 : !quantum.reg, !quantum.bit + %4 = quantum.insert %3[ 1], %out_qubit_1 : !quantum.reg, !quantum.bit + quantum.dealloc %4 : !quantum.reg + return %from_elements_0, %from_elements_1 : tensor, tensor + // CHECK: { + // CHECK: "test_clifford_t_to_ppm_1": { + // CHECK: "max_weight_pi4": 2, + // CHECK: "max_weight_pi8": 1, + // CHECK: "num_logical_qubits": 2, + // CHECK: "num_of_ppm": 2, + // CHECK: "num_pi4_gates": 7, + // CHECK: "num_pi8_gates": 1 + // CHECK: } + // CHECK: } + // CHECK: { + // CHECK: "test_clifford_t_to_ppm_1": { + // CHECK: "max_weight_pi4": 2, + // CHECK: "max_weight_pi8": 1, + // CHECK: "num_logical_qubits": 2, + // CHECK: "num_of_ppm": 2, + // CHECK: "num_pi4_gates": 7, + // CHECK: "num_pi8_gates": 1 + // CHECK: } + // CHECK: } + // CHECK: { + // CHECK: "test_clifford_t_to_ppm_1": { + // CHECK: "max_weight_pi8": 1, + // CHECK: "num_logical_qubits": 2, + // CHECK: "num_of_ppm": 2, + // CHECK: "num_pi8_gates": 1 + // CHECK: } + // CHECK: } + // CHECK: { + // CHECK: "test_clifford_t_to_ppm_1": { + // CHECK: "max_weight_pi2": 1, + // CHECK: "num_logical_qubits": 2, + // CHECK: "num_of_ppm": 5, + // CHECK: "num_pi2_gates": 1 + // CHECK: } + // CHECK: } + // CHECK: { + // CHECK: "test_clifford_t_to_ppm_1": { + // CHECK: "max_weight_pi2": 1, + // CHECK: "num_logical_qubits": 2, + // CHECK: "num_of_ppm": 5, + // CHECK: "num_pi2_gates": 1 + // CHECK: } + // CHECK: } +} From 04e914ed737fd1105f6f2d645e9277bbbc4cc282 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 19:07:49 -0400 Subject: [PATCH 27/30] mlir test with ppm specs --- mlir/test/QEC/PPMSpecsTest.mlir | 79 ++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/mlir/test/QEC/PPMSpecsTest.mlir b/mlir/test/QEC/PPMSpecsTest.mlir index c28f3fb62b..3a4fed299e 100644 --- a/mlir/test/QEC/PPMSpecsTest.mlir +++ b/mlir/test/QEC/PPMSpecsTest.mlir @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -// RUN: quantum-opt quantum-opt --to-ppr --ppm-specs --commute-ppr --ppm-specs --merge-ppr-ppm --ppm-specs --decompose-non-clifford-ppr --ppm-specs --decompose-clifford-ppr --ppm-specs --split-input-file -verify-diagnostics %s > %t.ppm +// RUN: quantum-opt --to-ppr --ppm-specs --commute-ppr --ppm-specs --merge-ppr-ppm --ppm-specs --decompose-non-clifford-ppr --ppm-specs --decompose-clifford-ppr --ppm-specs --split-input-file -verify-diagnostics %s > %t.ppm func.func @test_clifford_t_to_ppm_1() -> (tensor, tensor) { %0 = quantum.alloc( 2) : !quantum.reg @@ -75,3 +75,80 @@ func.func @test_clifford_t_to_ppm_1() -> (tensor, tensor) { // CHECK: } // CHECK: } } + +// RUN: quantum-opt --ppm-compilation --ppm-specs --split-input-file -verify-diagnostics %s > %t.ppm + +func.func @game_of_surface_code() -> (tensor, tensor, tensor, tensor) { + %c = stablehlo.constant dense<0> : tensor + %extracted = tensor.extract %c[] : tensor + %c_0 = stablehlo.constant dense<4> : tensor + %0 = quantum.alloc( 4) : !quantum.reg + %c_1 = stablehlo.constant dense<3> : tensor + %extracted_2 = tensor.extract %c_1[] : tensor + %1 = quantum.extract %0[%extracted_2] : !quantum.reg -> !quantum.bit + // PPR X(-pi/4) = H Tdag H + %out_qubits = quantum.custom "Hadamard"() %1 : !quantum.bit + %out_qubits_3 = quantum.custom "T"() %out_qubits adj : !quantum.bit + %out_qubits_4 = quantum.custom "Hadamard"() %out_qubits_3 : !quantum.bit + %extracted_5 = tensor.extract %c[] : tensor + %2 = quantum.extract %0[%extracted_5] : !quantum.reg -> !quantum.bit + %out_qubits_6 = quantum.custom "T"() %2 : !quantum.bit + %c_7 = stablehlo.constant dense<2> : tensor + %extracted_8 = tensor.extract %c_7[] : tensor + %3 = quantum.extract %0[%extracted_8] : !quantum.reg -> !quantum.bit + %c_9 = stablehlo.constant dense<1> : tensor + %extracted_10 = tensor.extract %c_9[] : tensor + %4 = quantum.extract %0[%extracted_10] : !quantum.reg -> !quantum.bit + %out_qubits_11:2 = quantum.custom "CNOT"() %3, %4 : !quantum.bit, !quantum.bit + %out_qubits_12:2 = quantum.custom "CNOT"() %out_qubits_11#1, %out_qubits_6 : !quantum.bit, !quantum.bit + %out_qubits_13:2 = quantum.custom "CNOT"() %out_qubits_4, %out_qubits_12#1 : !quantum.bit, !quantum.bit + // PPR X(pi/4) = H T H + %out_qubits_14 = quantum.custom "T"() %out_qubits_13#1 : !quantum.bit + %out_qubits_15 = quantum.custom "Hadamard"() %out_qubits_14 : !quantum.bit + %out_qubits_16 = quantum.custom "T"() %out_qubits_15 adj : !quantum.bit + %out_qubits_17 = quantum.custom "Hadamard"() %out_qubits_16 : !quantum.bit + %mres, %out_qubit = quantum.measure %out_qubits_17 : i1, !quantum.bit + %from_elements = tensor.from_elements %mres : tensor + %out_qubits_18 = quantum.custom "S"() %out_qubits_12#0 : !quantum.bit + %out_qubits_19 = quantum.custom "Hadamard"() %out_qubits_18 : !quantum.bit + %out_qubits_20 = quantum.custom "T"() %out_qubits_19 : !quantum.bit + %out_qubits_21 = quantum.custom "Hadamard"() %out_qubits_20 : !quantum.bit + %mres_22, %out_qubit_23 = quantum.measure %out_qubits_21 : i1, !quantum.bit + %from_elements_24 = tensor.from_elements %mres_22 : tensor + %out_qubits_25 = quantum.custom "Hadamard"() %out_qubits_11#0 : !quantum.bit + %out_qubits_26 = quantum.custom "T"() %out_qubits_25 : !quantum.bit + %out_qubits_27 = quantum.custom "Hadamard"() %out_qubits_26 : !quantum.bit + %out_qubits_28 = quantum.custom "T"() %out_qubits_27 : !quantum.bit + %out_qubits_29 = quantum.custom "Hadamard"() %out_qubits_28 : !quantum.bit + %out_qubits_30 = quantum.custom "T"() %out_qubits_29 : !quantum.bit + %out_qubits_31 = quantum.custom "Hadamard"() %out_qubits_30 : !quantum.bit + %mres_32, %out_qubit_33 = quantum.measure %out_qubits_31 : i1, !quantum.bit + %from_elements_34 = tensor.from_elements %mres_32 : tensor + %out_qubits_35 = quantum.custom "S"() %out_qubits_13#0 : !quantum.bit + %out_qubits_36 = quantum.custom "Hadamard"() %out_qubits_35 : !quantum.bit + %out_qubits_37 = quantum.custom "T"() %out_qubits_36 : !quantum.bit + %out_qubits_38 = quantum.custom "Hadamard"() %out_qubits_37 : !quantum.bit + %mres_39, %out_qubit_40 = quantum.measure %out_qubits_38 : i1, !quantum.bit + %from_elements_41 = tensor.from_elements %mres_39 : tensor + %extracted_42 = tensor.extract %c[] : tensor + %5 = quantum.insert %0[%extracted_42], %out_qubit : !quantum.reg, !quantum.bit + %extracted_43 = tensor.extract %c_7[] : tensor + %6 = quantum.insert %5[%extracted_43], %out_qubit_33 : !quantum.reg, !quantum.bit + %extracted_44 = tensor.extract %c_9[] : tensor + %7 = quantum.insert %6[%extracted_44], %out_qubit_23 : !quantum.reg, !quantum.bit + %extracted_45 = tensor.extract %c_1[] : tensor + %8 = quantum.insert %7[%extracted_45], %out_qubit_40 : !quantum.reg, !quantum.bit + quantum.dealloc %8 : !quantum.reg + quantum.device_release + return %from_elements, %from_elements_24, %from_elements_34, %from_elements_41 : tensor, tensor, tensor, tensor + // CHECK: { + // CHECK: "game_of_surface_code": { + // CHECK: "max_weight_pi2": 4, + // CHECK: "num_logical_qubits": 4, + // CHECK: "num_of_ppm": 31, + // CHECK: "num_pi2_gates": 9 + // CHECK: } + // CHECK: } +} + +// ----- \ No newline at end of file From e4c17b97e7b31e240bc6df8a00049c2e09a45d17 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 19:14:28 -0400 Subject: [PATCH 28/30] fix asserts --- .../pytest/test_peephole_optimizations.py | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/test/pytest/test_peephole_optimizations.py b/frontend/test/pytest/test_peephole_optimizations.py index 0ae100aea5..ab2660937a 100644 --- a/frontend/test/pytest/test_peephole_optimizations.py +++ b/frontend/test/pytest/test_peephole_optimizations.py @@ -405,17 +405,17 @@ def g(): ppm_specs = get_ppm_spec(test_convert_clifford_to_ppr_workflow) - ppm_specs["f_0"]["num_logical_qubits"] == 2 - ppm_specs["f_0"]["num_of_ppm"] == 2 - ppm_specs["f_0"]["num_pi8_gates"] == 1 - ppm_specs["f_0"]["max_weight_pi8"] == 1 + assert ppm_specs["f_0"]["num_logical_qubits"] == 2 + assert ppm_specs["f_0"]["num_of_ppm"] == 2 + assert ppm_specs["f_0"]["num_pi8_gates"] == 1 + assert ppm_specs["f_0"]["max_weight_pi8"] == 1 - ppm_specs["g_0"]["num_logical_qubits"] == 2 - ppm_specs["g_0"]["num_of_ppm"] == 2 - ppm_specs["g_0"]["num_pi4_gates"] == 2 - ppm_specs["g_0"]["max_weight_pi4"] == 3 - ppm_specs["g_0"]["num_pi8_gates"] == 2 - ppm_specs["g_0"]["max_weight_pi8"] == 1 + assert ppm_specs["g_0"]["num_logical_qubits"] == 2 + assert ppm_specs["g_0"]["num_of_ppm"] == 2 + assert ppm_specs["g_0"]["num_pi4_gates"] == 3 + assert ppm_specs["g_0"]["max_weight_pi4"] == 2 + assert ppm_specs["g_0"]["num_pi8_gates"] == 2 + assert ppm_specs["g_0"]["max_weight_pi8"] == 1 def test_clifford_to_ppm(): @@ -457,12 +457,12 @@ def g(): ppm_specs = get_ppm_spec(test_clifford_to_ppm_workflow) - ppm_specs["f_0"]["num_logical_qubits"] == 2 - ppm_specs["f_0"]["num_of_ppm"] == 7 - ppm_specs["f_0"]["num_pi2_gates"] == 2 - ppm_specs["f_0"]["max_weight_pi2"] == 2 + assert ppm_specs["f_0"]["num_logical_qubits"] == 2 + assert ppm_specs["f_0"]["num_of_ppm"] == 7 + assert ppm_specs["f_0"]["num_pi2_gates"] == 2 + assert ppm_specs["f_0"]["max_weight_pi2"] == 2 - ppm_specs["g_0"]["num_logical_qubits"] == 2 + assert ppm_specs["g_0"]["num_logical_qubits"] == 2 if __name__ == "__main__": From a2265be74cc0f8d60684ccd9f574cb1893168745 Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 19:29:14 -0400 Subject: [PATCH 29/30] lit tests --- frontend/test/lit/test_ppm_specs.py | 229 ++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 frontend/test/lit/test_ppm_specs.py diff --git a/frontend/test/lit/test_ppm_specs.py b/frontend/test/lit/test_ppm_specs.py new file mode 100644 index 0000000000..820edb4852 --- /dev/null +++ b/frontend/test/lit/test_ppm_specs.py @@ -0,0 +1,229 @@ +# 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. + +"""This file performs the frontend tests for printing PPR/PPM specs after the PPR and PPM passes +are correctly lowered.""" + +# RUN: %PYTHON %s | FileCheck %s + +# pylint: disable=line-too-long + +import pennylane as qml + +from catalyst import measure, qjit +from catalyst.passes import commute_ppr, merge_ppr_ppm, ppm_compilation, ppr_to_ppm, to_ppr, get_ppm_spec + + +def test_convert_clifford_to_ppr(): + """ + Test the `to_ppr` pass. + Check that the original qnode is correctly kept and untransformed. + """ + + pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] + + @qjit(pipelines=pipe, target="mlir") + @to_ppr + @qml.qnode(qml.device("null.qubit", wires=2)) + def circuit(): + qml.H(0) + qml.S(1) + qml.T(0) + qml.CNOT([0, 1]) + return measure(0) + + print(get_ppm_spec(circuit)) + +# CHECK: {'circuit_0': {'max_weight_pi4': 2, 'max_weight_pi8': 1, 'num_logical_qubits': 2, 'num_of_ppm': 1, 'num_pi4_gates': 7, 'num_pi8_gates': 1}} +test_convert_clifford_to_ppr() + + +def test_commute_ppr(): + """ + Test the `commute_ppr` pass. + Ensure that the `qec.ppr` with pi/8 rotations are moved to the beginning of the circuit. + """ + + pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] + + @qjit(pipelines=pipe, target="mlir") + @commute_ppr + @to_ppr + @qml.qnode(qml.device("null.qubit", wires=2)) + def cir_commute_ppr(): + qml.H(0) + qml.S(1) + qml.T(0) + qml.T(1) + qml.CNOT([0, 1]) + return measure(0), measure(1) + + print(get_ppm_spec(cir_commute_ppr)) + +# CHECK: {'cir_commute_ppr_0': {'max_weight_pi4': 2, 'max_weight_pi8': 1, 'num_logical_qubits': 2, 'num_of_ppm': 2, 'num_pi4_gates': 7, 'num_pi8_gates': 2}} +test_commute_ppr() + + +def test_commute_ppr_max_pauli_size(): + """ + Test the `commute_ppr` pass with max_pauli_size. + The Pauli string should not be larger than max_pauli_size. + """ + + pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] + + @qjit(pipelines=pipe, target="mlir") + @commute_ppr(max_pauli_size=2) + @to_ppr + @qml.qnode(qml.device("null.qubit", wires=2)) + def cir_commute_ppr_max_pauli_size(): + qml.CNOT([0, 2]) + qml.T(0) + qml.T(1) + qml.CNOT([0, 1]) + qml.S(0) + qml.H(0) + qml.T(0) + return measure(0), measure(1) + + print(get_ppm_spec(cir_commute_ppr_max_pauli_size)) + +# CHECK: {'cir_commute_ppr_max_pauli_size_0': {'max_weight_pi4': 2, 'max_weight_pi8': 2, 'num_logical_qubits': 2, 'num_of_ppm': 2, 'num_pi4_gates': 10, 'num_pi8_gates': 3}} +test_commute_ppr_max_pauli_size() + + +def test_merge_ppr_ppm(): + """ + Test the `merge_ppr_ppm` pass. + `qec.ppr` should be merged into `qec.ppm`, thus no `qec.ppr` should be left. + """ + + pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] + + @qjit(pipelines=pipe, target="mlir") + @merge_ppr_ppm + @to_ppr + @qml.qnode(qml.device("null.qubit", wires=2)) + def cir_merge_ppr_ppm(): + qml.H(0) + qml.S(1) + qml.CNOT([0, 1]) + return measure(0), measure(1) + + print(get_ppm_spec(cir_merge_ppr_ppm)) + +# CHECK: {'cir_merge_ppr_ppm_0': {'num_logical_qubits': 2, 'num_of_ppm': 2}} +test_merge_ppr_ppm() + + +def test_merge_ppr_ppm_max_pauli_size(): + """ + Test the `merge_ppr_ppm` pass with max_pauli_size. + The Pauli string should not be larger than max_pauli_size. + """ + + pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] + + @qjit(pipelines=pipe, target="mlir") + @merge_ppr_ppm(max_pauli_size=1) + @to_ppr + @qml.qnode(qml.device("null.qubit", wires=2)) + def cir_merge_ppr_ppm_max_pauli_size(): + qml.CNOT([0, 2]) + qml.T(0) + qml.T(1) + qml.CNOT([0, 1]) + return measure(0), measure(1) + + print(get_ppm_spec(cir_merge_ppr_ppm_max_pauli_size)) + +# CHECK: {'cir_merge_ppr_ppm_max_pauli_size_0': {'max_weight_pi4': 2, 'max_weight_pi8': 1, 'num_logical_qubits': 2, 'num_of_ppm': 2, 'num_pi4_gates': 3, 'num_pi8_gates': 2}} +test_merge_ppr_ppm_max_pauli_size() + + +def test_ppr_to_ppm(): + """ + Test the pipeline `ppr_to_ppm` pass. + Check that the `qec.ppr` is correctly decomposed into `qec.ppm`. + """ + + pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] + + device = qml.device("null.qubit", wires=2) + + @qjit(pipelines=pipe, target="mlir") + def circuit_ppr_to_ppm(): + + @ppr_to_ppm + @to_ppr + @qml.qnode(device) + def cir_default(): + qml.S(0) + + @ppr_to_ppm(decompose_method="clifford-corrected", avoid_y_measure=True) + @to_ppr + @qml.qnode(device) + def cir_inject_magic_state(): + qml.T(0) + qml.CNOT([0, 1]) + + return cir_default(), cir_inject_magic_state() + + print(get_ppm_spec(circuit_ppr_to_ppm)) + +# CHECK: {'cir_default_0': {'max_weight_pi2': 1, 'num_logical_qubits': 2, 'num_of_ppm': 2, 'num_pi2_gates': 1}, 'cir_inject_magic_state_0': {'max_weight_pi2': 2, 'num_logical_qubits': 2, 'num_of_ppm': 10, 'num_pi2_gates': 5}} +test_ppr_to_ppm() + + +def test_clifford_to_ppm(): + """ + Test the pipeline `to_ppm` pass. + Check whole pipeline of PPM's sub-passes. + The Pauli string should not be larger than max_pauli_size, but in ppr_to_ppm, + the Pauli string can increase by one because of an additional auxiliary qubit. + """ + + pipe = [("pipe", ["enforce-runtime-invariants-pipeline"])] + + @qjit(pipelines=pipe, target="mlir") + def test_clifford_to_ppm_workflow(): + + @ppm_compilation + @qml.qnode(qml.device("null.qubit", wires=2)) + def cir_clifford_to_ppm(): + qml.H(0) + qml.CNOT(wires=[0, 1]) + qml.T(0) + qml.T(1) + return [measure(0), measure(1)] + + @ppm_compilation( + decompose_method="clifford-corrected", avoid_y_measure=True, max_pauli_size=2 + ) + @qml.qnode(qml.device("null.qubit", wires=5)) + def cir_clifford_to_ppm_with_params(): + for idx in range(5): + qml.H(idx) + qml.CNOT(wires=[idx, idx + 1]) + qml.CNOT(wires=[idx + 1, (idx + 2) % 5]) + qml.T(idx) + qml.T(idx + 1) + return [measure(idx) for idx in range(5)] + + return cir_clifford_to_ppm(), cir_clifford_to_ppm_with_params() + + print(get_ppm_spec(test_clifford_to_ppm_workflow)) + +# CHECK: meow {'cir_clifford_to_ppm_0': {'max_weight_pi2': 2, 'num_logical_qubits': 2, 'num_of_ppm': 8, 'num_pi2_gates': 2}, 'cir_clifford_to_ppm_with_params_0': {'max_weight_pi2': 2, 'num_logical_qubits': 5, 'num_of_ppm': 119, 'num_pi2_gates': 57}} +test_clifford_to_ppm() From b282b0bd81e27f9fd84bf0f0f8d9738108fcd77e Mon Sep 17 00:00:00 2001 From: ritu-thombre99 Date: Fri, 13 Jun 2025 19:42:35 -0400 Subject: [PATCH 30/30] lit tests --- frontend/test/lit/test_ppm_specs.py | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/frontend/test/lit/test_ppm_specs.py b/frontend/test/lit/test_ppm_specs.py index 820edb4852..ffe1f1dd30 100644 --- a/frontend/test/lit/test_ppm_specs.py +++ b/frontend/test/lit/test_ppm_specs.py @@ -22,7 +22,14 @@ import pennylane as qml from catalyst import measure, qjit -from catalyst.passes import commute_ppr, merge_ppr_ppm, ppm_compilation, ppr_to_ppm, to_ppr, get_ppm_spec +from catalyst.passes import ( + commute_ppr, + get_ppm_spec, + merge_ppr_ppm, + ppm_compilation, + ppr_to_ppm, + to_ppr, +) def test_convert_clifford_to_ppr(): @@ -45,6 +52,7 @@ def circuit(): print(get_ppm_spec(circuit)) + # CHECK: {'circuit_0': {'max_weight_pi4': 2, 'max_weight_pi8': 1, 'num_logical_qubits': 2, 'num_of_ppm': 1, 'num_pi4_gates': 7, 'num_pi8_gates': 1}} test_convert_clifford_to_ppr() @@ -71,6 +79,7 @@ def cir_commute_ppr(): print(get_ppm_spec(cir_commute_ppr)) + # CHECK: {'cir_commute_ppr_0': {'max_weight_pi4': 2, 'max_weight_pi8': 1, 'num_logical_qubits': 2, 'num_of_ppm': 2, 'num_pi4_gates': 7, 'num_pi8_gates': 2}} test_commute_ppr() @@ -99,6 +108,7 @@ def cir_commute_ppr_max_pauli_size(): print(get_ppm_spec(cir_commute_ppr_max_pauli_size)) + # CHECK: {'cir_commute_ppr_max_pauli_size_0': {'max_weight_pi4': 2, 'max_weight_pi8': 2, 'num_logical_qubits': 2, 'num_of_ppm': 2, 'num_pi4_gates': 10, 'num_pi8_gates': 3}} test_commute_ppr_max_pauli_size() @@ -123,6 +133,7 @@ def cir_merge_ppr_ppm(): print(get_ppm_spec(cir_merge_ppr_ppm)) + # CHECK: {'cir_merge_ppr_ppm_0': {'num_logical_qubits': 2, 'num_of_ppm': 2}} test_merge_ppr_ppm() @@ -148,6 +159,7 @@ def cir_merge_ppr_ppm_max_pauli_size(): print(get_ppm_spec(cir_merge_ppr_ppm_max_pauli_size)) + # CHECK: {'cir_merge_ppr_ppm_max_pauli_size_0': {'max_weight_pi4': 2, 'max_weight_pi8': 1, 'num_logical_qubits': 2, 'num_of_ppm': 2, 'num_pi4_gates': 3, 'num_pi8_gates': 2}} test_merge_ppr_ppm_max_pauli_size() @@ -182,6 +194,7 @@ def cir_inject_magic_state(): print(get_ppm_spec(circuit_ppr_to_ppm)) + # CHECK: {'cir_default_0': {'max_weight_pi2': 1, 'num_logical_qubits': 2, 'num_of_ppm': 2, 'num_pi2_gates': 1}, 'cir_inject_magic_state_0': {'max_weight_pi2': 2, 'num_logical_qubits': 2, 'num_of_ppm': 10, 'num_pi2_gates': 5}} test_ppr_to_ppm() @@ -225,5 +238,6 @@ def cir_clifford_to_ppm_with_params(): print(get_ppm_spec(test_clifford_to_ppm_workflow)) -# CHECK: meow {'cir_clifford_to_ppm_0': {'max_weight_pi2': 2, 'num_logical_qubits': 2, 'num_of_ppm': 8, 'num_pi2_gates': 2}, 'cir_clifford_to_ppm_with_params_0': {'max_weight_pi2': 2, 'num_logical_qubits': 5, 'num_of_ppm': 119, 'num_pi2_gates': 57}} + +# CHECK: {'cir_clifford_to_ppm_0': {'max_weight_pi2': 2, 'num_logical_qubits': 2, 'num_of_ppm': 8, 'num_pi2_gates': 2}, 'cir_clifford_to_ppm_with_params_0': {'max_weight_pi2': 2, 'num_logical_qubits': 5, 'num_of_ppm': 119, 'num_pi2_gates': 57}} test_clifford_to_ppm()