Source code for psipose.ansatze.hardware_efficient

"""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 self.n_params = layers * n_qubits * 3
[docs] def circuit(self, weights, wires): """Build the quantum circuit. Parameters ---------- weights : array-like, shape (layers, n_qubits, 3) Rotation parameters. weights[l, q, 0] gives RX angle, weights[l, q, 1] gives RY angle, weights[l, q, 2] gives 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) if isinstance(weights, np.ndarray) and weights.ndim == 1: weights = weights.reshape(self.layers, n_qubits, 3) for layer in range(self.layers): 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]) if n_qubits > 1: for q in range(n_qubits - 1): qml.CNOT(wires=[wires[q], wires[q + 1]]) 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)