Skill Tracer
Skills › Scientific Agent Skills › pennylane › quantum_ml.md

quantum_ml.md

147 lines · skills/pennylane/references/quantum_ml.md at 5f2bc03

Open on GitHub
1# Quantum machine learning
2
3PennyLane 0.45.1 integrates with Autograd, PyTorch and JAX. TensorFlow maintenance
4ended in 0.44 and KerasLayer was removed. The examples below exercise small local
5models, not a claim of generalization or advantage over classical methods.
6

Quantum machine learning

PennyLane 0.45.1 integrates with Autograd, PyTorch and JAX. TensorFlow maintenance ended in 0.44 and KerasLayer was removed. The examples below exercise small local models, not a claim of generalization or advantage over classical methods.

Raw lines 1 to 6
7## TorchLayer: correct batch shapes and end-to-end gradients
8
9Tested with PyTorch 2.14.1. The QNode has an `inputs` argument; all other arguments
10are trainable weights with entries in `weight_shapes`. Avoid unrestricted `*args`
11or `**kwargs`. `AngleEmbedding` handles the leading batch dimension.
13```python
14import pennylane as qml
15import torch
17torch.manual_seed(7)
18dev = qml.device("default.qubit", wires=2)
19@qml.qnode(dev, interface="torch", diff_method="backprop")
20def qnode(inputs, weights):
21 qml.AngleEmbedding(inputs, wires=[0, 1], rotation="Y")
22 qml.StronglyEntanglingLayers(weights, wires=[0, 1])
23 return [qml.expval(qml.Z(i)) for i in range(2)]
25layer = qml.qnn.TorchLayer(qnode, {"weights": (2, 2, 3)})
26model = torch.nn.Sequential(torch.nn.Linear(4, 2), layer, torch.nn.Linear(2, 2)).double()
27X = torch.tensor([[0.2, 0.1, 0.4, -0.2], [-0.3, 0.2, -0.1, 0.4],
28 [0.5, 0.3, 0.0, -0.2], [-0.2, -0.3, 0.3, 0.5]], dtype=torch.float64)
29y = torch.tensor([0, 1, 0, 1], dtype=torch.long)
30optimizer = torch.optim.Adam(model.parameters(), lr=0.02)
31optimizer.zero_grad()
32logits = model(X)
33assert logits.shape == (4, 2)
34loss = torch.nn.functional.cross_entropy(logits, y)
35loss.backward()
36assert all(p.grad is not None and torch.isfinite(p.grad).all() for p in model.parameters())
37optimizer.step()
38```
40A manual QNode can promote input float32 to float64; align a custom surrounding
41network's dtype. Do not convert trainable outputs through ordinary NumPy or
42`torch.tensor(existing_tensor)`, which detaches gradients. Use `torch.stack` when
43combining tensor results. Save model state plus circuit/weight-shape configuration.

TorchLayer: correct batch shapes and end-to-end gradients

Tested with PyTorch 2.14.1. The QNode has an inputs argument; all other arguments are trainable weights with entries in weight_shapes. Avoid unrestricted *args or **kwargs. AngleEmbedding handles the leading batch dimension.

import pennylane as qml
import torch

torch.manual_seed(7)
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev, interface="torch", diff_method="backprop")
def qnode(inputs, weights):
    qml.AngleEmbedding(inputs, wires=[0, 1], rotation="Y")
    qml.StronglyEntanglingLayers(weights, wires=[0, 1])
    return [qml.expval(qml.Z(i)) for i in range(2)]

layer = qml.qnn.TorchLayer(qnode, {"weights": (2, 2, 3)})
model = torch.nn.Sequential(torch.nn.Linear(4, 2), layer, torch.nn.Linear(2, 2)).double()
X = torch.tensor([[0.2, 0.1, 0.4, -0.2], [-0.3, 0.2, -0.1, 0.4],
                  [0.5, 0.3, 0.0, -0.2], [-0.2, -0.3, 0.3, 0.5]], dtype=torch.float64)
y = torch.tensor([0, 1, 0, 1], dtype=torch.long)
optimizer = torch.optim.Adam(model.parameters(), lr=0.02)
optimizer.zero_grad()
logits = model(X)
assert logits.shape == (4, 2)
loss = torch.nn.functional.cross_entropy(logits, y)
loss.backward()
assert all(p.grad is not None and torch.isfinite(p.grad).all() for p in model.parameters())
optimizer.step()

A manual QNode can promote input float32 to float64; align a custom surrounding network's dtype. Do not convert trainable outputs through ordinary NumPy or torch.tensor(existing_tensor), which detaches gradients. Use torch.stack when combining tensor results. Save model state plus circuit/weight-shape configuration.

Raw lines 7 to 44
45## JAX: explicit dataset vectorization
47Use the PennyLane-tested JAX/JAXlib 0.7.1 pair in a separate environment. JAX's latest
48release is not a compatibility claim for PennyLane. Use explicit precision and
49batch only the sample axis:
51```python
52import pennylane as qml
53import jax
54import jax.numpy as jnp
56jax.config.update("jax_enable_x64", True)
57dev = qml.device("default.qubit", wires=2)
58@qml.qnode(dev, interface="jax", diff_method="backprop")
59def prediction(x, weights):
60 qml.AngleEmbedding(x, wires=[0, 1], rotation="Y")
61 qml.RY(weights[0], 0)
62 qml.RY(weights[1], 1)
63 qml.CNOT([0, 1])
64 return qml.expval(qml.Z(1))
66predict_batch = jax.vmap(prediction, in_axes=(0, None))
67@jax.jit
68def loss_fn(weights, X, y):
69 return jnp.mean((predict_batch(X, weights) - y)**2)
71X = jnp.array([[0.1, 0.3], [0.2, -0.4], [-0.3, 0.5]])
72y = jnp.array([1.0, -1.0, 1.0])
73weights = jnp.array([0.1, 0.2])
74value, grad = jax.value_and_grad(loss_fn)(weights, X, y)
75assert jnp.isfinite(value) and jnp.isfinite(grad).all()
76assert predict_batch(X, weights).shape == (3,)
77weights = weights - 0.05 * grad
78assert loss_fn(weights, X, y) < value
79```
81Finite-shot JAX workflows additionally need explicit PRNG keys, split for new
82random draws. Validate the specific JIT, differentiation, measurement and device
83combination; not all combinations support vector-valued Jacobians. Catalyst's
84compiled quantum workflow is distinct from wrapping a simulator loss in `jax.jit`.

JAX: explicit dataset vectorization

Use the PennyLane-tested JAX/JAXlib 0.7.1 pair in a separate environment. JAX's latest release is not a compatibility claim for PennyLane. Use explicit precision and batch only the sample axis:

import pennylane as qml
import jax
import jax.numpy as jnp

jax.config.update("jax_enable_x64", True)
dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev, interface="jax", diff_method="backprop")
def prediction(x, weights):
    qml.AngleEmbedding(x, wires=[0, 1], rotation="Y")
    qml.RY(weights[0], 0)
    qml.RY(weights[1], 1)
    qml.CNOT([0, 1])
    return qml.expval(qml.Z(1))

predict_batch = jax.vmap(prediction, in_axes=(0, None))
@jax.jit
def loss_fn(weights, X, y):
    return jnp.mean((predict_batch(X, weights) - y)**2)

X = jnp.array([[0.1, 0.3], [0.2, -0.4], [-0.3, 0.5]])
y = jnp.array([1.0, -1.0, 1.0])
weights = jnp.array([0.1, 0.2])
value, grad = jax.value_and_grad(loss_fn)(weights, X, y)
assert jnp.isfinite(value) and jnp.isfinite(grad).all()
assert predict_batch(X, weights).shape == (3,)
weights = weights - 0.05 * grad
assert loss_fn(weights, X, y) < value

Finite-shot JAX workflows additionally need explicit PRNG keys, split for new random draws. Validate the specific JIT, differentiation, measurement and device combination; not all combinations support vector-valued Jacobians. Catalyst's compiled quantum workflow is distinct from wrapping a simulator loss in jax.jit.

Raw lines 45 to 85
86## Autograd classifier and stable loss
88```python
89import pennylane as qml
90from pennylane import numpy as np
92dev = qml.device("default.qubit", wires=2)
93@qml.qnode(dev)
94def classifier(x, weights):
95 qml.AngleEmbedding(x, wires=[0, 1], rotation="Y")
96 qml.StronglyEntanglingLayers(weights, wires=[0, 1])
97 return qml.expval(qml.Z(0))
99def binary_loss(weights, X, y):
100 # Here P(y=1) = (1 + <Z>)/2 is an explicit label convention.
101 z = np.stack([classifier(x, weights) for x in X])
102 probability = np.clip((1 + z) / 2, 1e-8, 1 - 1e-8)
103 return -np.mean(y * np.log(probability) + (1 - y) * np.log1p(-probability))
105X = np.array([[0.1, 0.2], [0.9, -0.8]], requires_grad=False)
106y = np.array([1.0, 0.0], requires_grad=False)
107weights = np.array([[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]], requires_grad=True)
108grad = qml.grad(binary_loss)(weights, X, y)
109assert grad.shape == weights.shape and np.isfinite(grad).all()
110```
112For labels defined as the probability of the measured `|1>` outcome, instead use
113`(1 - <Z>)/2`. Keep the convention consistent in training and scoring. Clipping
114avoids logarithms of zero but saturates gradients at the boundary. A multiclass
115model should return the promised number of logits/probabilities on allocated wires;
116three observables cannot use an only-two-wire device.

Autograd classifier and stable loss

import pennylane as qml
from pennylane import numpy as np

dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def classifier(x, weights):
    qml.AngleEmbedding(x, wires=[0, 1], rotation="Y")
    qml.StronglyEntanglingLayers(weights, wires=[0, 1])
    return qml.expval(qml.Z(0))

def binary_loss(weights, X, y):
    # Here P(y=1) = (1 + <Z>)/2 is an explicit label convention.
    z = np.stack([classifier(x, weights) for x in X])
    probability = np.clip((1 + z) / 2, 1e-8, 1 - 1e-8)
    return -np.mean(y * np.log(probability) + (1 - y) * np.log1p(-probability))

X = np.array([[0.1, 0.2], [0.9, -0.8]], requires_grad=False)
y = np.array([1.0, 0.0], requires_grad=False)
weights = np.array([[[0.1, 0.2, 0.3], [0.4, 0.5, 0.6]]], requires_grad=True)
grad = qml.grad(binary_loss)(weights, X, y)
assert grad.shape == weights.shape and np.isfinite(grad).all()

For labels defined as the probability of the measured |1> outcome, instead use (1 - <Z>)/2. Keep the convention consistent in training and scoring. Clipping avoids logarithms of zero but saturates gradients at the boundary. A multiclass model should return the promised number of logits/probabilities on allocated wires; three observables cannot use an only-two-wire device.

Raw lines 86 to 117
118## Encodings and model evaluation
120- Angle embedding needs at most one feature per allocated wire; scale using training
121 data only. Angular periodicity can alias features.
122- Amplitude embedding needs a nonzero finite vector of size at most `2**n` with
123 declared padding/normalization. State preparation can be expensive and feature
124 differentiation is generally limited by its classical preprocessing.
125- Basis embedding accepts binary vectors. IQP embedding includes feature-product
126 phases; use the documented template when claiming that encoding.
127- `ApproxTimeEvolution(H, time, n)` is a Trotter approximation; noncommuting terms
128 need convergence checks. Specify the Hamiltonian and approximation order/steps.
129- QCNN pooling must actually remove measured wires from later active-wire sets;
130 allocating eight wires and measuring/discarding different indices is a different
131 architecture. Recurrent models may return classical expectations as hidden state;
132 distinguish that from persistent quantum memory.
133- For transfer learning, encode the input before frozen layers, freeze the intended
134 parameters in the native framework, and match layer shapes. Fit preprocessing on
135 training data and compare with the same classical feature extractor and head.
137Record split/group leakage controls, seed repetitions, shots, hyperparameter budget,
138classical baselines and confidence intervals. A synthetic training loss decrease
139is a gradient smoke test, not predictive validation or quantum advantage.

Encodings and model evaluation

  • Angle embedding needs at most one feature per allocated wire; scale using training data only. Angular periodicity can alias features.
  • Amplitude embedding needs a nonzero finite vector of size at most 2**n with declared padding/normalization. State preparation can be expensive and feature differentiation is generally limited by its classical preprocessing.
  • Basis embedding accepts binary vectors. IQP embedding includes feature-product phases; use the documented template when claiming that encoding.
  • ApproxTimeEvolution(H, time, n) is a Trotter approximation; noncommuting terms need convergence checks. Specify the Hamiltonian and approximation order/steps.
  • QCNN pooling must actually remove measured wires from later active-wire sets; allocating eight wires and measuring/discarding different indices is a different architecture. Recurrent models may return classical expectations as hidden state; distinguish that from persistent quantum memory.
  • For transfer learning, encode the input before frozen layers, freeze the intended parameters in the native framework, and match layer shapes. Fit preprocessing on training data and compare with the same classical feature extractor and head.

Record split/group leakage controls, seed repetitions, shots, hyperparameter budget, classical baselines and confidence intervals. A synthetic training loss decrease is a gradient smoke test, not predictive validation or quantum advantage.

Raw lines 118 to 140
141## Sources
143- [TorchLayer](https://docs.pennylane.ai/en/stable/code/api/pennylane.qnn.TorchLayer.html)
144- [Torch interface](https://docs.pennylane.ai/en/stable/introduction/interfaces/torch.html)
145- [JAX interface](https://docs.pennylane.ai/en/stable/introduction/interfaces/jax.html)
146- [Tested upstream dependencies](https://github.com/PennyLaneAI/pennylane/blob/v0.45.1/pyproject.toml)
147- [Templates](https://docs.pennylane.ai/en/stable/introduction/templates.html)