From b48f4d3b1159749fea3a84c794fdfadbf6a2f848 Mon Sep 17 00:00:00 2001 From: sengthai Date: Sat, 30 Aug 2025 13:05:55 -0400 Subject: [PATCH 01/16] Add depth analysis --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 79 +++++++++++++++++++++++ mlir/test/QEC/PPMSpecsTest.mlir | 74 +++++++++++++++++++++ 2 files changed, 153 insertions(+) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index ca921043c2..50da1a51f3 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include "QEC/IR/QECOpInterfaces.h" #define DEBUG_TYPE "ppm-specs" #include @@ -25,6 +26,8 @@ #include "Catalyst/Utils/SCFUtils.h" #include "QEC/IR/QECDialect.h" +#include "QEC/Utils/QECLayer.h" +#include "QEC/Utils/QECOpUtils.h" #include "Quantum/IR/QuantumOps.h" using namespace llvm; @@ -117,6 +120,78 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase return success(); } + bool commuteToLayer(QECOpInterface rhsOp, QECLayer &lhsLayer) + { + for (auto lhsOp : lhsLayer.getOps()) { + if (!commutes(rhsOp, lhsOp)) { + return false; + } + } + return true; + } + + bool isPPR(QECOpInterface op) { return isa(op); } + bool isPPM(QECOpInterface op) { return isa(op); } + + // Check if two ops have the same rotation kind. + bool equalTypes(QECOpInterface lhsOp, QECOpInterface rhsOp) + { + return (isPPR(lhsOp) == isPPR(rhsOp) || isPPM(lhsOp) == isPPM(rhsOp)); + } + + // Add op to current layer if it commutes with the last op in the layer and has the same type. + bool canAddToCurrentLayer(QECOpInterface op, QECLayer ¤tLayer) + { + if (currentLayer.empty()) + return true; + + auto lastOp = currentLayer.getOps().back(); + return equalTypes(lastOp, op) && commuteToLayer(op, currentLayer); + } + + LogicalResult + countLayerDepths(llvm::DenseMap> *PPMSpecs, + llvm::BumpPtrAllocator *stringAllocator) + { + QECLayerContext layerContext; + QECLayer currentLayer(&layerContext); + std::vector layers; + + getOperation()->walk([&](QECOpInterface op) { + if (canAddToCurrentLayer(op, currentLayer)) { + currentLayer.insertToLayer(op); + return WalkResult::skip(); + } + + layers.emplace_back(std::move(currentLayer)); + + currentLayer = QECLayer(&layerContext); + currentLayer.insertToLayer(op); + + return WalkResult::advance(); + }); + + // Add the last layer if it is not empty. + if (!currentLayer.empty()) { + layers.emplace_back(std::move(currentLayer)); + } + + for (auto &layer : layers) { + auto op = layer.getOps().back(); + int16_t absRk = std::abs(static_cast(op.getRotationKind())); + auto parentFuncOp = op->getParentOfType(); + StringRef funcName = parentFuncOp.getName(); + + llvm::StringSaver saver(*stringAllocator); + StringRef key = isPPR(op) ? saver.save("depth_pi" + std::to_string(absRk) + "_gates") + : saver.save("depth_ppm_gates"); + + (*PPMSpecs)[funcName][key]++; + } + + return success(); + } + LogicalResult printSpecs() { llvm::BumpPtrAllocator stringAllocator; @@ -148,6 +223,10 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase } }); + if (failed(countLayerDepths(&PPMSpecs, &stringAllocator))) { + return failure(); + } + if (wr.wasInterrupted()) { return failure(); } diff --git a/mlir/test/QEC/PPMSpecsTest.mlir b/mlir/test/QEC/PPMSpecsTest.mlir index ceeec1e1e8..974141d9d4 100644 --- a/mlir/test/QEC/PPMSpecsTest.mlir +++ b/mlir/test/QEC/PPMSpecsTest.mlir @@ -445,3 +445,77 @@ func.func public @while_error(%arg0: !quantum.bit, %b: i1) { return } + +// ----- + +func.func public @game_of_surface_code_t_layers(%qr0 : !quantum.bit, %qr1 : !quantum.bit, %qr2 : !quantum.bit, %qr3 : !quantum.bit) { + + // CHECK: "test_t_layer_opt_GoSC": { + // CHECK: "depth_pi8_gates": 4, + // CHECK: "max_weight_pi8": 4, + // CHECK: "num_pi8_gates": 6 + // CHECK: }, + + // layer 1 + %0:4 = qec.ppr ["I", "Z", "I", "I"](-8) %qr0, %qr1, %qr2, %qr3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + + // layer 2 + %1:4 = qec.ppr ["X", "Y", "Z", "Y"](8) %0#0, %0#1, %0#2, %0#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + %2:4 = qec.ppr ["X", "I", "Y", "Z"](8) %1#0, %1#1, %1#2, %1#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + + // layer 3 + %3:4 = qec.ppr ["X", "Z", "I", "X"](-8) %2#0, %2#1, %2#2, %2#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + %4:4 = qec.ppr ["X", "Z", "Y", "I"](8) %3#0, %3#1, %3#2, %3#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + + // layer 4 + %5:4 = qec.ppr ["Y", "I", "Y", "Y"](8) %4#0, %4#1, %4#2, %4#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + + func.return +} + +// ----- + +func.func public @game_of_surface_code_t_layers_no_identity(%qr0 : !quantum.bit, %qr1 : !quantum.bit, %qr2 : !quantum.bit, %qr3 : !quantum.bit) { + + // CHECK: "game_of_surface_code_t_layers_no_identity": { + // CHECK: "depth_pi8_gates": 4, + // CHECK: "max_weight_pi8": 4, + // CHECK: "num_pi8_gates": 6 + // CHECK: }, + + // layer 1 + %0 = qec.ppr ["Z"](-8) %qr1 : !quantum.bit + + // layer 2 + %1:4 = qec.ppr ["X", "Y", "Z", "Y"](8) %qr0, %0, %qr2, %qr3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + %2:3 = qec.ppr ["X", "Y", "Z"](8) %1#0, %1#2, %1#3 : !quantum.bit, !quantum.bit, !quantum.bit + + // layer 3 + %3:3 = qec.ppr ["X", "Z", "X"](-8) %2#0, %1#1, %2#2 : !quantum.bit, !quantum.bit, !quantum.bit + %4:3 = qec.ppr ["X", "Z", "Y"](8) %3#0, %3#1, %2#1 : !quantum.bit, !quantum.bit, !quantum.bit + + // layer 4 + %5:3 = qec.ppr ["Y", "Y", "Y"](8) %4#0, %4#2, %3#2 : !quantum.bit, !quantum.bit, !quantum.bit + + func.return +} + +// ----- + + +func.func public @game_of_surface_code_t_layers_opt(%arg0: !quantum.bit, %arg1: !quantum.bit, %arg2: !quantum.bit, %arg3: !quantum.bit) { + + // CHECK: "game_of_surface_code_t_layers_opt": { + // CHECK: "depth_pi8_gates": 2, + // CHECK: "max_weight_pi8": 4, + // CHECK: "num_pi8_gates": 6 + // CHECK: }, + + %0:4 = qec.ppr ["I", "Z", "I", "I"](-8) %arg0, %arg1, %arg2, %arg3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + %1:4 = qec.ppr ["X", "I", "Y", "Z"](8) %0#0, %0#1, %0#2, %0#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + %2:4 = qec.ppr ["X", "Z", "Y", "I"](8) %1#0, %1#1, %1#2, %1#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + %3:4 = qec.ppr ["X", "Y", "Z", "Y"](8) %2#0, %2#1, %2#2, %2#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + %4:4 = qec.ppr ["X", "Z", "I", "X"](-8) %3#0, %3#1, %3#2, %3#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + %5:4 = qec.ppr ["Y", "I", "Y", "Y"](8) %4#0, %4#1, %4#2, %4#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit + return +} From 9129b007ded9e471b8f5c3d1950370eab402d86d Mon Sep 17 00:00:00 2001 From: sengthai Date: Sat, 30 Aug 2025 13:08:28 -0400 Subject: [PATCH 02/16] Improve import fromat --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 50da1a51f3..cfec6bb09a 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "QEC/IR/QECOpInterfaces.h" #define DEBUG_TYPE "ppm-specs" #include @@ -26,6 +25,7 @@ #include "Catalyst/Utils/SCFUtils.h" #include "QEC/IR/QECDialect.h" +#include "QEC/IR/QECOpInterfaces.h" #include "QEC/Utils/QECLayer.h" #include "QEC/Utils/QECOpUtils.h" #include "Quantum/IR/QuantumOps.h" From bd0f325f635b8170f6bd901794acebd20571a3bd Mon Sep 17 00:00:00 2001 From: sengthai Date: Sat, 30 Aug 2025 17:37:38 -0400 Subject: [PATCH 03/16] Improve the algo --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 62 +++++++++++------------ mlir/test/QEC/PPMSpecsTest.mlir | 8 +-- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index cfec6bb09a..a1fe3f691f 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -149,55 +149,46 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase return equalTypes(lastOp, op) && commuteToLayer(op, currentLayer); } - LogicalResult - countLayerDepths(llvm::DenseMap> *PPMSpecs, - llvm::BumpPtrAllocator *stringAllocator) + void countDepths(std::vector &layers, + llvm::DenseMap> *PPMSpecs) { - QECLayerContext layerContext; - QECLayer currentLayer(&layerContext); - std::vector layers; - - getOperation()->walk([&](QECOpInterface op) { - if (canAddToCurrentLayer(op, currentLayer)) { - currentLayer.insertToLayer(op); - return WalkResult::skip(); - } - - layers.emplace_back(std::move(currentLayer)); - - currentLayer = QECLayer(&layerContext); - currentLayer.insertToLayer(op); - - return WalkResult::advance(); - }); - - // Add the last layer if it is not empty. - if (!currentLayer.empty()) { - layers.emplace_back(std::move(currentLayer)); - } - for (auto &layer : layers) { + assert(!layer.empty() && "Layer is empty"); + auto op = layer.getOps().back(); int16_t absRk = std::abs(static_cast(op.getRotationKind())); auto parentFuncOp = op->getParentOfType(); StringRef funcName = parentFuncOp.getName(); - - llvm::StringSaver saver(*stringAllocator); + llvm::BumpPtrAllocator stringAllocator; + llvm::StringSaver saver(stringAllocator); StringRef key = isPPR(op) ? saver.save("depth_pi" + std::to_string(absRk) + "_gates") : saver.save("depth_ppm_gates"); (*PPMSpecs)[funcName][key]++; } - - return success(); } LogicalResult printSpecs() { llvm::BumpPtrAllocator stringAllocator; llvm::DenseMap> PPMSpecs; + + QECLayerContext layerContext; + QECLayer currentLayer(&layerContext); + std::vector layers; + // Walk over all operations in the IR (could be ModuleOp or FuncOp) WalkResult wr = getOperation()->walk([&](Operation *op) { + // Count Depth + if (auto qecOp = dyn_cast(op)) { + if (!canAddToCurrentLayer(qecOp, currentLayer)) { + layers.emplace_back(std::move(currentLayer)); + currentLayer = QECLayer(&layerContext); + } + currentLayer.insertToLayer(qecOp); + } + + // Count Logical Qubit if (isa(op)) { if (failed(countLogicalQubit(op, &PPMSpecs))) { return WalkResult::interrupt(); @@ -205,6 +196,7 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase return WalkResult::advance(); } + // Count PPMs else if (isa(op)) { if (failed(countPPM(cast(op), &PPMSpecs))) { return WalkResult::interrupt(); @@ -212,21 +204,27 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase return WalkResult::advance(); } + // Count PPRs else if (isa(op)) { if (failed(countPPR(cast(op), &PPMSpecs, &stringAllocator))) { return WalkResult::interrupt(); } return WalkResult::advance(); } + + // Skip other ops else { return WalkResult::skip(); } }); - if (failed(countLayerDepths(&PPMSpecs, &stringAllocator))) { - return failure(); + // Add the last layer if it is not empty. + if (!currentLayer.empty()) { + layers.emplace_back(std::move(currentLayer)); } + countDepths(layers, &PPMSpecs); + if (wr.wasInterrupted()) { return failure(); } diff --git a/mlir/test/QEC/PPMSpecsTest.mlir b/mlir/test/QEC/PPMSpecsTest.mlir index 974141d9d4..9de6bf941e 100644 --- a/mlir/test/QEC/PPMSpecsTest.mlir +++ b/mlir/test/QEC/PPMSpecsTest.mlir @@ -450,11 +450,11 @@ func.func public @while_error(%arg0: !quantum.bit, %b: i1) { func.func public @game_of_surface_code_t_layers(%qr0 : !quantum.bit, %qr1 : !quantum.bit, %qr2 : !quantum.bit, %qr3 : !quantum.bit) { - // CHECK: "test_t_layer_opt_GoSC": { + // CHECK: "game_of_surface_code_t_layers": { // CHECK: "depth_pi8_gates": 4, // CHECK: "max_weight_pi8": 4, // CHECK: "num_pi8_gates": 6 - // CHECK: }, + // CHECK: } // layer 1 %0:4 = qec.ppr ["I", "Z", "I", "I"](-8) %qr0, %qr1, %qr2, %qr3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit @@ -481,7 +481,7 @@ func.func public @game_of_surface_code_t_layers_no_identity(%qr0 : !quantum.bit, // CHECK: "depth_pi8_gates": 4, // CHECK: "max_weight_pi8": 4, // CHECK: "num_pi8_gates": 6 - // CHECK: }, + // CHECK: } // layer 1 %0 = qec.ppr ["Z"](-8) %qr1 : !quantum.bit @@ -509,7 +509,7 @@ func.func public @game_of_surface_code_t_layers_opt(%arg0: !quantum.bit, %arg1: // CHECK: "depth_pi8_gates": 2, // CHECK: "max_weight_pi8": 4, // CHECK: "num_pi8_gates": 6 - // CHECK: }, + // CHECK: } %0:4 = qec.ppr ["I", "Z", "I", "I"](-8) %arg0, %arg1, %arg2, %arg3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit %1:4 = qec.ppr ["X", "I", "Y", "Z"](8) %0#0, %0#1, %0#2, %0#3 : !quantum.bit, !quantum.bit, !quantum.bit, !quantum.bit From bb6229369a50bae78421ab2ee9b7a3c81cc93811 Mon Sep 17 00:00:00 2001 From: sengthai Date: Sat, 30 Aug 2025 17:45:18 -0400 Subject: [PATCH 04/16] Added changelog --- doc/releases/changelog-dev.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index a32c1c31b2..318aeef698 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -28,6 +28,9 @@

Improvements 🛠

+* Improve the pass `--ppm-specs` to count the depth of PPRs and PPMs in the circuit. + [(#2014)](https://github.com/PennyLaneAI/catalyst/pull/2014) + * A new pass `--partition-layers` has been added to group PPR/PPM operations into `qec.layer` operations based on qubit interactive and commutativity, enabling circuit analysis and potentially to support parallel execution. @@ -77,7 +80,7 @@ %0 = transform.apply_registered_pass "some-pass" with options = {"an-option" = true, "maxValue" = 1 : i64, "multi-word-option" = 1 : i64} ``` -* Added checks to raise an error when the input qubits to the multi-qubit gates in the runtime CAPI are not all distinct. +* Added checks to raise an error when the input qubits to the multi-qubit gates in the runtime CAPI are not all distinct. [(#2006)](https://github.com/PennyLaneAI/catalyst/pull/2006). * Commuting Clifford Pauli Product Rotation (PPR) operations, past non-Clifford PPRs, now supports P(Ï€/2) Cliffords in addition to P(Ï€/4) @@ -86,7 +89,6 @@ * A new jax primitive `qdealloc_qb_p` is available for single qubit deallocations. [(#2005)](https://github.com/PennyLaneAI/catalyst/pull/2005) -

Breaking changes 💔

* The `shots` property has been removed from `OQDDevice`. The number of shots for a qnode execution is now set directly on the qnode via `qml.set_shots`, From c894d1292947f49e6b96a05536794284e3825997 Mon Sep 17 00:00:00 2001 From: sengthai Date: Sat, 30 Aug 2025 18:00:39 -0400 Subject: [PATCH 05/16] Rename keys --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 4 ++-- mlir/test/QEC/PPMSpecsTest.mlir | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index a1fe3f691f..7708f13cbf 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -161,8 +161,8 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase StringRef funcName = parentFuncOp.getName(); llvm::BumpPtrAllocator stringAllocator; llvm::StringSaver saver(stringAllocator); - StringRef key = isPPR(op) ? saver.save("depth_pi" + std::to_string(absRk) + "_gates") - : saver.save("depth_ppm_gates"); + StringRef key = isPPR(op) ? saver.save("depth_pi" + std::to_string(absRk) + "_ppr") + : saver.save("depth_ppm"); (*PPMSpecs)[funcName][key]++; } diff --git a/mlir/test/QEC/PPMSpecsTest.mlir b/mlir/test/QEC/PPMSpecsTest.mlir index 9de6bf941e..b0d070377c 100644 --- a/mlir/test/QEC/PPMSpecsTest.mlir +++ b/mlir/test/QEC/PPMSpecsTest.mlir @@ -451,7 +451,7 @@ func.func public @while_error(%arg0: !quantum.bit, %b: i1) { func.func public @game_of_surface_code_t_layers(%qr0 : !quantum.bit, %qr1 : !quantum.bit, %qr2 : !quantum.bit, %qr3 : !quantum.bit) { // CHECK: "game_of_surface_code_t_layers": { - // CHECK: "depth_pi8_gates": 4, + // CHECK: "depth_pi8_ppr": 4, // CHECK: "max_weight_pi8": 4, // CHECK: "num_pi8_gates": 6 // CHECK: } @@ -478,7 +478,7 @@ func.func public @game_of_surface_code_t_layers(%qr0 : !quantum.bit, %qr1 : !qua func.func public @game_of_surface_code_t_layers_no_identity(%qr0 : !quantum.bit, %qr1 : !quantum.bit, %qr2 : !quantum.bit, %qr3 : !quantum.bit) { // CHECK: "game_of_surface_code_t_layers_no_identity": { - // CHECK: "depth_pi8_gates": 4, + // CHECK: "depth_pi8_ppr": 4, // CHECK: "max_weight_pi8": 4, // CHECK: "num_pi8_gates": 6 // CHECK: } @@ -506,7 +506,7 @@ func.func public @game_of_surface_code_t_layers_no_identity(%qr0 : !quantum.bit, func.func public @game_of_surface_code_t_layers_opt(%arg0: !quantum.bit, %arg1: !quantum.bit, %arg2: !quantum.bit, %arg3: !quantum.bit) { // CHECK: "game_of_surface_code_t_layers_opt": { - // CHECK: "depth_pi8_gates": 2, + // CHECK: "depth_pi8_ppr": 2, // CHECK: "max_weight_pi8": 4, // CHECK: "num_pi8_gates": 6 // CHECK: } From bffe380f24b46ebf5404d2064f10b10133366e16 Mon Sep 17 00:00:00 2001 From: sengthai Date: Sat, 30 Aug 2025 18:20:13 -0400 Subject: [PATCH 06/16] Fix StringSaver --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 7708f13cbf..3bc7a585b2 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -150,7 +150,8 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase } void countDepths(std::vector &layers, - llvm::DenseMap> *PPMSpecs) + llvm::DenseMap> *PPMSpecs, + llvm::BumpPtrAllocator *stringAllocator) { for (auto &layer : layers) { assert(!layer.empty() && "Layer is empty"); @@ -159,8 +160,7 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase int16_t absRk = std::abs(static_cast(op.getRotationKind())); auto parentFuncOp = op->getParentOfType(); StringRef funcName = parentFuncOp.getName(); - llvm::BumpPtrAllocator stringAllocator; - llvm::StringSaver saver(stringAllocator); + llvm::StringSaver saver(*stringAllocator); StringRef key = isPPR(op) ? saver.save("depth_pi" + std::to_string(absRk) + "_ppr") : saver.save("depth_ppm"); @@ -223,7 +223,7 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase layers.emplace_back(std::move(currentLayer)); } - countDepths(layers, &PPMSpecs); + countDepths(layers, &PPMSpecs, &stringAllocator); if (wr.wasInterrupted()) { return failure(); From 544951e5578496cfa1f05f917f6cc0a57e50ee9d Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Mon, 8 Sep 2025 13:07:46 -0400 Subject: [PATCH 07/16] First commit for adding quantum specs info pass (quantum-specs-info). This is added as a new pass in the Quantum dialect and is based on the ppr-specs pass in the QEC dialect. The specs are written to file as well as sent to stdout. Some other changes include: -Adding hooks to debug into catalyst C-code, through python frontend. -Changed intermediate files directory name to be always same. --- frontend/catalyst/compiler.py | 52 +++++-- frontend/catalyst/debug/__init__.py | 2 +- frontend/catalyst/jit.py | 3 + frontend/catalyst/pipelines.py | 9 +- frontend/catalyst/utils/filesystem.py | 4 +- mlir/cmake/modules/CMakeLists.txt | 2 +- mlir/include/Quantum/Transforms/Passes.h | 1 + mlir/include/Quantum/Transforms/Passes.td | 7 + .../Catalyst/Transforms/RegisterAllPasses.cpp | 1 + mlir/lib/Quantum/Transforms/CMakeLists.txt | 14 ++ .../Quantum/Transforms/QuantumSpecsInfo.cpp | 141 ++++++++++++++++++ 11 files changed, 220 insertions(+), 16 deletions(-) create mode 100644 mlir/lib/Quantum/Transforms/QuantumSpecsInfo.cpp diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index 86c16c20c9..669ad6dda5 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -21,6 +21,7 @@ import pathlib import platform import shutil +import signal import subprocess import sys import tempfile @@ -424,9 +425,9 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): workspace, Directory ), f"Compiler expects a Directory type, got {type(workspace)}." assert workspace.is_dir(), f"Compiler expects an existing directory, got {workspace}." - assert ( - self.options.lower_to_llvm - ), "lower_to_llvm must be set to True in order to compile to a shared object" + #assert ( + # self.options.lower_to_llvm + #), "lower_to_llvm must be set to True in order to compile to a shared object" if self.options.verbose: print(f"[LIB] Running compiler driver in {workspace}", file=self.options.logfile) @@ -444,12 +445,45 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): try: if self.options.verbose: print(f"[SYSTEM] {' '.join(cmd)}", file=self.options.logfile) - result = subprocess.run(cmd, check=True, capture_output=True, text=True) + #result = subprocess.run(cmd, check=True, capture_output=True, text=True) + + if self.options.debug_compiler!=True: + result = subprocess.run(cmd, check=True, capture_output=True, text=True) + if self.options.debug_compiler: + with subprocess.Popen( + cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True + ) as p: + # Ensure process creation succeeds + if p.returncode not in {0, None}: + raise subprocess.CalledProcessError(p.returncode, cmd) + if self.options.debug_compiler: + print(f"Compiler PID={p.pid}") + print( + f"""Ensure C++ debugger is attached and running before continuing with: + kill -s SIGCONT {p.pid}""" + ) + p.send_signal(signal.SIGSTOP) + res_stdout, res_stderr = p.communicate() + # Ensure process execution succeeds + if p.returncode not in {0, None}: + raise subprocess.CalledProcessError(p.returncode, cmd, res_stdout, res_stderr) + if self.options.verbose or os.getenv("ENABLE_DIAGNOSTICS"): - if result.stdout: - print(result.stdout.strip(), file=self.options.logfile) - if result.stderr: - print(result.stderr.strip(), file=self.options.logfile) + #if result.stdout: + # print(result.stdout.strip(), file=self.options.logfile) + #if result.stderr: + # print(result.stderr.strip(), file=self.options.logfile) + if self.options.debug_compiler!=True: + if result.stdout: + print(result.stdout.strip(), file=self.options.logfile) + if result.stderr: + print(result.stderr.strip(), file=self.options.logfile) + if self.options.debug_compiler: + if res_stdout: + print(res_stdout.strip(), file=self.options.logfile) + if res_stderr: + print(res_stderr.strip(), file=self.options.logfile) + except subprocess.CalledProcessError as e: # pragma: nocover raise CompileError(f"catalyst failed with error code {e.returncode}: {e.stderr}") from e @@ -458,7 +492,7 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): out_IR = f.read() else: out_IR = None - + output = LinkerDriver.run(output_object_name, options=self.options) output_object_name = str(pathlib.Path(output).absolute()) diff --git a/frontend/catalyst/debug/__init__.py b/frontend/catalyst/debug/__init__.py index e70e96027f..9b6e00b0f4 100644 --- a/frontend/catalyst/debug/__init__.py +++ b/frontend/catalyst/debug/__init__.py @@ -24,7 +24,7 @@ ) from catalyst.debug.instruments import instrumentation from catalyst.debug.printing import print, print_memref # pylint: disable=redefined-builtin - +from catalyst.debug.debugger import is_debugger_active __all__ = ( "callback", "print", diff --git a/frontend/catalyst/jit.py b/frontend/catalyst/jit.py index d7bdc00375..8dfafdcb64 100644 --- a/frontend/catalyst/jit.py +++ b/frontend/catalyst/jit.py @@ -90,6 +90,7 @@ def qjit( circuit_transform_pipeline=None, pass_plugins=None, dialect_plugins=None, + debug_compiler=False, ): # pylint: disable=too-many-arguments,unused-argument """A just-in-time decorator for PennyLane and JAX programs using Catalyst. @@ -161,6 +162,8 @@ def qjit( If not specified, the default pass pipeline will be applied. pass_plugins (Optional[List[Path]]): List of paths to pass plugins. dialect_plugins (Optional[List[Path]]): List of paths to dialect plugins. + debug_compiler (Optional[bool]): Enable external debugger attachment to the compiler + driver when launching from an active Python debugging environment. Returns: QJIT object. diff --git a/frontend/catalyst/pipelines.py b/frontend/catalyst/pipelines.py index 328a4d6623..5e4545447e 100644 --- a/frontend/catalyst/pipelines.py +++ b/frontend/catalyst/pipelines.py @@ -109,10 +109,12 @@ class CompileOptions: Default is None. pass_plugins (Optional[Iterable[Path]]): List of paths to pass plugins. dialect_plugins (Optional[Iterable[Path]]): List of paths to dialect plugins. + debug_compiler (Optional[bool]): Enable external debugger attachment to the compiler + driver when launching from an active Python debugging environment. """ verbose: Optional[bool] = False - logfile: Optional[TextIOWrapper] = sys.stderr + logfile: Optional[TextIOWrapper] = "test1.txt" #sys.stderr target: Optional[str] = "binary" keep_intermediate: Optional[Union[str, int, bool, KeepIntermediateLevel]] = False pipelines: Optional[List[Any]] = None @@ -122,13 +124,14 @@ class CompileOptions: static_argnums: Optional[Union[int, Iterable[int]]] = None static_argnames: Optional[Union[str, Iterable[str]]] = None abstracted_axes: Optional[Union[Iterable[Iterable[str]], Dict[int, str]]] = None - lower_to_llvm: Optional[bool] = True + lower_to_llvm: Optional[bool] = False #"True" checkpoint_stage: Optional[str] = "" - disable_assertions: Optional[bool] = False + disable_assertions: Optional[bool] = True seed: Optional[int] = None circuit_transform_pipeline: Optional[dict[str, dict[str, str]]] = None pass_plugins: Optional[Set[Path]] = None dialect_plugins: Optional[Set[Path]] = None + debug_compiler: Optional[bool] = False def __post_init__(self): # Convert keep_intermediate to Enum diff --git a/frontend/catalyst/utils/filesystem.py b/frontend/catalyst/utils/filesystem.py index 586e3c16d4..ced47eb018 100644 --- a/frontend/catalyst/utils/filesystem.py +++ b/frontend/catalyst/utils/filesystem.py @@ -121,12 +121,12 @@ def _get_or_create_directory(path, name): preferred_name = name.name # TODO: Maybe just look for the last one? - while curr_preferred_abspath.exists(): + while curr_preferred_abspath.exists() and False: curr_preferred_name_str = preferred_name + "_" + str(count) curr_preferred_name = pathlib.Path(curr_preferred_name_str) curr_preferred_abspath = path / curr_preferred_name count += 1 free_preferred_abspath = pathlib.Path(curr_preferred_abspath) - free_preferred_abspath.mkdir() + free_preferred_abspath.mkdir(exist_ok=True) return free_preferred_abspath diff --git a/mlir/cmake/modules/CMakeLists.txt b/mlir/cmake/modules/CMakeLists.txt index b262fb0b01..b1394ce1a2 100644 --- a/mlir/cmake/modules/CMakeLists.txt +++ b/mlir/cmake/modules/CMakeLists.txt @@ -26,7 +26,7 @@ set(llvm_cmake_builddir "${LLVM_BINARY_DIR}/${LLVM_INSTALL_PACKAGE_DIR}") # Targets with third party dependencies are not exported. get_property(MLIR_EXPORTS GLOBAL PROPERTY MLIR_EXPORTS) -set(TARGETS_TO_REMOVE nlohmann_json tomlplusplus_tomlplusplus ion-transforms CatalystCompilerDriver QECUtils QuantumCAPI qec-transforms) +set(TARGETS_TO_REMOVE nlohmann_json tomlplusplus_tomlplusplus ion-transforms CatalystCompilerDriver QECUtils QuantumCAPI qec-transforms quantum-transforms) list(REMOVE_ITEM MLIR_EXPORTS ${TARGETS_TO_REMOVE}) export(TARGETS ${MLIR_EXPORTS} ExternalStablehloLib FILE ${catalyst_cmake_builddir}/CatalystTargets.cmake) diff --git a/mlir/include/Quantum/Transforms/Passes.h b/mlir/include/Quantum/Transforms/Passes.h index 00f33d8fa4..d619aee3ca 100644 --- a/mlir/include/Quantum/Transforms/Passes.h +++ b/mlir/include/Quantum/Transforms/Passes.h @@ -34,5 +34,6 @@ std::unique_ptr createDisentangleCNOTPass(); std::unique_ptr createDisentangleSWAPPass(); std::unique_ptr createIonsDecompositionPass(); std::unique_ptr createLoopBoundaryOptimizationPass(); +std::unique_ptr createQuantumSpecsInfoPass(); } // namespace catalyst diff --git a/mlir/include/Quantum/Transforms/Passes.td b/mlir/include/Quantum/Transforms/Passes.td index 5f67cecd27..56fc358c0e 100644 --- a/mlir/include/Quantum/Transforms/Passes.td +++ b/mlir/include/Quantum/Transforms/Passes.td @@ -129,4 +129,11 @@ def LoopBoundaryOptimizationPass : Pass<"loop-boundary"> { } // ----- Quantum circuit transformation passes end ----- // + +def QuantumSpecsInfoPass : Pass<"quantum-specs-info"> { + let summary = "Informative pass to provide quantum specs, like gate counts."; + + let constructor = "catalyst::createQuantumSpecsInfoPass()"; +} + #endif // QUANTUM_PASSES diff --git a/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp b/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp index 0e7be8337b..f2d9b8770a 100644 --- a/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp +++ b/mlir/lib/Catalyst/Transforms/RegisterAllPasses.cpp @@ -75,4 +75,5 @@ void catalyst::registerAllCatalystPasses() mlir::registerPass(catalyst::createSplitMultipleTapesPass); mlir::registerPass(catalyst::createTestPass); mlir::registerPass(catalyst::createTLayerReductionPass); + mlir::registerPass(catalyst::createQuantumSpecsInfoPass); } diff --git a/mlir/lib/Quantum/Transforms/CMakeLists.txt b/mlir/lib/Quantum/Transforms/CMakeLists.txt index 3a244ac4d6..5998e84c6c 100644 --- a/mlir/lib/Quantum/Transforms/CMakeLists.txt +++ b/mlir/lib/Quantum/Transforms/CMakeLists.txt @@ -20,6 +20,7 @@ file(GLOB SRC IonsDecompositionPatterns.cpp loop_boundary_optimization.cpp LoopBoundaryOptimizationPatterns.cpp + QuantumSpecsInfo.cpp ) get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS) @@ -36,6 +37,19 @@ set(DEPENDS add_mlir_library(${LIBRARY_NAME} STATIC ${SRC} LINK_LIBS PRIVATE ${LIBS} DEPENDS ${DEPENDS}) target_compile_features(${LIBRARY_NAME} PUBLIC cxx_std_20) + +target_link_libraries(${LIBRARY_NAME} + PRIVATE nlohmann_json::nlohmann_json + ) + +# Disable warning from json library +if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") +set_source_files_properties( + QuantumSpecsInfo.cpp + COMPILE_FLAGS "-Wno-covered-switch-default" +) +endif() + target_include_directories(${LIBRARY_NAME} PUBLIC . ${PROJECT_SOURCE_DIR}/include diff --git a/mlir/lib/Quantum/Transforms/QuantumSpecsInfo.cpp b/mlir/lib/Quantum/Transforms/QuantumSpecsInfo.cpp new file mode 100644 index 0000000000..30a0edeb6a --- /dev/null +++ b/mlir/lib/Quantum/Transforms/QuantumSpecsInfo.cpp @@ -0,0 +1,141 @@ +// 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 "quantum-specs-info" + +#include + +#include "mlir/Dialect/Func/IR/FuncOps.h" +#include "mlir/Pass/Pass.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" + +#include "Quantum/IR/QuantumDialect.h" +#include "Quantum/IR/QuantumOps.h" +#include "Quantum/IR/QuantumInterfaces.h" +#include "QEC/IR/QECDialect.h" + +using namespace llvm; +using namespace mlir; +using namespace catalyst; +using namespace catalyst::quantum; +using namespace catalyst::qec; +using json = nlohmann::json; + +namespace catalyst { +namespace quantum { + +#define GEN_PASS_DEF_QUANTUMSPECSINFOPASS +#define GEN_PASS_DECL_QUANTUMSPECSINFOPASS + +#include "Quantum/Transforms/Passes.h.inc" + +struct QuantumSpecsInfoPass : public impl::QuantumSpecsInfoPassBase { + using impl::QuantumSpecsInfoPassBase::QuantumSpecsInfoPassBase; + + LogicalResult printQuantumSpecs() + { + llvm::BumpPtrAllocator stringAllocator; + llvm::DenseMap> PPMSpecs; + + int numQuantumGates=0; + int numMeasureOp=0; + int numPPRotation=0; + int numPPMeasurement=0; + int numTotal=0; + + // Walk over all operations in the IR (could be ModuleOp or FuncOp) + WalkResult wr = getOperation()->walk([&](Operation *op) { + + //if (auto qOp = dyn_cast(op)) { + if (isa(op)) { + auto parentFuncOp = op->getParentOfType(); + StringRef funcName = parentFuncOp.getName(); + + //llvm::errs() << funcName; + PPMSpecs[funcName]["QuantumGate_Count"] = ++numQuantumGates; + PPMSpecs[funcName]["Total_Count"] = ++numTotal; + //numQuantumGates++; + return WalkResult::advance(); + } + + if (isa(op)) { + auto parentFuncOp = op->getParentOfType(); + StringRef funcName = parentFuncOp.getName(); + + //llvm::errs() << funcName; + PPMSpecs[funcName]["MeasureOp_Count"] = ++numMeasureOp; + PPMSpecs[funcName]["Total_Count"] = ++numTotal; + //numQuantumGates++; + return WalkResult::advance(); + } + + // Count PPMs + else if (isa(op)) { + auto parentFuncOp = op->getParentOfType(); + StringRef funcName = parentFuncOp.getName(); + PPMSpecs[funcName]["PPMeasurement_Count"] = ++numPPMeasurement; + PPMSpecs[funcName]["Total_Count"] = ++numTotal; + return WalkResult::advance(); + } + + // Count PPRs + else if (isa(op)) { + auto parentFuncOp = op->getParentOfType(); + StringRef funcName = parentFuncOp.getName(); + PPMSpecs[funcName]["PPRotation_Count"] = ++numPPRotation; + PPMSpecs[funcName]["Total_Count"] = ++numTotal; + return WalkResult::advance(); + } + + // Skip other ops + else { + return WalkResult::skip(); + } + + }); + + if (wr.wasInterrupted()) { + return failure(); + } + + std::error_code EC; + //llvm::raw_fd_ostream fileOutputStream("test.txt", EC, llvm::sys::fs::OF_None); + llvm::raw_fd_ostream fileOutputStream("test.txt", EC, llvm::sys::fs::OF_Append); + if (EC) { + llvm::errs() << "Error opening file: " << EC.message() << "\n"; + return failure(); // Handle error + } + json PPMSpecsJson = PPMSpecs; + llvm::outs() << PPMSpecsJson.dump(4) + << "\n"; // dump(4) makes an indent with 4 spaces when printing JSON + fileOutputStream << PPMSpecsJson.dump(4) + << "\n"; // dump(4) makes an indent with 4 spaces when printing JSON + fileOutputStream.flush(); + return success(); + } + + void runOnOperation() final + { + if (failed(printQuantumSpecs())) { + signalPassFailure(); + } + } +}; + +} //namespace quantum + +std::unique_ptr createQuantumSpecsInfoPass() { return std::make_unique(); } + +} // namespace catalyst \ No newline at end of file From 0fedea0c1440bcf0e83f0b721604035240f709b7 Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Mon, 8 Sep 2025 13:12:43 -0400 Subject: [PATCH 08/16] Test file for quantum-specs-info pass. --- mlir/test/Quantum/QuantumSpecsInfoTest.mlir | 46 +++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 mlir/test/Quantum/QuantumSpecsInfoTest.mlir diff --git a/mlir/test/Quantum/QuantumSpecsInfoTest.mlir b/mlir/test/Quantum/QuantumSpecsInfoTest.mlir new file mode 100644 index 0000000000..b2793ee7cd --- /dev/null +++ b/mlir/test/Quantum/QuantumSpecsInfoTest.mlir @@ -0,0 +1,46 @@ +// 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-specs-info --split-input-file -verify-diagnostics %s | FileCheck %s + +//CHECK: { +//CHECK: "circuit_0": { +//CHECK: "PPMeasurement_Count": 1, +//CHECK: "PPRotation_Count": 6, +//CHECK: "QuantumGate_Count": 1, +//CHECK: "Total_Count": 8 +//CHECK: } +//CHECK: } +func.func public @circuit_0() -> tensor attributes {diff_method = "adjoint", llvm.linkage = #llvm.linkage, qnode} { + %cst = arith.constant 1.5707963267948966 : f64 + %c0_i64 = arith.constant 0 : i64 + quantum.device shots(%c0_i64) ["/home/zorawar.bassi/pyenv/versions/pennylaneB/lib/python3.13/site-packages/pennylane_lightning/liblightning_qubit_catalyst.so", "LightningSimulator", "{'mcmc': False, 'num_burnin': 0, 'kernel_name': None}"] + %0 = quantum.alloc( 2) : !quantum.reg + %1 = quantum.extract %0[ 1] : !quantum.reg -> !quantum.bit + %2 = qec.ppr ["Z"](4) %1 : !quantum.bit + %3 = qec.ppr ["X"](4) %2 : !quantum.bit + %4 = qec.ppr ["Z"](4) %3 : !quantum.bit + %5 = quantum.extract %0[ 0] : !quantum.reg -> !quantum.bit + %out_qubits:2 = quantum.multirz(%cst) %5, %4 : !quantum.bit, !quantum.bit + %6 = qec.ppr ["Z"](4) %out_qubits#1 : !quantum.bit + %7 = qec.ppr ["X"](4) %6 : !quantum.bit + %8 = qec.ppr ["Z"](4) %7 : !quantum.bit + %mres, %out_qubits_0 = qec.ppm ["Z"] %8 : !quantum.bit + %from_elements = tensor.from_elements %mres : tensor + %9 = quantum.insert %0[ 1], %out_qubits_0 : !quantum.reg, !quantum.bit + %10 = quantum.insert %9[ 0], %out_qubits#0 : !quantum.reg, !quantum.bit + quantum.dealloc %10 : !quantum.reg + quantum.device_release + return %from_elements : tensor +} From 0f9c9a47f4d703b319aa615c98b26fe6a99c489d Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Mon, 8 Sep 2025 16:55:28 -0400 Subject: [PATCH 09/16] Fix formatting --- frontend/catalyst/compiler.py | 28 +++++----- frontend/catalyst/debug/__init__.py | 1 + frontend/catalyst/pipelines.py | 4 +- .../Quantum/Transforms/QuantumSpecsInfo.cpp | 51 ++++++++----------- 4 files changed, 39 insertions(+), 45 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index 669ad6dda5..d32300a006 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -425,9 +425,9 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): workspace, Directory ), f"Compiler expects a Directory type, got {type(workspace)}." assert workspace.is_dir(), f"Compiler expects an existing directory, got {workspace}." - #assert ( - # self.options.lower_to_llvm - #), "lower_to_llvm must be set to True in order to compile to a shared object" + # assert ( + # self.options.lower_to_llvm + # ), "lower_to_llvm must be set to True in order to compile to a shared object" if self.options.verbose: print(f"[LIB] Running compiler driver in {workspace}", file=self.options.logfile) @@ -445,9 +445,9 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): try: if self.options.verbose: print(f"[SYSTEM] {' '.join(cmd)}", file=self.options.logfile) - #result = subprocess.run(cmd, check=True, capture_output=True, text=True) - - if self.options.debug_compiler!=True: + # result = subprocess.run(cmd, check=True, capture_output=True, text=True) + + if self.options.debug_compiler != True: result = subprocess.run(cmd, check=True, capture_output=True, text=True) if self.options.debug_compiler: with subprocess.Popen( @@ -466,14 +466,16 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): res_stdout, res_stderr = p.communicate() # Ensure process execution succeeds if p.returncode not in {0, None}: - raise subprocess.CalledProcessError(p.returncode, cmd, res_stdout, res_stderr) + raise subprocess.CalledProcessError( + p.returncode, cmd, res_stdout, res_stderr + ) if self.options.verbose or os.getenv("ENABLE_DIAGNOSTICS"): - #if result.stdout: - # print(result.stdout.strip(), file=self.options.logfile) - #if result.stderr: - # print(result.stderr.strip(), file=self.options.logfile) - if self.options.debug_compiler!=True: + # if result.stdout: + # print(result.stdout.strip(), file=self.options.logfile) + # if result.stderr: + # print(result.stderr.strip(), file=self.options.logfile) + if self.options.debug_compiler != True: if result.stdout: print(result.stdout.strip(), file=self.options.logfile) if result.stderr: @@ -492,7 +494,7 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): out_IR = f.read() else: out_IR = None - + output = LinkerDriver.run(output_object_name, options=self.options) output_object_name = str(pathlib.Path(output).absolute()) diff --git a/frontend/catalyst/debug/__init__.py b/frontend/catalyst/debug/__init__.py index 9b6e00b0f4..294d5b06f2 100644 --- a/frontend/catalyst/debug/__init__.py +++ b/frontend/catalyst/debug/__init__.py @@ -25,6 +25,7 @@ from catalyst.debug.instruments import instrumentation from catalyst.debug.printing import print, print_memref # pylint: disable=redefined-builtin from catalyst.debug.debugger import is_debugger_active + __all__ = ( "callback", "print", diff --git a/frontend/catalyst/pipelines.py b/frontend/catalyst/pipelines.py index 5e4545447e..c8e45e4ce7 100644 --- a/frontend/catalyst/pipelines.py +++ b/frontend/catalyst/pipelines.py @@ -114,7 +114,7 @@ class CompileOptions: """ verbose: Optional[bool] = False - logfile: Optional[TextIOWrapper] = "test1.txt" #sys.stderr + logfile: Optional[TextIOWrapper] = "test1.txt" # sys.stderr target: Optional[str] = "binary" keep_intermediate: Optional[Union[str, int, bool, KeepIntermediateLevel]] = False pipelines: Optional[List[Any]] = None @@ -124,7 +124,7 @@ class CompileOptions: static_argnums: Optional[Union[int, Iterable[int]]] = None static_argnames: Optional[Union[str, Iterable[str]]] = None abstracted_axes: Optional[Union[Iterable[Iterable[str]], Dict[int, str]]] = None - lower_to_llvm: Optional[bool] = False #"True" + lower_to_llvm: Optional[bool] = False # "True" checkpoint_stage: Optional[str] = "" disable_assertions: Optional[bool] = True seed: Optional[int] = None diff --git a/mlir/lib/Quantum/Transforms/QuantumSpecsInfo.cpp b/mlir/lib/Quantum/Transforms/QuantumSpecsInfo.cpp index 30a0edeb6a..ead6fb8862 100644 --- a/mlir/lib/Quantum/Transforms/QuantumSpecsInfo.cpp +++ b/mlir/lib/Quantum/Transforms/QuantumSpecsInfo.cpp @@ -14,17 +14,16 @@ #define DEBUG_TYPE "quantum-specs-info" -#include - #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/Pass/Pass.h" #include "llvm/Support/Debug.h" #include "llvm/Support/FileSystem.h" +#include +#include "QEC/IR/QECDialect.h" #include "Quantum/IR/QuantumDialect.h" -#include "Quantum/IR/QuantumOps.h" #include "Quantum/IR/QuantumInterfaces.h" -#include "QEC/IR/QECDialect.h" +#include "Quantum/IR/QuantumOps.h" using namespace llvm; using namespace mlir; @@ -49,38 +48,31 @@ struct QuantumSpecsInfoPass : public impl::QuantumSpecsInfoPassBase> PPMSpecs; - int numQuantumGates=0; - int numMeasureOp=0; - int numPPRotation=0; - int numPPMeasurement=0; - int numTotal=0; + int numQuantumGates = 0; + int numMeasureOp = 0; + int numPPRotation = 0; + int numPPMeasurement = 0; + int numTotal = 0; // Walk over all operations in the IR (could be ModuleOp or FuncOp) WalkResult wr = getOperation()->walk([&](Operation *op) { - - //if (auto qOp = dyn_cast(op)) { - if (isa(op)) { + if (isa(op)) { auto parentFuncOp = op->getParentOfType(); StringRef funcName = parentFuncOp.getName(); - - //llvm::errs() << funcName; + // llvm::errs() << funcName; PPMSpecs[funcName]["QuantumGate_Count"] = ++numQuantumGates; PPMSpecs[funcName]["Total_Count"] = ++numTotal; - //numQuantumGates++; return WalkResult::advance(); } - if (isa(op)) { + if (isa(op)) { auto parentFuncOp = op->getParentOfType(); StringRef funcName = parentFuncOp.getName(); - - //llvm::errs() << funcName; + // llvm::errs() << funcName; PPMSpecs[funcName]["MeasureOp_Count"] = ++numMeasureOp; PPMSpecs[funcName]["Total_Count"] = ++numTotal; - //numQuantumGates++; return WalkResult::advance(); } - // Count PPMs else if (isa(op)) { auto parentFuncOp = op->getParentOfType(); @@ -89,7 +81,6 @@ struct QuantumSpecsInfoPass : public impl::QuantumSpecsInfoPassBase(op)) { auto parentFuncOp = op->getParentOfType(); @@ -98,20 +89,17 @@ struct QuantumSpecsInfoPass : public impl::QuantumSpecsInfoPassBase createQuantumSpecsInfoPass() { return std::make_unique(); } +std::unique_ptr createQuantumSpecsInfoPass() +{ + return std::make_unique(); +} } // namespace catalyst \ No newline at end of file From 9bff7cc6bda759a563e929c385746a3b945de1c0 Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Tue, 9 Sep 2025 11:23:21 -0400 Subject: [PATCH 10/16] Fix error due to incorrect merging with stashed results. --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index 7cf9393d3c..d9aa22eca6 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -19,6 +19,9 @@ #include +// #include "llvm/Support/raw_ostream.h" +#include "llvm/Support/FileSystem.h" + #include "mlir/Dialect/Func/IR/FuncOps.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Pass/Pass.h" @@ -26,6 +29,7 @@ #include "Catalyst/Utils/SCFUtils.h" #include "QEC/IR/QECDialect.h" #include "QEC/IR/QECOpInterfaces.h" +//#include "QEC/Utils/PauliStringWrapper.h" #include "QEC/Utils/QECLayer.h" #include "QEC/Utils/QECOpUtils.h" #include "Quantum/IR/QuantumOps.h" @@ -218,13 +222,6 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase } }); - // Add the last layer if it is not empty. - if (!currentLayer.empty()) { - layers.emplace_back(std::move(currentLayer)); - } - - countDepths(layers, &PPMSpecs, &stringAllocator); - if (wr.wasInterrupted()) { return failure(); } @@ -236,9 +233,18 @@ struct CountPPMSpecsPass : public impl::CountPPMSpecsPassBase countDepths(layers, PPMSpecs, stringAllocator); + std::error_code EC; + llvm::raw_fd_ostream fileOutputStream("test.txt", EC, llvm::sys::fs::OF_Append); + if (EC) { + llvm::errs() << "Error opening file: " << EC.message() << "\n"; + return failure(); // Handle error + } json PPMSpecsJson = PPMSpecs; llvm::outs() << PPMSpecsJson.dump(4) << "\n"; // dump(4) makes an indent with 4 spaces when printing JSON + fileOutputStream << PPMSpecsJson.dump(4) + << "\n"; // dump(4) makes an indent with 4 spaces when printing JSON + fileOutputStream.flush(); return success(); } From 6c8ae3be3f9de60c1d32592c00701ccbd1550d5d Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Tue, 9 Sep 2025 11:37:05 -0400 Subject: [PATCH 11/16] Formatting fix --- mlir/lib/QEC/Transforms/CountPPMSpecs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp index d9aa22eca6..5d2b6191f8 100644 --- a/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp +++ b/mlir/lib/QEC/Transforms/CountPPMSpecs.cpp @@ -29,7 +29,7 @@ #include "Catalyst/Utils/SCFUtils.h" #include "QEC/IR/QECDialect.h" #include "QEC/IR/QECOpInterfaces.h" -//#include "QEC/Utils/PauliStringWrapper.h" +// #include "QEC/Utils/PauliStringWrapper.h" #include "QEC/Utils/QECLayer.h" #include "QEC/Utils/QECOpUtils.h" #include "Quantum/IR/QuantumOps.h" From 505d167fe49948e13bdbd846c346bd66b05ced58 Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Tue, 9 Sep 2025 11:46:50 -0400 Subject: [PATCH 12/16] Formatting fix --- frontend/catalyst/debug/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/catalyst/debug/__init__.py b/frontend/catalyst/debug/__init__.py index 294d5b06f2..0485022206 100644 --- a/frontend/catalyst/debug/__init__.py +++ b/frontend/catalyst/debug/__init__.py @@ -22,6 +22,7 @@ get_compilation_stages_groups, replace_ir, ) + from catalyst.debug.instruments import instrumentation from catalyst.debug.printing import print, print_memref # pylint: disable=redefined-builtin from catalyst.debug.debugger import is_debugger_active From 59a1979fb5b3503b5b076ff403fee85fc9b87e8c Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Tue, 9 Sep 2025 11:49:27 -0400 Subject: [PATCH 13/16] Update __init__.py --- frontend/catalyst/debug/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/catalyst/debug/__init__.py b/frontend/catalyst/debug/__init__.py index 0485022206..b4c604d64e 100644 --- a/frontend/catalyst/debug/__init__.py +++ b/frontend/catalyst/debug/__init__.py @@ -23,9 +23,9 @@ replace_ir, ) +from catalyst.debug.debugger import is_debugger_active from catalyst.debug.instruments import instrumentation from catalyst.debug.printing import print, print_memref # pylint: disable=redefined-builtin -from catalyst.debug.debugger import is_debugger_active __all__ = ( "callback", From 0f3381cf9a91604cb11aaf8c812b47e02b35abb3 Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Tue, 9 Sep 2025 11:56:02 -0400 Subject: [PATCH 14/16] Update __init__.py --- frontend/catalyst/debug/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/catalyst/debug/__init__.py b/frontend/catalyst/debug/__init__.py index b4c604d64e..0726fb542a 100644 --- a/frontend/catalyst/debug/__init__.py +++ b/frontend/catalyst/debug/__init__.py @@ -22,7 +22,6 @@ get_compilation_stages_groups, replace_ir, ) - from catalyst.debug.debugger import is_debugger_active from catalyst.debug.instruments import instrumentation from catalyst.debug.printing import print, print_memref # pylint: disable=redefined-builtin From 92f52af8c9b4a151cb6c68fb5cde93687384c83d Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Tue, 9 Sep 2025 12:08:00 -0400 Subject: [PATCH 15/16] Update compiler.py --- frontend/catalyst/compiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index d32300a006..439cf77268 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -449,7 +449,7 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): if self.options.debug_compiler != True: result = subprocess.run(cmd, check=True, capture_output=True, text=True) - if self.options.debug_compiler: + else: # self.options.debug_compiler == True: with subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) as p: @@ -480,7 +480,7 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): print(result.stdout.strip(), file=self.options.logfile) if result.stderr: print(result.stderr.strip(), file=self.options.logfile) - if self.options.debug_compiler: + else: # self.options.debug_compiler == True: if res_stdout: print(res_stdout.strip(), file=self.options.logfile) if res_stderr: From e053012718611fad792a82e0467f2063cdcd8271 Mon Sep 17 00:00:00 2001 From: zsb-xqc Date: Tue, 9 Sep 2025 12:14:37 -0400 Subject: [PATCH 16/16] Update compiler.py --- frontend/catalyst/compiler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/catalyst/compiler.py b/frontend/catalyst/compiler.py index 439cf77268..3176ff170e 100644 --- a/frontend/catalyst/compiler.py +++ b/frontend/catalyst/compiler.py @@ -449,7 +449,7 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): if self.options.debug_compiler != True: result = subprocess.run(cmd, check=True, capture_output=True, text=True) - else: # self.options.debug_compiler == True: + else: # self.options.debug_compiler == True: with subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) as p: @@ -480,7 +480,7 @@ def run_from_ir(self, ir: str, module_name: str, workspace: Directory): print(result.stdout.strip(), file=self.options.logfile) if result.stderr: print(result.stderr.strip(), file=self.options.logfile) - else: # self.options.debug_compiler == True: + else: # self.options.debug_compiler == True: if res_stdout: print(res_stdout.strip(), file=self.options.logfile) if res_stderr: