Skip to content

Conversation

@paul0403
Copy link
Member

Context:
Merge v0.13.0-rc back into main.

lazypanda10117 and others added 27 commits October 6, 2025 16:24
… 1 (#2090)

**Context:** Dynamic one-shot execution causes a crash or yields
incorrect output when we set `shots=1`. (See related issues #2087 and
#2088 for details). This issue was caused by an oversight when using the
function `jnp.squeeze`. When `shots=1`, the code removes the first axis
of the output array, which corresponds to the number of shots. This
interferes with subsequent calculations and leads to the crashes and
incorrect results as mentioned.

**Description of the Change:** 
This change modifies the output handling to correctly preserve the shots
dimension in the case when `shots=1`.

closes #2087 #2088

[[sc-100800]]
[[sc-100803]]

---------

Co-authored-by: Isaac De Vlugt <[email protected]>
Co-authored-by: Ali Asadi <[email protected]>
Co-authored-by: ringo-but-quantum <[email protected]>
Co-authored-by: Paul <[email protected]>
Rename the`t_layer_reduction` transform to `reduce_t_depth` for more
user-friendly

[[sc-99454]]
… parameters (#2099)

**Context:**
Fix the issue with compiling decomposition_rules with flatten list of
parameters

``` python
qml.capture.enable()
qml.decomposition.enable_graph()

@qjit(target="mlir")
@partial(qml.transforms.decompose, gate_set=[qml.RX, qml.RY, qml.RZ])
@qml.qnode(qml.device(backend, wires=2))
def captured_circuit(x: float, y: float, z: float):
    qml.Rot(x, y, z, 0)
    return qml.expval(qml.PauliZ(0))

capture_result = captured_circuit(1.5, 2.5, 3.5)

qml.decomposition.disable_graph()
qml.capture.disable()

print(captured_circuit.mlir)
```

Compiles the Rot decomp rule to:

``` llvm
 func.func public @_rot_to_rz_ry_rz(%arg0: !quantum.reg, %arg1: tensor<f64>, %arg2: tensor<f64>, %arg3: tensor<f64>, %arg4: tensor<1xi64>) -> !quantum.reg attributes {llvm.linkage = #llvm.linkage<internal>, num_wires = 1 : i64, target_gate = "Rot"} {
      %0 = stablehlo.slice %arg4 [0:1] : (tensor<1xi64>) -> tensor<1xi64>
      %1 = stablehlo.reshape %0 : (tensor<1xi64>) -> tensor<i64>
      %extracted = tensor.extract %1[] : tensor<i64>
      %2 = quantum.extract %arg0[%extracted] : !quantum.reg -> !quantum.bit
      %extracted_0 = tensor.extract %arg2[] : tensor<f64>
      %out_qubits = quantum.custom "RZ"(%extracted_0) %2 : !quantum.bit
      %3 = stablehlo.slice %arg4 [0:1] : (tensor<1xi64>) -> tensor<1xi64>
      %4 = stablehlo.reshape %3 : (tensor<1xi64>) -> tensor<i64>
      %extracted_1 = tensor.extract %1[] : tensor<i64>
      %5 = quantum.insert %arg0[%extracted_1], %out_qubits : !quantum.reg, !quantum.bit
      %extracted_2 = tensor.extract %4[] : tensor<i64>
      %6 = quantum.extract %5[%extracted_2] : !quantum.reg -> !quantum.bit
      %extracted_3 = tensor.extract %arg3[] : tensor<f64>
      %out_qubits_4 = quantum.custom "RY"(%extracted_3) %6 : !quantum.bit
      %7 = stablehlo.slice %arg4 [0:1] : (tensor<1xi64>) -> tensor<1xi64>
      %8 = stablehlo.reshape %7 : (tensor<1xi64>) -> tensor<i64>
      %extracted_5 = tensor.extract %4[] : tensor<i64>
      %9 = quantum.insert %5[%extracted_5], %out_qubits_4 : !quantum.reg, !quantum.bit
      %extracted_6 = tensor.extract %8[] : tensor<i64>
      %10 = quantum.extract %9[%extracted_6] : !quantum.reg -> !quantum.bit
      %extracted_7 = tensor.extract %arg1[] : tensor<f64>
      %out_qubits_8 = quantum.custom "RZ"(%extracted_7) %10 : !quantum.bit
      %extracted_9 = tensor.extract %8[] : tensor<i64>
      %11 = quantum.insert %9[%extracted_9], %out_qubits_8 : !quantum.reg, !quantum.bit
      return %11 : !quantum.reg
    }
```

The results in a decomposition function RZ RY RZ with the params order:
param1, param2, param 0 instead of params arg0 (param 0), arg1 (param 1)
and arg2 (param 2).

The issue is coming from the fact that JAX Tracer doesn't preserve the
order of keyword arguments passed from Python in the partial
initialization of rules. We are now passing arguments as 'unnamed' but
ordered args.


``` llvm
    func.func public @_rot_to_rz_ry_rz(%arg0: !quantum.reg, %arg1: tensor<f64>, %arg2: tensor<f64>, %arg3: tensor<f64>, %arg4: tensor<1xi64>) -> !quantum.reg attributes {llvm.linkage = #llvm.linkage<internal>, num_wires = 1 : i64, target_gate = "Rot"} {
      %0 = stablehlo.slice %arg4 [0:1] : (tensor<1xi64>) -> tensor<1xi64>
      %1 = stablehlo.reshape %0 : (tensor<1xi64>) -> tensor<i64>
      %extracted = tensor.extract %1[] : tensor<i64>
      %2 = quantum.extract %arg0[%extracted] : !quantum.reg -> !quantum.bit
      %extracted_0 = tensor.extract %arg1[] : tensor<f64>
      %out_qubits = quantum.custom "RZ"(%extracted_0) %2 : !quantum.bit
      %3 = stablehlo.slice %arg4 [0:1] : (tensor<1xi64>) -> tensor<1xi64>
      %4 = stablehlo.reshape %3 : (tensor<1xi64>) -> tensor<i64>
      %extracted_1 = tensor.extract %1[] : tensor<i64>
      %5 = quantum.insert %arg0[%extracted_1], %out_qubits : !quantum.reg, !quantum.bit
      %extracted_2 = tensor.extract %4[] : tensor<i64>
      %6 = quantum.extract %5[%extracted_2] : !quantum.reg -> !quantum.bit
      %extracted_3 = tensor.extract %arg2[] : tensor<f64>
      %out_qubits_4 = quantum.custom "RY"(%extracted_3) %6 : !quantum.bit
      %7 = stablehlo.slice %arg4 [0:1] : (tensor<1xi64>) -> tensor<1xi64>
      %8 = stablehlo.reshape %7 : (tensor<1xi64>) -> tensor<i64>
      %extracted_5 = tensor.extract %4[] : tensor<i64>
      %9 = quantum.insert %5[%extracted_5], %out_qubits_4 : !quantum.reg, !quantum.bit
      %extracted_6 = tensor.extract %8[] : tensor<i64>
      %10 = quantum.extract %9[%extracted_6] : !quantum.reg -> !quantum.bit
      %extracted_7 = tensor.extract %arg3[] : tensor<f64>
      %out_qubits_8 = quantum.custom "RZ"(%extracted_7) %10 : !quantum.bit
      %extracted_9 = tensor.extract %8[] : tensor<i64>
      %11 = quantum.insert %9[%extracted_9], %out_qubits_8 : !quantum.reg, !quantum.bit
      return %11 : !quantum.reg
    }
```

**Benefits:**
- Decomposition works with flatten params end-to-end

**Possible Drawbacks:**

**Related GitHub Issues:**
[sc-100937]
**Context:**
PRs targeting `v0.13.0-rc` should be tested with the release candidate
branches of PL and Lightning as well.
We manually overwrite the CI to install these directly after `make
frontend` (which gets the version from `.dep_versions`).

Note that when merging rc branch back to main after the release, this PR
must be reverted for `.dep_versions` to take back control of the
versions.
The implementation simply wraps user provided functions in
parameter-less wrappers that propagate user-provided arguments
via closure.

A special case is maintained from a earlier bugfix in
#1232 that allowed
"standard" usage of qml.cond with gate constructor without matching
the return types across branches, by dropping the return value in the
single operator case.

closes #2086
supersedes #1531 and #1232
…2101)

**Context:**
Change check-catalyst (CI on PRs in catalyst repo targeting the rc
branch) to use rc PennyLane from testpypi instead of git.
**Context:**

Fix qml.prod with autograph

```python
@qml.prod
def template(b):
    if b:
        qml.H(0)
        qml.X(0)

@qjit(autograph=True)
@qml.qnode(qml.device("null.qubit", wires=1))
def circuit(b: bool):
    template(b)
    return qml.state()

print(circuit(True))
```

Error message:
```
jax.errors.TracerBoolConversionError: Attempted boolean conversion of traced array with shape bool[].
The error occurred while tracing the function circuit at ... for qjit_capture. This concrete value was not available in Python because it depends on the value of the argument b.
See https://docs.jax.dev/en/latest/errors.html#jax.errors.TracerBoolConversionError
```

**Description of the Change:**

HOTFIX: Handle calls to functions that were decorated with qml.prod,
qml.adjoint, etc. These decorators return wrapper functions that call
the original function without autograph conversion. We detect these
wrappers and unwrap them to convert the original function with
autograph.

TODO: Once PL has has dedicated way to propagate autograph through
decorators, we should remove this way of handling `qml.prod`.

**Benefits:**

**Possible Drawbacks:**

**Related GitHub Issues:**
[sc-95653]
#1911

---------

Co-authored-by: David Ittah <[email protected]>
**Context:**
Kokkos tests in CI need to install base lightning package as well.

---------

Co-authored-by: Joseph Lee <[email protected]>
Co-authored-by: Ali Asadi <[email protected]>
**Context:**
There are some error cases where we should issue a better, more
actionable error message regarding the dynamic allocation feature.

**Description of the Change:**
Add specific error messages for the following error cases:
- Using dynamic allocation without program capture
- Implicit wires in terminal MP
- Dynamically allocated wires in terminal MP
- Use-after-free
- Subroutines

**Benefits:**
Better error message.

**Related GitHub Issues:**
closes #2071

[sc-100475]
Do not fallback to QubitUnitary unless operator provides no
decomposition. Previously, a QubitUnitary would be constructed for any
Controlled operator not natively supported by the device. While this can
have some advantages, e.g. better performance on simulators for gates up
to a certain size (which was never the intention of the fallback), it
can also cause issues like overly large constant matrices embedded into
the IR, and can also be unexpected to users since they might expect a
decomposition to elementary gates.

The proposed update should be more robust in that it still uses the
QubitUnitary fallback for gates that don't implement a decomposition,
but otherwise makes use of the provided one.

closes #2077
**Context:**
We don't run codefactor on rc branch. This causes some codefactor
complaints on the autosync from rc branch to main branch every day (e.g.
the final manual commits I added in
#2108).

Next release I will enable codefactor on rc branch. This time around,
it's already Thursday so I'll just sync it manually.
**Context:**

**Description of the Change:**
Added traversal logic of mcm. For an example, finding the `qreg` and
index of `qb3` in the following case, it needs to traverse the def-use
chain through the ops.

```
qb0 = extract(...)
...
meas_result, qb2 = qml.measure(qb1, 0)
...
qb4 = op(qb3, ...) 
```

**Refactoring**

1. Use `QuantumGate` to traverse the gates instead of `CustomOp`
2. Collect qreg and qubit index with only one walk

**Benefits:**

**Possible Drawbacks:**

**Related GitHub Issues:**
[sc-100311]

---------

Co-authored-by: Paul <[email protected]>
Co-authored-by: Mehrdad Malek <[email protected]>
Co-authored-by: ringo-but-quantum <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeffrey Kam <[email protected]>
Co-authored-by: Isaac De Vlugt <[email protected]>
Co-authored-by: Ali Asadi <[email protected]>
Co-authored-by: paul0403 <[email protected]>
**Description of the Change:**
Updated conditional checks to ensure correct handling of measurement
changes during QNode transformations.

**Related GitHub Issues:**
Closes #2070

---------

Co-authored-by: Hong-Sheng Zheng <[email protected]>
**Context:** When using mid circuit measurements `catalyst.measure()`
and passing its value into a measurement process under the
single-branch-statistics mode, it fails with a low level error
concerning LLVM translation, which is not helpful to the user.

```
import catalyst
import pennylane as qml

@qml.qjit
@qml.set_shots(10)
@qml.qnode(qml.device("lightning.qubit", wires=2))
def c():
    qml.H(0)
    m = catalyst.measure(0)
    return qml.expval(m)

c()
```
Running the above program gives the following error.
```
CompileError: catalyst failed with error code 1: Compilation failed:
c:27:12: error: LLVM Translation failed for operation: builtin.unrealized_conversion_cast
      %9 = "quantum.compbasis"(%8) {operandSegmentSizes = array<i32: 0, 1>} : (!quantum.reg) -> !quantum.obs
           ^
c:27:12: note: see current operation: %20 = "builtin.unrealized_conversion_cast"() : () -> i64
Failed to translate LLVM module
```

**Description of the Change:**
Added some validation checks earlier to capture and raise the
appropriate error during the tracing stage.

**Benefits:** A more informative error message to let users know the
cause of failure.

[[sc-97982]]
WORK IN PROGRESS

**Context:**
Nanobind reports reference leaks in certain CIs. This turns out to be
caused by global caching of qnodes, even when they are no longer needed.

**Description of the Change:**
1. Updates the dynamic one-shot transform to no longer take the entire
qnode as a global variable
2. Removes the cache within the `Function` class which would gradually
accumulate objects since it is no longer needed

**Benefits:**
Prevents Python objects from remaining in memory after they are no
longer needed

**Possible Drawbacks:**

**Related GitHub Issues:**
[sc-99563]

---------

Co-authored-by: Paul <[email protected]>
Co-authored-by: Mehrdad Malek <[email protected]>
Co-authored-by: ringo-but-quantum <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Jeffrey Kam <[email protected]>
Co-authored-by: Isaac De Vlugt <[email protected]>
Co-authored-by: Ali Asadi <[email protected]>
Co-authored-by: paul0403 <[email protected]>
Co-authored-by: Sengthai Heng <[email protected]>
Co-authored-by: David Ittah <[email protected]>
**Context:**
Various small OQC fixes.

**Description of the Change:**
- The error message when account info is missing now includes the env
variables to set
- OQC cpp device itself should not be the one starting the python
interpreter (this will cause python's "cannot start multiple
interpreters" error from user python script). Instead, whatever cpp
tests that need this should start their python interpreter with pybind.
- update to new OQC API 
- Properly link up the backend to use, instead of always falling back to
lucy
- Migrate to new shots scheme (shots goes with qnode, not the device)

**Benefits:**
OQC device works

**Related GitHub Issues:** #2021
[sc-98652]
**Context:**

Insertion of qubits to qreg is missing after the PR:
https://github.com/PennyLaneAI/catalyst/pull/2068/files

**Description of the Change:**

Restore the change

TODO:
- [x] Add integration test

**Benefits:**

**Possible Drawbacks:**

**Related GitHub Issues:**

---------

Co-authored-by: Ali Asadi <[email protected]>
v0.13.0 changelog edits

[sc-98522](https://app.shortcut.com/xanaduai/story/98522/catalyst-changelog)

---------

Co-authored-by: Paul <[email protected]>
Co-authored-by: AntonNI8 <[email protected]>
Co-authored-by: David Ittah <[email protected]>
**Context:**
Pylint released 4.0.0 yesterday and broke CI.
To not interfere with release, we pin Pylint to the previous working
version 3.3.9.
…2118)

**Context:**

Bump the PennyLane and Lightning minimum versions in preparation for the
Catalyst 0.13.0 release. These changes ensure we can build the wheels
after the Lightning release and before the core PennyLane release.
@paul0403 paul0403 closed this Oct 15, 2025
@paul0403 paul0403 deleted the v0.13.0-rc branch October 15, 2025 22:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants