Source code for uniqc.algorithmics.ansatz.hea
"""Hardware-Efficient Ansatz (HEA).
Generates a parameterised circuit with alternating layers of single-qubit
rotations and entangling CNOT gates, suitable for NISQ devices.
"""
__all__ = ["hea"]
from typing import List, Optional
import numpy as np
from uniqc.circuit_builder import Circuit
[docs]
def hea(
n_qubits: int,
depth: int = 1,
qubits: Optional[List[int]] = None,
params: Optional[np.ndarray] = None,
) -> Circuit:
"""Build a Hardware-Efficient Ansatz (HEA) circuit.
The ansatz consists of *depth* repeated layers. Each layer applies:
1. ``Rz(q, θ) → Ry(q, θ)`` on every qubit (parameterised).
2. A ring of CNOT gates: ``CNOT(i, (i+1) % n)`` for i = 0..n-1.
The total number of parameters is ``2 * n_qubits * depth``.
Args:
n_qubits: Number of qubits.
depth: Number of repeated layers (default 1).
qubits: Qubit indices. ``None`` → ``list(range(n_qubits))``.
params: 1-D array of rotation angles. ``None`` → random initialisation.
Returns:
A :class:`Circuit` object containing the ansatz gates.
Raises:
ValueError: *params* length does not match ``2 * n_qubits * depth``.
Example:
>>> from uniqc.algorithmics.ansatz import hea
>>> c = hea(n_qubits=4, depth=2)
>>> c.max_qubit + 1
4
"""
if qubits is None:
qubits = list(range(n_qubits))
else:
qubits = list(qubits)
n_params = 2 * n_qubits * depth
if params is None:
params = np.random.uniform(0, 2 * np.pi, size=n_params)
else:
params = np.asarray(params)
if len(params) != n_params:
raise ValueError(
f"Expected {n_params} parameters, got {len(params)}"
)
circuit = Circuit()
idx = 0
for _ in range(depth):
# Single-qubit rotations
for q in qubits:
if abs(params[idx]) > 1e-15:
circuit.rz(q, float(params[idx]))
idx += 1
if abs(params[idx]) > 1e-15:
circuit.ry(q, float(params[idx]))
idx += 1
# Entangling layer (ring topology)
for i in range(n_qubits):
circuit.cx(qubits[i], qubits[(i + 1) % n_qubits])
return circuit