PennyLane
Install
Install
  1. Blog/
  2. Releases/
  3. PennyLane v0.43 and Catalyst v0.13 released

October 16, 2025

PennyLane v0.43 and Catalyst v0.13 released

Isaac De Vlugt

Isaac De Vlugt

Gabriela Sanchez Diaz

Gabriela Sanchez Diaz

Diego Guala

Diego Guala

Anton Naim Ibrahim

Anton Naim Ibrahim

PennyLane v0.43 and Catalyst v0.13 released

PennyLane v0.43 and Catalyst v0.13 are here so that your quantum programming experience feels like fine dining 🎩!

Contents

  • Dynamic wire allocation 🍽️
  • Resource estimation πŸ“–
  • Track your resources when using Catalyst 🧾
  • Quantum optimization with qjit πŸ«–
  • Optimize controlled compute-uncompute patterns 🍴
  • Deprecations and breaking changes πŸ’”
  • Contributors ✍️

Dynamic wire allocation 🍽️

Call ahead to reserve your wires 🀡

Dynamic wire allocation

Wires can now be dynamically allocated and deallocated in quantum functions with qml.allocate and qml.deallocate, unlocking decompositions with better resource usage, complex dynamic subroutines, and more. Works with qjit!

The qml.allocate function can accept three arguments that dictate how dynamically allocated wires are handled:

  • num_wires: the number of wires to dynamically allocate.
  • state = "zero" or "any": the initial state that the dynamically allocated wires are requested to be in. Currently, supported values are "zero" (initialized in the all-zero state) or "any" (any arbitrary state).
  • restored = True or False: a guarantee that the allocated wires will be restored to their original state (True) or not (False) when those wires are deallocated.

The recommended way to safely allocate and deallocate wires is to use qml.allocate as a context manager:

import pennylane as qml

@qml.qnode(qml.device("default.qubit"), mcm_method="tree-traversal")
def circuit():
    qml.H(0)

    for i in range(2):
        with qml.allocate(1, state="zero", restored=True) as new_wire1:
            with qml.allocate(1, state="any", restored=False) as new_wire2:
                m0 = qml.measure(new_wire1[0], reset=True)
                qml.cond(m0 == 1, qml.Z)(new_wire2[0])
                qml.CNOT((0, new_wire2[0]))

    return qml.expval(qml.Z(0))

In the above example, the high-level circuit shows four separate allocations and deallocations (two per loop iteration). However, the circuit that the device receives gets automatically compiled to only use two additional wires (wires labelled 1 and 2 in the diagram below).

>>> print(qml.draw(circuit, level="device")())
0: ──H───────────╭●──────────────╭●──  <Z>
1: ───↗│  β”‚0βŸ©β”€β”€β”€β”€β”‚β”€β”€β”€β”€β†—β”‚  β”‚0βŸ©β”€β”€β”€β”€β”‚β”€β”€β”€
2: ───║────────Z─╰X───║────────Z─╰X──
      β•šβ•β•β•β•β•β•β•β•β•      β•šβ•β•β•β•β•β•β•β•β•

This is due to the fact that new_wire1 and new_wire2 can both be reused after they've been deallocated in the first iteration of the for loop.

Support for using qml.allocate and qml.deallocate with @qml.qjit is also available with some key differences. For more information, check out the Catalyst documentation!

Resource estimation πŸ“–

Quickly check what's in the budget with an all-new resource estimation module πŸ’³

Resource estimation

A new toolkit dedicated to resource estimation is now available in the estimator module! The functionality therein is designed to rapidly and flexibly estimate the quantum resources required to execute programs.

The main entry point to these new features is the estimate function, which allows you to estimate the quantum resources (such as qubits and gate counts) required to execute a circuit on a device with a specific target gate set. The estimate function can be used on circuits written at different levels of detail to get high-level estimates of gate counts and additional wires fast.

For workflows that are already defined in detail, like executable QNodes, the estimate function works as follows:

import pennylane as qml
import pennylane.estimator as qre

dev = qml.device("null.qubit")

@qml.qnode(dev)
def circ():
    for w in range(2):
        qml.Hadamard(wires=w)
    qml.CNOT(wires=[0,1])
    qml.RX(1.23*np.pi, wires=0)
    qml.RY(1.23*np.pi, wires=1)
    qml.QFT(wires=[0, 1, 2])
    return qml.state()
>>> res = qre.estimate(circ)()
>>> print(res)
--- Resources: ---
 Total wires: 3
  algorithmic wires: 3
  allocated wires: 0
    zero state: 0
    any state: 0
 Total gates : 408
  'T': 396,
  'CNOT': 9,
  'Hadamard': 3

If exact argument values or detailed operator descriptions are unknown, unavailable, tedious or even infeasible to compute, quantum programs can be expressed using new lightweight representations of PennyLane operations that require minimal information to obtain high-level resource estimates. As part of this release, the estimator module has added lightweight versions of many PennyLane operations that avoid the need to provide computationally expensive inputs, and potentially expensive pre-processing.

The example below uses lightweight "resource" operators to create a circuit with 50 wires, including a QROMStatePreparation acting on 48 wires. Defining this state preparation for execution would require a prohibitively large state vector, but we are able to estimate the required resources with only metadata, bypassing this computational barrier. Even at this scale, the resource estimate is computed in a fraction of a second!

def my_circuit():
    qre.QROMStatePreparation(num_state_qubits=48)
    for w in range(2):
        qre.Hadamard(wires=w)
    qre.QROM(num_bitstrings=32, size_bitstring=8, restored=False)
    qre.CNOT(wires=[0,1])
    qre.RX(wires=0)
    qre.RY(wires=1)
    qre.QFT(num_wires=30)
    return
>>> res = qre.estimate(my_circuit)()
>>> print(res)
--- Resources: ---
 Total wires: 129
  algorithmic wires: 50
  allocated wires: 79
    zero state: 71
    any state: 8
 Total gates : 2.702E+16
  'Toffoli': 1.126E+15,
  'T': 5.751E+4,
  'CNOT': 2.027E+16,
  'X': 2.252E+15,
  'Z': 32,
  'S': 64,
'Hadamard': 3.378E+15

This just scratches the surfaceβ€”go check out the full release notes for a complete list of resource estimation features added in PennyLane v0.43!

Track your resources when using Catalyst 🧾

Dig into the details to pick your sides and sauces with qjit-compatible circuit resource tracking 🀀

Catalyst compatibility with specs

Use qml.specs to track the resources of programs compiled with qml.qjit! This new feature is currently supported when using level="device", which provides exact gate counts of the quantum-compiled program that the device executes.

import pennylane as qml
from functools import partial

gateset = {qml.H, qml.S, qml.CNOT, qml.T, qml.RX, qml.RY, qml.RZ}

@qml.qjit
@partial(qml.transforms.decompose, gate_set=gateset)
@qml.qnode(qml.device("null.qubit", wires=100))
def circuit():
    qml.QFT(wires=range(100))
    qml.Hadamard(wires=0)
    qml.CNOT(wires=[0, 1])
    qml.OutAdder(
                x_wires=range(10),
                y_wires=range(10,20),
                output_wires=range(20,31)
                )
    return qml.expval(qml.Z(0) @ qml.Z(1))
>>> circ_specs = qml.specs(circuit, level="device")()
>>> print(circ_specs['resources'])
num_wires: 100
num_gates: 138134
depth: 90142
shots: Shots(total=None)
gate_types:
{'CNOT': 55313, 'RZ': 82698, 'Hadamard': 123}
gate_sizes:
{2: 55313, 1: 82821}

Quantum optimization with qjit πŸ«–

Get on the gravy train with quantum optimizers that work with qjit πŸš‚

QJIT-compatible quantum optimizers

Leveraging qjit to optimize hybrid workflows with the momentum quantum natural gradient optimizer is now possible with qml.MomentumQNGOptimizerQJIT, providing better runtime scaling than its non-JIT-compatible counterpart.

The v0.42 release saw the addition of the qml.QNGOptimizerQJIT optimizer, which is a qjit-compatible analogue to qml.QNGOptimizer. Both qml.QNGOptimizerQJIT and qml.MomentumQNGOptimizerQJIT have an Optax-like interface:

import pennylane as qml
import jax.numpy as jnp

dev = qml.device("lightning.qubit", wires=2)

@qml.qnode(dev)
def circuit(params):
    qml.RX(params[0], wires=0)
    qml.RY(params[1], wires=1)
    return qml.expval(qml.Z(0) + qml.X(1))

opt = qml.MomentumQNGOptimizerQJIT(stepsize=0.1, momentum=0.2)

def update_step_qjit(i, args):
    params, state = args
    return opt.step(circuit, params, state)

@qml.qjit
def optimization_qjit(params, iters):
    state = opt.init(params)
    args = (params, state)
    params, state = qml.for_loop(iters)(update_step_qjit)(args)
    return params

Quantum just-in-time compilation works exceptionally well with repeatedly executing the same function in a for loop. As you can see, 10^5 iterations takes seconds:

>>> import time
>>> params = jnp.array([0.1, 0.2])
>>> iters = 100_000
>>> start = time.process_time()
>>> optimization_qjit(params=params, iters=iters)
Array([ 3.14159265, -1.57079633], dtype=float64)
>>> time.process_time() - start
21.319525

Optimize controlled compute-uncompute patterns 🍴

Work some fibre into your quantum regimen with an easy-to-use and versatile change-of-basis operation πŸ«›πŸ₯•

Change basis op

Benefit from an optimization on controlled compute-uncompute patterns with the new qml.change_op_basis function. Operators arranged in a compute-uncompute pattern (U V U^\dagger, which is equivalent to changing the basis in which V is expressed) can be efficiently controlled, as only the central (target) operator V needs to be controlled.

These new features leverage the graph-based decomposition system, enabled with qml.decompostion.enable_graph(). To illustrate their use, consider the following example. The compute-uncompute pattern is composed of a QFT, followed by a PhaseAdder, and finally an inverse QFT.

import pennylane as qml
from functools import partial

qml.decomposition.enable_graph()

dev = qml.device("default.qubit")

@partial(qml.transforms.decompose, max_expansion=1)
@qml.qnode(dev)
def circuit():
    qml.H(0)
    qml.CNOT([1,2])
    qml.ctrl(
        qml.change_op_basis(qml.QFT([1,2]), qml.PhaseAdder(1, x_wires=[1,2])),
        control=0
    )
    return qml.state()

When this circuit is decomposed, the QFT and Adjoint(QFT) are not controlled, resulting in a much more resource-efficient decomposition:

>>> print(qml.draw(circuit)())
0: ──H──────╭●─────────────────  State
1: ─╭●─╭QFTβ”€β”œPhaseAdder─╭QFT†──  State
2: ─╰X─╰QFT─╰PhaseAdder─╰QFT†──  State

Additionally, the decompositions for several templates have been updated to use this optimization, including qml.Adder, qml.Multiplier, qml.OutAdder, qml.OutMultiplier, and qml.PrepSelPrep.

Deprecations and breaking changes πŸ’”

As new things are added, outdated features are removed. To keep track of things in the deprecation pipeline, check out the deprecations page.

Here's a summary of what has changed in this release:

  • The PennyLane ecosystem will no longer support Intel MacOS platforms for v0.44 and newer now that PennyLane-Lightning has dropped support. If needed, MacOS x86 wheels can be built manually. Additionally, MacOS ARM wheels will require a minimum OS version of 14.0 for continued use with v0.44 and newer. This change is needed to account for MacOS officially deprecating support for Intel CPUs (see their blog post for more details).
  • Support for Python 3.10 has been removed and support for Python 3.13 has been added.
  • Setting shots on a device through the shots keyword argument (e.g., qml.device("default.qubit", wires=2, shots=1000)) and in QNode calls (e.g., qml.QNode(circuit, dev)(shots=1000)) has been deprecated. Please use the qml.set_shots transform to set the number of shots instead.
  • Support for using TensorFlow with PennyLane has been deprecated and will be dropped in Pennylane v0.44. Instead, we recommend using the JAX or PyTorch interfaces for machine learning applications to benefit from enhanced support and features. Please consult the following demos for more usage information: Turning quantum nodes into Torch Layers and How to optimize a QML model using JAX and Optax.

These highlights are just scratching the surface β€” check out the full release notes for PennyLane and Catalyst for more details.

Contributors ✍️

As always, this release would not have been possible without the hard work of our development team and contributors:

Guillermo Alonso, Ali Asadi, Utkarsh Azad, Astral Cai, Joey Carter, Pablo Antonio Moreno Casares, Yushao Chen, Amintor Dusko, Isaac De Vlugt, Diksha Dhawan, Gabriela Sanchez Diaz, Marcus Edwards, Tarik El-Khateeb, Ashley Enman, Lillian Frederiksen, Pietropaolo Frisoni, Simone Gasperini, Diego Guala, Sengthai Heng, Austin Huang, David Ittah, Anton Naim Ibrahim, Soran Jahangiri, Jeffrey Kam, Korbinian Kottmann, Elton Law, Christina Lee, Joseph Lee, Mehrdad Malekmohammadi, Luis Alfredo NuΓ±ez Meneses, Lee James O'Riordan, Erick Ochoa, Mudit Pandey, Andrija Paurevic, Justin Pickering, Alex Preciado, Shuli Shu, Jay Soni, Ritu Thombre, Roberto Turrado, Marc Vandelle, Paul Haochen Wang, David Wierichs, Jake Zaia, and Hongsheng Zheng.

About the authors

Isaac De Vlugt
Isaac De Vlugt

Isaac De Vlugt

My job is to help manage the PennyLane and Catalyst feature roadmap... and spam lots of emojis in the chat 🀠

Gabriela Sanchez Diaz
Gabriela Sanchez Diaz

Gabriela Sanchez Diaz

Diego Guala
Diego Guala

Diego Guala

Diego is a quantum scientist at Xanadu. His work is focused on supporting the development of the datasets service and PennyLane features.

Anton Naim Ibrahim
Anton Naim Ibrahim

Anton Naim Ibrahim

Exploring uncharted territory.

Last modified:Β October 16, 2025

Related Blog Posts

PennyLane

PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Built by researchers, for research. Created with ❀️ by Xanadu.

Research

  • Research
  • Performance
  • Hardware & Simulators
  • Demos
  • Quantum Compilation
  • Quantum Datasets

Education

  • Teach
  • Learn
  • Codebook
  • Coding Challenges
  • Videos
  • Glossary

Software

  • Install PennyLane
  • Features
  • Documentation
  • Catalyst Compilation Docs
  • Development Guide
  • API
  • GitHub
Stay updated with our newsletter

Β© Copyright 2025 | Xanadu | All rights reserved

TensorFlow, the TensorFlow logo and any related marks are trademarks of Google Inc.

Privacy Policy|Terms of Service|Cookie Policy|Code of Conduct