"""Hardware-efficient ansatz with single-qubit rotations and entangling gates."""
import numpy as np
import pennylane as qml
from ._base import BaseAnsatz
[docs]
class HardwareEfficientAnsatz(BaseAnsatz):
"""Hardware-efficient ansatz with alternating rotations and entangling layers.
Implements the hardware-efficient ansatz from Kandala et al.,
"Hardware-efficient variational quantum eigensolver for small molecules
and quantum magnets" (Nature 549, 242-246, 2017).
Each layer consists of:
1. Single-qubit rotations (RX, RY, RZ) on each qubit
2. Entangling gates (CNOTs) in a nearest-neighbor chain
Parameters
----------
n_qubits : int
Number of qubits.
layers : int, default=2
Number of repeating layers.
Examples
--------
>>> ansatz = HardwareEfficientAnsatz(n_qubits=4, layers=3)
>>> weight_shape = ansatz.weight_shape(4) # (layers, n_qubits, 3)
"""
def __init__(self, n_qubits, layers=2):
super().__init__(n_qubits)
self.layers = layers
# weight shape: (layers, n_qubits, 3) for RX, RY, RZ parameters
self.n_params = layers * n_qubits * 3
[docs]
def circuit(self, weights, wires):
"""Build the quantum circuit.
Implements the hardware-efficient ansatz from Kandala et al. (Nature 2017),
where each layer applies RX, RY, RZ rotations on every qubit followed
by a chain of CNOT entangling gates.
Parameters
----------
weights : array-like, shape (layers, n_qubits, 3)
Rotation parameters. weights[l, q, 0] is RX angle,
weights[l, q, 1] is RY angle, weights[l, q, 2] is RZ angle
for qubit q in layer l.
wires : Iterable[int]
Qubit wires.
Notes
-----
Weights can also be a 1D array of length layers * n_qubits * 3,
which will be reshaped automatically.
"""
wires = list(wires)
n_qubits = len(wires)
# Reshape weights if needed
if isinstance(weights, np.ndarray) and weights.ndim == 1:
weights = weights.reshape(self.layers, n_qubits, 3)
for layer in range(self.layers):
# Single-qubit rotations: RX, RY, RZ on each qubit
for q in range(n_qubits):
qml.RX(weights[layer, q, 0], wires=wires[q])
qml.RY(weights[layer, q, 1], wires=wires[q])
qml.RZ(weights[layer, q, 2], wires=wires[q])
# Entangling layer (CNOT chain)
if n_qubits > 1:
for q in range(n_qubits - 1):
qml.CNOT(wires=[wires[q], wires[q + 1]])
# Connect last qubit back to first for full connectivity
if n_qubits > 2:
qml.CNOT(wires=[wires[-1], wires[0]])
[docs]
def weight_shape(self, n_qubits):
"""Return the shape of the weight array: (layers, n_qubits, 3).
The 3 corresponds to RX, RY, RZ rotation angles per qubit
per layer, following Kandala et al. (Nature 2017).
"""
return (self.layers, n_qubits, 3)