Skip to content

Releases: pasqal-io/qadence

v1.2.1

12 Dec 13:45
92b7cb9
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v1.2.0...v1.2.1

---------- IMPORTANT--------------

This release adds a DIfferentiableBackend in JAX (https://jax.readthedocs.io/en/latest/) along with the 'horqrux' backend (https://github.com/pasqal-io/horqrux), a differentiable state vector simulator in JAX. It supports the differentiation modes AD and GPSR. The horqrux backend can be used via the low-level API only at the moment, which means there is no support for QuantumModels and ml_tools yet.

You can however easily train qadence QuantumCircuits using horqrux/JAX.

See an example how to, below:

(taken from https://github.com/pasqal-io/qadence/blob/main/examples/backends/low_level/horqrux_backend.py.)

from __future__ import annotations

from typing import Callable

import jax.numpy as jnp
import optax
from jax import Array, jit, value_and_grad
from numpy.typing import ArrayLike

from qadence.backends import backend_factory
from qadence.blocks.utils import chain
from qadence.circuit import QuantumCircuit
from qadence.constructors import feature_map, hea, total_magnetization
from qadence.types import BackendName, DiffMode

backend = BackendName.HORQRUX

num_epochs = 10
n_qubits = 4
depth = 1

fm = feature_map(n_qubits)
circ = QuantumCircuit(n_qubits, chain(fm, hea(n_qubits, depth=depth)))
obs = total_magnetization(n_qubits)

for diff_mode in [DiffMode.AD, DiffMode.GPSR]:
    bknd = backend_factory(backend, diff_mode)
    conv_circ, conv_obs, embedding_fn, vparams = bknd.convert(circ, obs)
    init_params = vparams.copy()
    optimizer = optax.adam(learning_rate=0.001)
    opt_state = optimizer.init(vparams)

    loss: Array
    grads: dict[str, Array]  # 'grads' is the same datatype as 'params'
    inputs: dict[str, Array] = {"phi": jnp.array(1.0)}

    def optimize_step(params: dict[str, Array], opt_state: Array, grads: dict[str, Array]) -> tuple:
        updates, opt_state = optimizer.update(grads, opt_state, params)
        params = optax.apply_updates(params, updates)
        return params, opt_state

    def exp_fn(params: dict[str, Array], inputs: dict[str, Array] = inputs) -> ArrayLike:
        return bknd.expectation(conv_circ, conv_obs, embedding_fn(params, inputs))

    init_pred = exp_fn(vparams)

    def mse_loss(params: dict[str, Array], y_true: Array) -> Array:
        expval = exp_fn(params)
        return (expval - y_true) ** 2

    @jit
    def train_step(
        params: dict,
        opt_state: Array,
        y_true: Array = jnp.array(1.0, dtype=jnp.float64),
        loss_fn: Callable = mse_loss,
    ) -> tuple:
        loss, grads = value_and_grad(loss_fn)(params, y_true)
        params, opt_state = optimize_step(params, opt_state, grads)
        return loss, params, opt_state

    for epoch in range(num_epochs):
        loss, vparams, opt_state = train_step(vparams, opt_state)
        print(f"epoch {epoch} loss:{loss}")

    final_pred = exp_fn(vparams)

    print(
        f"diff_mode '{diff_mode}: Initial prediction: {init_pred}, initial vparams: {init_params}"
    )
    print(f"Final prediction: {final_pred}, final vparams: {vparams}")
    print("----------")

v1.2.0

11 Dec 13:57
4ba3a9c
Compare
Choose a tag to compare

What's Changed

Breaking

  • [Bugfix, Feature] Consistent variable ordering in QNNs & finitediff function by @nmheim in #206

The QNN constructor now requires an inputs argument if the number of feature parameters in the given circuit is >1:

    fm = qd.kron(
        qd.feature_map(2, support=(0,1), param="x"),
        qd.feature_map(2, support=(2,3), param="y")
    )

    ufa = QNN(
        qd.QuantumCircuit(4, fm, qd.hea(4,2)),
        observable=qd.total_magnetization(4),
        inputs = ["x", "y"], # if this is not provided, the constructor will error
    )

    xs = torch.rand(5,2)
    ufa(xs)

If the QNN has <=1 parameter, things work as before.
The inputs argument is necessary to guarantee the order of variables of the tensors that are passed to the model. Given input tensors xs = torch.rand(batch_size, input_size:=2) a QNN with inputs=("t", "x") will assign t, x = xs[:,0], xs[:,1].

Features

Analog feature maps

Constructors for creating feature maps with analog blocks or semi-local addressing patterns.

import qadence as qd

# analog feature map with RX rotation and Chebyshev basis
# number of qubits is not specified since the qubit support is global
fm_analog = qd.analog_feature_map(fm_type=qd.BasisSet.CHEBYSHEV, op=qd.AnalogRX)

# feature map with semi-local addressing and custom weights on each qubit
n_qubits = 4
fm_semilocal = qd.rydberg_feature_map(n_qubits, weights=[0.25, 0.25, 0.5, 0.0])

Semi-local addressing patterns

Semi-local addressing patterns can be created by either specifying fixed values for the weights of the qubits being addressed or defining them as trainable parameters that can be optimized later in some training loop.

import torch
from qadence.analog import AddressingPattern

n_qubits = 3

# constant weights
w_det = {0: 0.9, 1: 0.5, 2: 1.0}
w_amp = {0: 0.1, 1: 0.4, 2: 0.8}
det = 9.0
amp = 6.5

# trainable weights
w_amp_tr = {i: f"w_amp{i}" for i in range(n_qubits)}
w_det_tr = {i: f"w_det{i}" for i in range(n_qubits)}
amp_tr = "max_amp"
det_tr = "max_det"

# creating pattern
pattern = AddressingPattern(
    n_qubits=n_qubits,
    det=det,
    amp=amp,
    weights_det=w_det,
    weights_amp=w_amp,
)

# pattern specified when creating device instance
device_specs = IdealDevice(pattern=pattern)

reg = Register.line(
    n_qubits,
    spacing=8.0,
    device_specs=device_specs,
)

New Contributors

Full Changelog: v1.1.1...v1.2.0

v1.1.1

27 Nov 11:41
f7b167a
Compare
Choose a tag to compare

What's Changed

  • [Fix] fill_identities which caused drawing issues by @nmheim in #210

Full Changelog: v1.1.0...v1.1.1

v1.1.0

21 Nov 16:08
49b804a
Compare
Choose a tag to compare

What's Changed

  • [Feature] Error mitigation structure + ZNE for Pulser backend by @Roland-djee in #105
  • [Feature] Add coords scaling and distance calculation directly to Register by @jpmoutinho in #186
  • [Fix] Control breaking changes in pulser backend spacing config by @jpmoutinho in #191
  • [Feature] Identity-initialized QNN by @n-toscano in #157
  • [Feature] Pyqtorch - First Order Adjoint Differentiation by @dominikandreasseitz in #155
  • [Fix] Export Toffoli gate in operations.py by @madagra in #195
  • [Refactoring] Improve mitigation protocol by @Roland-djee in #189

New Contributors

Full Changelog: v1.0.6...v1.0.7

v1.0.6

13 Nov 14:57
7cba53d
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v1.0.5...v1.0.6

v1.0.5

06 Nov 13:19
30eeee9
Compare
Choose a tag to compare

What's Changed

Full Changelog: v1.0.4...v1.0.5

v.1.0.4

05 Nov 22:18
57dd370
Compare
Choose a tag to compare

What's Changed

Full Changelog: v1.0.3...v1.0.4

v1.0.3

25 Oct 16:14
91f3699
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v1.0.2...v1.0.3

v1.0.2

17 Oct 16:20
145fd73
Compare
Choose a tag to compare

What's Changed

New Contributors

Full Changelog: v1.0.1...v1.0.2

v1.0.1

11 Oct 14:10
df89a5e
Compare
Choose a tag to compare

What's Changed

Full Changelog: v1.0.0...v1.0.1