PennyLane
Install
Install
  1. Blog/
  2. Plugins/
  3. Introducing hybridlane: Mixed-variable quantum programming in PennyLane

October 05, 2026

Introducing hybridlane: Mixed-variable quantum programming in PennyLane

Jim Furches

Jim Furches

Carlos Ortiz Marrero

Carlos Ortiz Marrero

Tim Stavenger

Tim Stavenger

The following is a guest post by Jim Furches, Carlos Ortiz Marrero, and Tim Stavenger, from the Pacific Northwest National Laboratory, showcasing hybridlane, a library for expressing, simulating, and executing heterogeneous quantum programs within the PennyLane ecosystem.

Almost all quantum software restricts circuits to be composed homogeneously of qubits or qumodes, while quantum hardware has no such requirement. What would full-stack mixed-variable quantum software look like?

Today we're announcing hybridlane, a library for full-stack programming of heterogeneous quantum circuits.

Inspired by Bosonic Qiskit, hybridlane is next-generation software that pushes beyond simulation to let you experiment with simulating, compiling, and, in principle, running mixed-variable quantum programs on hardware (sold separately), all in the comfort of your favorite differentiable quantum framework, PennyLane.

hybridlane is in early preview, and we invite you to get started experimenting with it. We'd love to hear what you think and how you use it in order to refine its development, and we'd like to collaborate on additional backends.

If this sounds interesting (and we hope it does), read on to discover how it works and what it can do!

Contents

  • Qubits and qudits and qumodes, oh my!
  • hybridlane: PennyLane + quantum type interference
  • Advancing mixed-variable simulation
  • Bridging simulators and hardware
  • What's included in the box
  • Where we're heading

Qubits and qudits and qumodes, oh my!

Quantum computing research for qubit-based systems has been supercharged by the software ecosystem available, from reusable algorithms and simulation to compilation routines and resource estimation. Much of this progress has been possible because very different physical systems can be represented through the same qubit abstraction. But that abstraction is not always the most natural or useful description of a quantum system.

Quantum hardware comes in many varieties. To build a qubit-based quantum computer, you need to find a controllable two-level physical subspace. You could pick two internal energy levels of an ion or neutral atom, or the two lowest energy levels of a transmon, or the path a photon takes in some waveguides, or... there's a lot of options 😵‍💫. Each modality is like an instrument: in principle they can all play the same melody, although each brings its own timbre and constraints.

Nature has other, more exotic options to consider beyond qubits. One could encode information in qudits, the d-dimensional analogue of the qubit. Or push it to the limits with qumodes, realizing continuous-variable quantum computing with theoretically infinite dimensions (though, in practice, limited by the amount of energy put in), giving you the flexibility to layer in glissandos and vibrato.

On the software side, mature qubit-based frameworks and languages can orchestrate everything from high-level algorithms and quantum error correction down to the MIDI-like pulse schedules required to make each qubit resonate. If you're composing for qudits and qumodes, the ecosystem is less developed. OpenQudit works with qudit-based circuits, and PennyLane has utilities for CV quantum computing (cannibalized from Strawberry Fields ... @Xanadu we see you 👀).

But, the best compositions don't necessarily stick to a single type of instrument, and quantum computers don't have to either. Recent research points to the potential advantages to mixing qubits and qumodes. The motional qumodes in an ion trap can mediate the two-qubit entangling gate, qumodes can natively simulate bosonic systems, and quantum error correction schemes like the Gottesman-Kitaev-Preskill (GKP) code and cat qubits can protect logical qubits by encoding them into qumodes.

Here's the catch: the vast majority of quantum software constrains you to homogeneous quantum circuits. Even PennyLane, which has qubits, qutrits, and qumodes, forces you to pick one type and stick with it for the duration of your circuit. Heterogeneous quantum systems can be simulated at the physics level with QuTiP and its protégé, Dynamiqs, and at the level of quantum instructions with Bosonic Qiskit. But these tools are limited to simulation — what about all the other layers of the software stack we've come to know and love?

hybridlane: PennyLane + quantum type interference

Working towards the goal of heterogeneous quantum computing software , we've been building hybridlane (yes, lowercase!), a programming framework for hybrid quantum circuits made of both discrete variables (DV) and continuous variables (CV) — aka qubits and qumodes. hybridlane extends PennyLane to handle CV-DV quantum computing through a composable wire type inference procedure. Here, you define a quantum circuit in familiar PennyLane fashion, and, when necessary, hybridlane's type checker determines which wires are qubits and which are qumodes by inspecting the operations you've applied.

Let's see it in action with a circuit that repeatedly shuffles quanta from a qubit to a qumode using the Jaynes-Cummings interaction:

import numpy as np
import pennylane as qp
import hybridlane as hl

dev = qp.device("default.hybrid", fock_level=32)

@qp.qnode(dev)
def jc_circuit(n):
    for j in range(n):
        qp.X(0)
        hl.JC(np.pi / (2 * np.sqrt(j + 1)), np.pi / 2, wires=[0, "m"])

    return hl.expval(hl.X("m")) # Position measurement <x̂>
>>> hl.type_check(jc_circuit)(5)
TypeCheckResult(wire_types=OrderedDict({0: Qubit(), 'm': Qumode()}), basis_maps=[BasisMap({'m': ComputationalBasis.Position})])

Drawing the circuit will also show you what the type checker has determined (and look, we have pretty icons 🤩!)

import matplotlib.pyplot as plt

jc_fig = plt.figure()
hl.draw_mpl(jc_circuit, style="sketch", fig=jc_fig)(5)
jc_fig
Circuit output for qubit-qumode converter

As the type checker is included with every hybridlane device, you practically never need to invoke it yourself in your workflows. If you make a mistake, it'll let you know.

Currently, hybridlane imposes two related restrictions on type inference. First, each wire must have a single valid type; if two gates introduce conflicting assignments, the type checker throws an error. In this example, the X gate constrains wire 0 to be a qubit, and then the BS gate fails because its first wire argument is constrained to be a qumode.


@qp.qnode(dev)
def invalid_circuit():
    qp.X(0)
    hl.BS(0.5, 0.123, wires=(0, 1)) # ❌ error: wire 0 is a qubit, but beamsplitter takes 2 qumodes

    return hl.expval(hl.N(1))
>>> invalid_circuit()
TypeCheckError
Operation Beamsplitter(0.5, 0.123, wires=[0, 1]) is incompatible with previous circuit operations.
 - Wire 0 was previously inferred as Qubit(), but is now inferred as Qumode()

The second requirement is that all operations are monomorphic, meaning each operation has one fixed type signature that does not depend on context. For example, the above BS gate has type signature (qumode, qumode). hybridlane does not allow an operation whose signature contains an unknown or polymorphic type that could later resolve to either a qubit or a qumode. This idea is well-suited to programming NISQ CV-DV devices, where physical gates generally correspond to concrete interactions between specific kinds of quantum systems.

These restrictions primarily allow hybridlane to leverage PennyLane's graph decomposition system. Otherwise, calling qp.decompose could inadvertently transform a qubit to a qumode or introduce incompatible gates. They also make type inference straightforward; because each operation defines concrete constraints and the system never needs to explore alternative assignments, the procedure runs in linear time with respect to the number of operations.

Type inference is the core idea behind hybridlane. Users can write circuits in familiar PennyLane style without explicitly annotating their wires, while hybridlane derives their types from the operations applied to them and checks that the circuit is well-typed. The rest of this framework explores how far we can push this idea within PennyLane.

Advancing mixed-variable simulation

For much of hybridlane's history, it translated programs to Bosonic Qiskit for simulation. This was effective for prototyping hybridlane by letting us focus on designing the user experience. But, it came with some drawbacks like a restricted gate set and performance overhead from the translation itself.

With our latest release, we're shipping hybridlane with a new simulator, default.hybrid. Like default.qubit, it runs on PennyLane's qp.math library, and it brings many improvements like support for more gates and all the benefits of JAX: GPU support, automatic differentiation, and just-in-time (JIT) compilation 🚀.

To demonstrate an optimization workload incorporating those features, here's an example numerically synthesizing the binomial code state \ket{0_L} = \frac{1}{\sqrt{2}}(\ket{0} + \ket{4}) on a single qumode using the universal bosonic SNAP + D gate set.

import jax
import optax
from jax import numpy as jnp

# Set the truncation for all qumodes in the circuit. This can also be set per-qumode
# for finer control
fock_level = 20
jax_dev = qp.device("default.hybrid", fock_level=fock_level)

# Initialize default.hybrid with autodiff support and define our circuit
@qp.qnode(jax_dev, interface="jax", diff_method="backprop")
def state_prep_circuit(params):
    def loop_func(i):
        hl.D(params[i, 0], params[i, 1], wires=0)

        for j in range(8):
            hl.SNAP(params[i, 2 + j], j, wires=0)

    qp.for_loop(0, params.shape[0])(loop_func)()
    return hl.state()

@jax.jit
def loss(params, codeword):
    # Compute the infidelity between the state we prepared and the target state
    state = state_prep_circuit(params)
    return 1 - hl.math.real(hl.math.fidelity_statevector(state, codeword))

@jax.jit
def optimize():
    # Target state is the binomial codeword |0L>
    codeword = hl.math.concatenate(
        [
            hl.math.array([1, 0, 0, 0, 1], like="jax") / jnp.sqrt(2),
            hl.math.zeros(fock_level - 5, like="jax"),
        ]
    )

    optimizer = optax.adam(learning_rate=0.01)

    def update(i, x):
        params, opt_state = x
        _, grads = jax.value_and_grad(loss)(params, codeword)
        updates, opt_state = optimizer.update(grads, opt_state)
        params = optax.apply_updates(params, updates)
        return params, opt_state

    # Initialize parameters randomly with shape (n_layers, 10)
    key = jax.random.key(0)
    params = jax.random.normal(key, (5, 10))

    # Perform optimization for 100 steps
    opt_state = optimizer.init(params)
    params, _ = jax.lax.fori_loop(0, 100, update, (params, opt_state))
    return params
>>> optimize()
Array([[ 1.982689  ,  2.1412716 , -1.4421436 , -0.21197473, -0.28419435,
        -0.04030197, -0.5250142 , -0.0858266 ,  0.36952868, -1.296063  ],
       [ 2.2193365 , -2.0285115 , -0.3477476 , -0.6349916 ,  1.8068101 ,
         1.9111933 ,  0.30991226,  1.5190036 , -0.21940559, -2.7732008 ],
       [-1.6394372 ,  1.7357317 , -0.22353517,  1.6091074 , -0.53233147,
         0.8039989 ,  0.3550365 ,  1.4796588 , -1.5016267 , -1.6207513 ],
       [-1.6311504 ,  0.38452587, -0.49183983,  0.6512487 ,  0.03348165,
         0.36249456,  1.1192211 , -1.4368721 , -0.69012165, -0.7565678 ],
       [-0.65728146, -1.0290021 ,  0.72562   ,  0.80924904,  0.04269255,
        -0.57767123, -0.8069234 , -1.9412533 ,  1.3161184 ,  0.7542728 ]],      dtype=float32)

Upon invoking optimize(), JAX traces the entire computation — including the circuit structure, the loss function, and the optimizer loop — and lowers it to optimized native code, dramatically accelerating the computation. With the older Bosonic Qiskit device in a similar optimization loop, hybridlane would have to recompute the circuit structure every iteration, but here JAX's JIT compiler reuses the structure and only updates the parameters.

This JIT compilation capability makes default.hybrid particularly suited to parameter sweeps, numerical unitary synthesis, variational quantum algorithms, and any workflow that repeatedly evaluates different parameters while preserving the circuit structure.

Bridging simulators and hardware

As mentioned earlier, one of hybridlane's major design goals is cross-platform mixed-variable quantum programming: write your circuit once, verify it in simulation, then compile it to run on real hardware. We approach this by leveraging PennyLane's device abstractions. New backends are just PennyLane devices, compilation passes are familiar transforms, and you can reuse your favorite algorithm templates.

We can demonstrate a cross-platform workflow with this displacement gate calibration example. First, we define a circuit that draws a loop in the qumode's phase space with area \beta^2 and whose direction is conditioned on the state of the qubit. Leveraging the phase kickback effect, this realizes a R_X(-4\beta^2) gate on the qubit. Importantly, since the rotation angle of the qubit depends on the area of the loop, if the hardware displacement amounts differ from \beta, that will be reflected in the qubit's state.

def cd_circuit(beta):
    qp.H('q')
    hl.CD(beta, 0, wires=['q', 'm'])
    hl.D(beta, np.pi/2, wires='m')
    hl.CD(-beta, 0, wires=['q', 'm'])
    hl.D(-beta, np.pi/2, wires='m')
    qp.H('q')

    return hl.expval(qp.Z('q'))

cd_fig = plt.figure()
hl.draw_mpl(cd_circuit, style="sketch", fig=cd_fig)(0.5)
cd_fig
Circuit output for displacement gate calibration example

To verify it works, we simulate it using default.hybrid — again with JIT compilation since it's a parameter sweep over \beta — and compare it to the ground truth.

# Prepare simulator version of the circuit
sim_qnode = qp.QNode(cd_circuit, dev)

# Evaluate the circuit at discrete points
beta_sample = jnp.linspace(0, 2, 40)
expval = [jax.jit(sim_qnode)(beta) for beta in beta_sample]

# Compare against the analytically expected value
beta_exact = jnp.linspace(0, 2, 250)
expval_exact = jnp.cos(4 * beta_exact**2)

plot_fig = plt.figure()
plt.plot(beta_exact, expval_exact, label=r'$\cos(4\beta^2)$')
plt.scatter(beta_sample, expval, label="Samples")
plt.xlabel(r"$\beta$")
plt.ylabel(r"$\text{Tr}[\rho_q Z]$")
plt.legend()
plot_fig
Hardware displacement plot

Now we can switch devices and reuse the exact same circuit definition. For this example, we’ll compile the circuit for an ion trap, whose qumodes are the ions’ motional modes. We developed the sandia.qscout device below in collaboration with Sandia National Laboratories (SNL). It serves as a compilation target consisting of preprocessing transforms; it does not provide cloud access to execute circuits on SNL’s hardware.

# Some fancy drawing options tailored to the ion trap
from hybridlane.devices.sandia_qscout import get_default_style
draw_options = get_default_style()

# hybridlane uses the graph decomposition system extensively
qp.decomposition.enable_graph()

# Instantiate the ion trap with its maximum number of ions and
# bind our circuit to it.
ion_trap = qp.device('sandiaqscout.hybrid', n_qubits=6)
hw_qnode = qp.set_shots(qp.QNode(cd_circuit, ion_trap), 1024)

# Draw the circuit, automatically invoking the compilation
# since qp.decompose is part of the device preprocessing transforms
ion_trap_fig = plt.figure()
hl.draw_mpl(hw_qnode, style="sketch", level="device", fig=ion_trap_fig, **draw_options)(2.0)
ion_trap_fig
Hardware displacement plot

As you can see, we ended up with a much different circuit in the native gate set of the ion trap. The unsupported displacement gates (hl.D) were synthesized using dynamic qubit allocation on a clean qubit with conditional displacements (hl.CD). Then, the conditional displacements were decomposed into the native conditional displacement gates along the qubit's X basis (hl.XCD) and single-qubit rotations. Finally, the circuit's algorithmic wires were mapped to hardware wires with a custom virtual wire layout transform.

Behind the scenes, PennyLane is really doing the heavy lifting here. hybridlane's type checker inspected the circuit to determine the number of free qubits available for dynamic qubit allocation, then called qp.decompose with this information and used its custom decomposition identities to perform much of the transformation.

This cross-platform capability has proved very useful to our research group. By implementing devices for new, experimental simulators, we've been able to perform head-to-head performance benchmarking reusing the same circuits. Also, hybridlane has become instrumental for programming SNL's ion trap thanks to its ability to lower programs to Jaqal, their native quantum language, facilitating high-level programming of the trap.

What's included in the box

In addition to cat (states), hybridlane has several components to help get your research off the ground:

  • Gates and decompositions: We have a library of bosonic and hybrid CV-DV quantum gates defined symbolically, along with many decompositions for converting between them, and some templates like preparing a GKP state with non-Abelian quantum signal processing.
  • Simulation: You can leverage default.hybrid with NumPy to quickly verify small circuits, or switch to JAX when you need more performance on advanced workflows like optimization. We overloaded parts of PennyLane's intuitive qp.math library to work with heterogeneous Hilbert space dimensions.
  • PennyLane compatibility: Where possible, hybridlane remains compatible with PennyLane, so you can reuse your favorite qubit gates, algorithm templates, and transforms. It's really hard to overstate the utility of this. Throw in a PennyLane QuantumPhaseEstimation with a CV-DV system, or calculate resource requirements with qp.specs.
  • Intermediate representation: To simplify adding new devices (particularly hardware), we implemented our own variation of OpenQASM for CV-DV circuits. By providing an intermediate representation, you can focus on getting your device functional while hybridlane handles the higher levels of the software stack.

Where we're heading

In the core library, we have lots of room to expand our features to match PennyLane. For example, we aim to implement dynamic circuits and extend Catalyst/QIR to support qumodes. We would also like to add more general gradient transforms, improve resource estimation tools, or expand our algorithm template library ... there's lots of work to do.

We're actively working to support more simulators and quantum devices (can you ever have enough?), particularly exploring different modalities. This will require incorporating more compilation routines to fulfill our cross-platform ambitions.

Finally, exploring further the ideas behind hybridlane itself could be interesting, like allowing it to use polymorphic gates. This could consist of letting it choose to use a qubit or qumode implementation of the quantum Fourier transform, depending on which is more efficient. This would require substantial revisions to hybridlane and PennyLane's decomposition system, but it would unlock much more advanced resource estimation and optimization.

If any of this sounds interesting, please get in touch! Consider trying out hybridlane if it looks like it would accelerate your research and let us know what works (and what doesn't). We're happy to collaborate to support new quantum processors, and if you implement a cool new algorithm or transform as part of your research, we welcome a pull request 🩵!

About the authors

Jim Furches
Jim Furches

Jim Furches

Hi, I'm Jim I'm a post-masters research associate at PNNL working on quantum algorithms, benchmarking, and programming. When I'm not programming, I enjoy surfing 🏄‍♂️ and learning violin 🎻

Carlos Ortiz Marrero
Carlos Ortiz Marrero

Carlos Ortiz Marrero

I am an Assistant Professor in the Department of Computer Science at Colorado State University, a joint appointee at Pacific Northwest National Laboratory, and PI at the Co-design Center for Quantum Advantage (C2QA). I am currently working on problem...

Tim Stavenger
Tim Stavenger

Tim Stavenger

Software engineer leading teams of scientists and engineers at Pacific Northwest National Laboratory in researching quantum and AI safety & security topics with impact to national mission.

Last modified: October 05, 2026

Related Blog Posts

Never miss a milestone

Get the latest quantum updates delivered to your inbox.

Join the list
PennyLane

PennyLane is an open-source quantum software platform for quantum computing, quantum machine learning, and quantum chemistry. Create meaningful quantum algorithms, from inspiration to implementation.

Created with ❤️ by Xanadu.

Research

  • Research

  • Performance

  • Hardware and simulators

  • Demos library

  • Compilation hub

  • Quantum datasets

Education

  • Teach

  • Learn

  • Codebook

  • Coding challenges

  • Videos

  • Glossary

Software

  • Install

  • Features

  • PennyLane documentation

  • Catalyst documentation

  • Development guide

  • How-to guides

  • API

  • GitHub


Xanadu

© Copyright 2026 | Xanadu | All rights reserved

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

Privacy policyTerms of serviceCookies policyCode of conduct