From 59487ef96f01341572b19cea393076c7d8326cba Mon Sep 17 00:00:00 2001 From: Isaac De Vlugt Date: Tue, 30 Sep 2025 09:42:45 -0400 Subject: [PATCH 1/3] quick parse --- doc/releases/changelog-dev.md | 105 +++++++++++++++++----------------- 1 file changed, 52 insertions(+), 53 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index eb193f9254..e81263b9f9 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -87,65 +87,63 @@ * A new quantum compilation pass that reduces the depth and count of non-Clifford Pauli product rotations (PPRs) in circuits is now available. This compilation pass works by commuting - non-Clifford PPRs (often referred to as ``T`` gates) in adjacent + non-Clifford PPRs (often just referred to as ``T`` gates) in adjacent layers and merging compatible ones. More details can be found in Figure 6 of [A Game of Surface Codes](https://arXiv:1808.02892v3). [(#1975)](https://github.com/PennyLaneAI/catalyst/pull/1975) [(#2048)](https://github.com/PennyLaneAI/catalyst/pull/2048) - Consider the following circuit. + Consider the following circuit: ```python import pennylane as qml from catalyst import qjit, measure - from catalyst.passes import to_ppr, commute_ppr, t_layer_reduction, merge_ppr_ppm + from catalyst.passes import to_ppr, commute_ppr, t_layer_reduction, merge_ppr_ppm, ppm_specs pips = [("pipe", ["enforce-runtime-invariants-pipeline"])] + no_reduce_T = { + "to_ppr": {}, + "commute_ppr": {}, + "merge_ppr_ppm": {}, + } - @qjit(pipelines=pips, target="mlir") - @t_layer_reduction - @merge_ppr_ppm - @commute_ppr - @to_ppr - @qml.qnode(qml.device("null.qubit", wires=3)) - def circuit(): - for i in range(3): - qml.H(wires=i) - qml.S(wires=i) - qml.CNOT(wires=[i, (i + 1) % n]) - qml.T(wires=i) - qml.H(wires=i) - qml.T(wires=i) - - return [measure(wires=i) for i in range(n)] - ``` + reduce_T = { + "to_ppr": {}, + "commute_ppr": {}, + "merge_ppr_ppm": {}, + "t_layer_reduction": {} + } - After performing the ``catalyst.passes.to_ppr`` and ``catalyst.passes.merge_ppr_ppm`` - passes, the circuit contains a depth of four of non-Clifford PPRs. Subsequently applying the - ``t_layer_reduction`` pass will move PPRs around via commutation, resulting in a circuit with a - smaller PPR depth of three. + for pipeline in [reduce_T, no_reduce_T]: + + @qjit(pipelines=pips, target="mlir", circuit_transform_pipeline=pipeline) + @qml.qnode(qml.device("null.qubit", wires=3)) + def circuit(): + n = 3 + for i in range(n): + qml.H(wires=i) + qml.S(wires=i) + qml.CNOT(wires=[i, (i + 1) % n]) + qml.T(wires=i) + qml.H(wires=i) + qml.T(wires=i) + + return [measure(wires=i) for i in range(n)] + + print(ppm_specs(circuit)) + ``` ```pycon - >>> print(circuit.mlir_opt) - ... - %1 = quantum.extract %0[ 0] : !quantum.reg -> !quantum.bit - %2 = quantum.extract %0[ 1] : !quantum.reg -> !quantum.bit - // layer 1 - %3 = qec.ppr ["X"](8) %1 : !quantum.bit - %4 = qec.ppr ["X"](8) %2 : !quantum.bit - - // layer 2 - %5 = quantum.extract %0[ 2] : !quantum.reg -> !quantum.bit - %6:2 = qec.ppr ["Y", "X"](8) %3, %4 : !quantum.bit, !quantum.bit - %7 = qec.ppr ["X"](8) %5 : !quantum.bit - %8:3 = qec.ppr ["X", "Y", "X"](8) %6#0, %6#1, %7:!quantum.bit, !quantum.bit, !quantum.bit - - // layer 3 - %9:3 = qec.ppr ["X", "X", "Y"](8) %8#0, %8#1, %8#2:!quantum.bit, !quantum.bit, !quantum.bit - ... + {'circuit_0': {'depth_pi8_ppr': 3, 'depth_ppm': 1, 'logical_qubits': 3, 'max_weight_pi8': 3, 'num_of_ppm': 3, 'pi8_ppr': 6}} + {'circuit_0': {'depth_pi8_ppr': 4, 'depth_ppm': 1, 'logical_qubits': 3, 'max_weight_pi8': 3, 'num_of_ppm': 3, 'pi8_ppr': 6}} ``` + After performing the :func:`~.passes.to_ppr`, :func:`~.passes.commute_ppr`, and :func:`~.passes.merge_ppr_ppm`, + passes, the circuit contains a depth of four of non-Clifford PPRs (`depth_pi8_ppr`). Subsequently applying the + :func:`~.passes.t_layer_reduction` pass will move PPRs around via commutation, resulting in a circuit with a + smaller PPR depth of three. + * Catalyst now provides native support for `SingleExcitation`, `DoubleExcitation`, and `PCPhase` on compatible devices like Lightning simulators. This enhancement avoids unnecessary gate decomposition, @@ -165,14 +163,6 @@ return qml.probs() ``` -

Improvements 🛠

- -* Significantly improved resource tracking with `null.qubit`. - The new tracking has better integration with PennyLane (e.g. for passing the filename to write out), cleaner documentation, and its own wrapper class. - It also now tracks circuit depth, as well as gate counts by number of wires. - [(#2033)](https://github.com/PennyLaneAI/catalyst/pull/2033) - [(#2055)](https://github.com/PennyLaneAI/catalyst/pull/2055) - * Catalyst now supports returning classical and MCM values with the dynamic one-shot MCM method. [(#2004)](https://github.com/PennyLaneAI/catalyst/pull/2004) @@ -202,13 +192,21 @@ True], dtype=bool)) ``` -* Improve the pass `--ppm-specs` to count the depth of PPRs and PPMs in the circuit. - [(#2014)](https://github.com/PennyLaneAI/catalyst/pull/2014) - * The default mid-circuit measurement method in catalyst has been changed from `"single-branch-statistics"` to `"one-shot"`. [[#2017]](https://github.com/PennyLaneAI/catalyst/pull/2017) [[#2019]](https://github.com/PennyLaneAI/catalyst/pull/2019) +

Improvements 🛠

+ +* Significantly improved resource tracking with `null.qubit`. + The new tracking has better integration with PennyLane (e.g. for passing the filename to write out), cleaner documentation, and its own wrapper class. + It also now tracks circuit depth, as well as gate counts by number of wires. + [(#2033)](https://github.com/PennyLaneAI/catalyst/pull/2033) + [(#2055)](https://github.com/PennyLaneAI/catalyst/pull/2055) + +* 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. @@ -324,8 +322,9 @@

Deprecations 👋

-* Deprecated usages of `Device.shots` along with setting `device(..., shots=...)`. +* Deprecated usages of ``Device.shots`` along with setting ``device(..., shots=...)``. Heavily adjusted frontend pipelines within qfunc, tracer, verification and QJITDevice to account for this change. + Please use ``qml.set_shots(shots=...)`` or set shots at the QNode level (i.e., ``qml.QNode(..., shots=...)``). [(#1952)](https://github.com/PennyLaneAI/catalyst/pull/1952)

Bug fixes 🐛

From 005c461f78da17788d0e72d164220ff5cb1709c8 Mon Sep 17 00:00:00 2001 From: Isaac De Vlugt Date: Fri, 3 Oct 2025 17:02:12 -0400 Subject: [PATCH 2/3] merge changes from 2073 manually --- doc/releases/changelog-dev.md | 57 +++++++---------------------------- 1 file changed, 11 insertions(+), 46 deletions(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index e81263b9f9..31bc0c68e3 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -14,8 +14,8 @@ [(#2002)](https://github.com/PennyLaneAI/catalyst/pull/2002) Two new functions, ``qml.allocate()`` and ``qml.deallocate()``, [have been added to - PennyLane](https://docs.pennylane.ai/en/stable/development/release_notes.html#release-0-43-0) to support - dynamic wire allocation. With Catalyst, these features can be accessed on + PennyLane](https://docs.pennylane.ai/en/stable/development/release_notes.html#release-0-43-0) + to support dynamic wire allocation. With Catalyst, these features can be accessed on ``lightning.qubit``, ``lightning.kokkos``, and ``lightning.gpu``. Dynamic wire allocation refers to the allocation of wires in the middle of a circuit, as opposed to the static allocation during device initialization. For example: @@ -26,15 +26,13 @@ @qjit @qml.qnode(qml.device("lightning.qubit", wires=3)) # 3 initial qubits def circuit(): - qml.X(1) # |010> + qml.X(0) # |10> - with qml.allocate(1) as q: # |010> and |0>, 1 dynamically allocted qubit - qml.X(q[0]) # |010> and |1> - qml.CNOT(wires=[q[0], 2]) # |011> and |1> + with qml.allocate(1) as q: # |10> and |0>, 1 dynamically allocated qubit + qml.X(q[0]) # |10> and |1> + qml.CNOT(wires=[q[0], 1]) # |11> and |1> return qml.probs(wires=[0, 1, 2]) - - qml.capture.disable() ``` ```pycon @@ -42,48 +40,15 @@ [0. 0. 0. 1. 0. 0. 0. 0.] ``` - In the above program, 3 qubits are allocated during device initialization, and 1 - additional qubit is allocated inside the circuit with ``qml.allocate(1)``. This is clear - when we inspect the compiled MLIR: - - ``` - >>> print(circuit.mlir) - func.func public @circuit() -> tensor<8xf64> attributes {qnode} { - %c0_i64 = arith.constant 0 : i64 - quantum.device shots(%c0_i64) ["/path/to/liblightning_qubit_catalyst.so", "LightningSimulator", "{'mcmc': False, 'num_burnin': 0, 'kernel_name': None}"] - %0 = quantum.alloc( 3) : !quantum.reg - %1 = quantum.extract %0[ 1] : !quantum.reg -> !quantum.bit - %out_qubits = quantum.custom "PauliX"() %1 : !quantum.bit - %2 = quantum.alloc( 1) : !quantum.reg - %3 = quantum.extract %2[ 0] : !quantum.reg -> !quantum.bit - %out_qubits_0 = quantum.custom "PauliX"() %3 : !quantum.bit - %4 = quantum.extract %0[ 2] : !quantum.reg -> !quantum.bit - %out_qubits_1:2 = quantum.custom "CNOT"() %out_qubits_0, %4 : !quantum.bit, !quantum.bit - %5 = quantum.insert %2[ 0], %out_qubits_1#0 : !quantum.reg, !quantum.bit - quantum.dealloc %5 : !quantum.reg - %6 = quantum.extract %0[ 0] : !quantum.reg -> !quantum.bit - %7 = quantum.compbasis qubits %6, %out_qubits, %out_qubits_1#1 : !quantum.obs - %8 = quantum.probs %7 : tensor<8xf64> - %9 = quantum.insert %0[ 1], %out_qubits : !quantum.reg, !quantum.bit - %10 = quantum.insert %9[ 2], %out_qubits_1#1 : !quantum.reg, !quantum.bit - %11 = quantum.insert %10[ 0], %6 : !quantum.reg, !quantum.bit - quantum.dealloc %11 : !quantum.reg - quantum.device_release - return %8 : tensor<8xf64> - } - ``` - - We can see that there are now 2 pairs of ``quantum.alloc`` and ``quantum.dealloc`` - operations. The quantum register value ``%0`` corresponds to the initial wires on the - device, and the quantum register value ``%2`` corresponds to the dynamically allocated - wire. + In the above program, 2 qubits are allocated during device initialization, and 1 + additional qubit is allocated inside the circuit with ``qml.allocate(1)``. For more information on what ``qml.allocate`` and ``qml.deallocate`` do, please consult the [PennyLane v0.43 release notes](https://docs.pennylane.ai/en/stable/development/release_notes.html#release-0-43-0). However, there are some notable differences between the behaviour of these features - with ``qjit`` versus without. For details, please see - [the relevant sections on the Catalyst sharp bits page](https://docs.pennylane.ai/projects/catalyst/en/stable/dev/sharp_bits.html#functionality-differences-from-pennylane). + with ``qjit`` versus without. For details, please see the relevant sections in the + [Catalyst sharp bits page](https://docs.pennylane.ai/projects/catalyst/en/stable/dev/sharp_bits.html#functionality-differences-from-pennylane). * A new quantum compilation pass that reduces the depth and count of non-Clifford Pauli product rotations (PPRs) in circuits is now available. This compilation pass works by commuting @@ -484,7 +449,7 @@ for example the one-shot mid circuit measurement transform. [(#2057)](https://github.com/PennyLaneAI/catalyst/pull/2057) This pass is part of a bottom-of-stack MBQC execution pathway, with a thin shim between the - PPR/PPM layer and MBQC to enable end-to-end compilation on a mocked backend. Also, in MBQC gate + PPR/PPM layer and MBQC to enable end-to-end compilation on a mocked backend. Also, in an MBQC gate set, one of the gate `RotXZX` cannot yet be executed on available backends. ```python From 4b2f0fd61240d732a78204f7bafe0caf81b27017 Mon Sep 17 00:00:00 2001 From: Isaac De Vlugt Date: Fri, 3 Oct 2025 17:02:44 -0400 Subject: [PATCH 3/3] minor --- doc/releases/changelog-dev.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md index 31bc0c68e3..baadf01c49 100644 --- a/doc/releases/changelog-dev.md +++ b/doc/releases/changelog-dev.md @@ -37,7 +37,7 @@ ```pycon >>> print(circuit()) - [0. 0. 0. 1. 0. 0. 0. 0.] + [0. 0. 0. 1.] ``` In the above program, 2 qubits are allocated during device initialization, and 1