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

quantum_circuits.md

135 lines · skills/pennylane/references/quantum_circuits.md at 5f2bc03

Open on GitHub
1# Quantum circuits
2
3Examples target PennyLane 0.45.1. Each Python block is self-contained and tested.
4

Quantum circuits

Examples target PennyLane 0.45.1. Each Python block is self-contained and tested.

Raw lines 1 to 4
5## Gates, controls and wire labels
6
7`RX`, `RY`, `RZ` take angles in radians. `Rot(phi, theta, omega, wires=...)`
8and `U3(theta, phi, delta, wires=...)` have different conventions; inspect their
9matrices before substituting. `CNOT`, `CZ`, `SWAP`, `CRX`, `CRY`, `CRZ`,
10`IsingXX`, `IsingYY`, `IsingZZ`, `Toffoli` and `MultiRZ` use ordered wire lists.
11For a multi-controlled X, `wires` contains all controls followed by the target;
12`control_wires=` is not the current constructor.
14```python
15import pennylane as qml
16import numpy as np
18dev = qml.device("default.qubit", wires=["a", "b", "target"])
19@qml.qnode(dev)
20def controlled():
21 qml.BasisState(np.array([1, 1, 0]), wires=["a", "b", "target"])
22 qml.MultiControlledX(wires=["a", "b", "target"])
23 return qml.probs(wires=["a", "b", "target"])
24assert np.argmax(controlled()) == 7
26def layer(weights, wires):
27 for angle, wire in zip(weights, wires, strict=True):
28 qml.RY(angle, wires=wire)
29 for a, b in zip(wires[:-1], wires[1:]):
30 qml.CNOT(wires=[a, b])
32@qml.qnode(dev)
33def negative_control():
34 qml.ctrl(qml.X, control="a", control_values=[0])(wires="target")
35 return qml.expval(qml.Z("target"))
36assert np.allclose(negative_control(), -1)
37```
39Do not construct neighbors using `wire + 1`: labels need not be consecutive
40integers. Basis encodings require binary entries; amplitude preparation requires
41`2**n` amplitudes with nonzero finite norm. `qml.AmplitudeEmbedding(...,
42normalize=True)` normalizes amplitudes, but does not make arbitrary feature
43preprocessing differentiable.

Gates, controls and wire labels

RX, RY, RZ take angles in radians. Rot(phi, theta, omega, wires=...) and U3(theta, phi, delta, wires=...) have different conventions; inspect their matrices before substituting. CNOT, CZ, SWAP, CRX, CRY, CRZ, IsingXX, IsingYY, IsingZZ, Toffoli and MultiRZ use ordered wire lists. For a multi-controlled X, wires contains all controls followed by the target; control_wires= is not the current constructor.

import pennylane as qml
import numpy as np

dev = qml.device("default.qubit", wires=["a", "b", "target"])
@qml.qnode(dev)
def controlled():
    qml.BasisState(np.array([1, 1, 0]), wires=["a", "b", "target"])
    qml.MultiControlledX(wires=["a", "b", "target"])
    return qml.probs(wires=["a", "b", "target"])
assert np.argmax(controlled()) == 7

def layer(weights, wires):
    for angle, wire in zip(weights, wires, strict=True):
        qml.RY(angle, wires=wire)
    for a, b in zip(wires[:-1], wires[1:]):
        qml.CNOT(wires=[a, b])

@qml.qnode(dev)
def negative_control():
    qml.ctrl(qml.X, control="a", control_values=[0])(wires="target")
    return qml.expval(qml.Z("target"))
assert np.allclose(negative_control(), -1)

Do not construct neighbors using wire + 1: labels need not be consecutive integers. Basis encodings require binary entries; amplitude preparation requires 2**n amplitudes with nonzero finite norm. qml.AmplitudeEmbedding(..., normalize=True) normalizes amplitudes, but does not make arbitrary feature preprocessing differentiable.

Raw lines 5 to 44
45## Measurement feedback
47```python
48import pennylane as qml
49import numpy as np
51dev = qml.device("default.qubit", wires=3) # one auxiliary wire for deferred measurement
52@qml.qnode(dev)
53def adaptive():
54 qml.Hadamard(0)
55 bit = qml.measure(0)
56 qml.cond(bit, qml.X)(wires=1)
57 return qml.expval(qml.Z(0) @ qml.Z(1))
58assert np.allclose(adaptive(), 1.0)
59```
61A measurement value is symbolic during circuit construction. Use `qml.cond`
62(and bitwise `&`, `|`, `~` for combinations), not ordinary `if bit` or `while bit`.
63Native one-shot execution, deferred measurements and tree traversal have different
64resource costs/support. Check device, shot and differentiation compatibility.
65Ordinary Python loops unroll during construction; compiled dynamic loops require
66Catalyst's supported control flow. Reset/postselection changes the state and the
67effective accepted shot count; report the acceptance rate.

Measurement feedback

import pennylane as qml
import numpy as np

dev = qml.device("default.qubit", wires=3)  # one auxiliary wire for deferred measurement
@qml.qnode(dev)
def adaptive():
    qml.Hadamard(0)
    bit = qml.measure(0)
    qml.cond(bit, qml.X)(wires=1)
    return qml.expval(qml.Z(0) @ qml.Z(1))
assert np.allclose(adaptive(), 1.0)

A measurement value is symbolic during circuit construction. Use qml.cond (and bitwise &, |, ~ for combinations), not ordinary if bit or while bit. Native one-shot execution, deferred measurements and tree traversal have different resource costs/support. Check device, shot and differentiation compatibility. Ordinary Python loops unroll during construction; compiled dynamic loops require Catalyst's supported control flow. Reset/postselection changes the state and the effective accepted shot count; report the acceptance rate.

Raw lines 45 to 68
69## QFT and inverse
71Use the library transform rather than a partial hand-written controlled-RZ
72circuit: controlled phase and final wire reversal matter.
74```python
75import pennylane as qml
76import numpy as np
78wires = [0, 1, 2]
79U = qml.matrix(qml.QFT(wires=wires), wire_order=wires)
80indices = np.arange(8)
81expected = np.exp(2j * np.pi * np.outer(indices, indices) / 8) / np.sqrt(8)
82assert np.allclose(U, expected)
83Uinv = qml.matrix(qml.adjoint(qml.QFT)(wires=wires), wire_order=wires)
84assert np.allclose(Uinv @ U, np.eye(8))
85```

QFT and inverse

Use the library transform rather than a partial hand-written controlled-RZ circuit: controlled phase and final wire reversal matter.

import pennylane as qml
import numpy as np

wires = [0, 1, 2]
U = qml.matrix(qml.QFT(wires=wires), wire_order=wires)
indices = np.arange(8)
expected = np.exp(2j * np.pi * np.outer(indices, indices) / 8) / np.sqrt(8)
assert np.allclose(U, expected)
Uinv = qml.matrix(qml.adjoint(qml.QFT)(wires=wires), wire_order=wires)
assert np.allclose(Uinv @ U, np.eye(8))
Raw lines 69 to 86
87## Inspect and transform
89```python
90import pennylane as qml
91import numpy as np
93dev = qml.device("default.qubit", wires=2)
94@qml.qnode(dev)
95def circuit(x):
96 qml.Hadamard(0)
97 qml.Hadamard(0)
98 qml.RY(x, 0)
99 qml.CNOT([0, 1])
100 return qml.expval(qml.Z(1))
102optimized = qml.transforms.cancel_inverses(circuit)
103assert np.allclose(optimized(0.2), circuit(0.2))
104print(qml.draw(optimized)(0.2))
105spec = qml.specs(optimized, level="device")(0.2)
106resources = spec.resources
107assert resources.num_gates == 2
108assert resources.depth == 2
109print(resources.gate_types)
111with qml.tape.QuantumTape() as tape:
112 qml.Rot(0.1, 0.2, 0.3, wires=0)
113 qml.expval(qml.Z(0))
114tapes, postprocess = qml.transforms.decompose(tape, gate_set={qml.RZ, qml.RY})
115results = qml.execute(tapes, dev)
116assert np.allclose(postprocess(results), np.cos(0.2))
117```
119A tape transform returns `(tapes, postprocessing_fn)`, not a single tape.
120`commute_controlled` moves compatible gates through controlled operations; it does
121not move measurements to the end. `merge_rotations` combines compatible rotations.
122Compare outputs before/after transforms, and record the `qml.specs` level. In
1230.45.1 the result is `CircuitSpecs`, with a `SpecsResources` object under
124`.resources`; splitting transforms can make `.resources` a list. Dictionary
125lookups such as `specs['depth']` and legacy `num_trainable_params` do not apply.
126`qml.draw_mpl` additionally needs Matplotlib and returns `(figure, axes)`.

Inspect and transform

import pennylane as qml
import numpy as np

dev = qml.device("default.qubit", wires=2)
@qml.qnode(dev)
def circuit(x):
    qml.Hadamard(0)
    qml.Hadamard(0)
    qml.RY(x, 0)
    qml.CNOT([0, 1])
    return qml.expval(qml.Z(1))

optimized = qml.transforms.cancel_inverses(circuit)
assert np.allclose(optimized(0.2), circuit(0.2))
print(qml.draw(optimized)(0.2))
spec = qml.specs(optimized, level="device")(0.2)
resources = spec.resources
assert resources.num_gates == 2
assert resources.depth == 2
print(resources.gate_types)

with qml.tape.QuantumTape() as tape:
    qml.Rot(0.1, 0.2, 0.3, wires=0)
    qml.expval(qml.Z(0))
tapes, postprocess = qml.transforms.decompose(tape, gate_set={qml.RZ, qml.RY})
results = qml.execute(tapes, dev)
assert np.allclose(postprocess(results), np.cos(0.2))

A tape transform returns (tapes, postprocessing_fn), not a single tape. commute_controlled moves compatible gates through controlled operations; it does not move measurements to the end. merge_rotations combines compatible rotations. Compare outputs before/after transforms, and record the qml.specs level. In 0.45.1 the result is CircuitSpecs, with a SpecsResources object under .resources; splitting transforms can make .resources a list. Dictionary lookups such as specs['depth'] and legacy num_trainable_params do not apply. qml.draw_mpl additionally needs Matplotlib and returns (figure, axes).

Raw lines 87 to 127
128## Sources
130- [Operations](https://docs.pennylane.ai/en/stable/code/qml.html)
131- [MultiControlledX](https://docs.pennylane.ai/en/stable/code/api/pennylane.MultiControlledX.html)
132- [Dynamic circuits](https://docs.pennylane.ai/en/stable/introduction/dynamic_quantum_circuits.html)
133- [QFT](https://docs.pennylane.ai/en/stable/code/api/pennylane.QFT.html)
134- [Transforms](https://docs.pennylane.ai/en/stable/code/qml_transforms.html)
135- [specs](https://docs.pennylane.ai/en/stable/code/api/pennylane.specs.html)