Source code for qtest.fixtures.common_gates

"""Pytest fixtures (and plain factory functions) for common gate-sequence circuits.

Provides:

* ``hadamard_circuit(n)`` — n-qubit Hadamard layer (one ``H`` per qubit).
* ``random_clifford_circuit(n, depth, seed)`` — depth-controlled random
  Clifford built from the gate set ``{H, S, S†, X, Y, Z}`` plus occasional
  ``CX`` for ``n >= 2``.

Examples
--------
>>> # In a pytest test:
>>> def test_uniform(hadamard_circuit):  # doctest: +SKIP
...     assert_distribution_close(
...         hadamard_circuit(3), expected={"000": 1/8, ..., "111": 1/8}
...     )

>>> # As plain functions:
>>> from qtest.fixtures.common_gates import random_clifford_circuit
>>> qc = random_clifford_circuit(n=2, depth=20, seed=42)  # doctest: +SKIP
"""

from __future__ import annotations

from typing import Any, Callable

import numpy as np
import pytest

_CLIFFORD_SINGLES: tuple[str, ...] = ("h", "s", "sdg", "x", "y", "z")


# --------------------------------------------------------------------------- #
# Plain factory functions                                                     #
# --------------------------------------------------------------------------- #


[docs] def hadamards(n: int = 1) -> Any: """Return an n-qubit circuit that applies :math:`H` to every qubit. The resulting state from :math:`|0\\rangle^{\\otimes n}` is the uniform superposition :math:`H^{\\otimes n}|0\\rangle^{\\otimes n}`. """ from qiskit import QuantumCircuit if not isinstance(n, int) or isinstance(n, bool) or n < 1: raise ValueError(f"n must be a positive integer, got {n!r}") qc = QuantumCircuit(n, name=f"hadamards_{n}") for q in range(n): qc.h(q) return qc
[docs] def random_clifford_circuit( n: int = 1, depth: int = 10, seed: int | None = None, ) -> Any: """Return a random Clifford circuit on *n* qubits with *depth* gates. Each "layer" is a single gate: with probability ~0.3 (when ``n >= 2``) a random ``CX``, otherwise a random gate drawn from ``{H, S, S†, X, Y, Z}`` on a random qubit. Reproducible when ``seed`` is set. Parameters ---------- n Qubit count (``n >= 1``). depth Number of gates (``depth >= 0``). ``depth == 0`` returns the identity circuit. seed Seed for the RNG; ``None`` means non-deterministic. """ from qiskit import QuantumCircuit if not isinstance(n, int) or isinstance(n, bool) or n < 1: raise ValueError(f"n must be a positive integer, got {n!r}") if not isinstance(depth, int) or isinstance(depth, bool) or depth < 0: raise ValueError(f"depth must be a non-negative integer, got {depth!r}") rng = np.random.default_rng(seed) qc = QuantumCircuit(n, name=f"random_clifford_d{depth}_n{n}") for _ in range(depth): if n > 1 and rng.random() < 0.3: ctrl, tgt = rng.choice(n, size=2, replace=False).tolist() qc.cx(int(ctrl), int(tgt)) else: gate_name = _CLIFFORD_SINGLES[int(rng.integers(0, len(_CLIFFORD_SINGLES)))] qubit = int(rng.integers(0, n)) getattr(qc, gate_name)(qubit) return qc
# --------------------------------------------------------------------------- # # Pytest fixtures # # --------------------------------------------------------------------------- #
[docs] @pytest.fixture def hadamard_circuit() -> Callable[..., Any]: """Factory fixture for n-qubit Hadamard-layer circuits. Example:: def test_uniform(hadamard_circuit): qc = hadamard_circuit(3) # 3 qubits, all in |+> qc.measure_all() assert_distribution_close( qc, expected={f"{i:03b}": 1/8 for i in range(8)} ) """ return hadamards
[docs] @pytest.fixture def random_clifford() -> Callable[..., Any]: """Factory fixture for random Clifford circuits. Example:: def test_clifford_unitarity(random_clifford): for seed in range(5): assert_unitary(random_clifford(n=3, depth=20, seed=seed)) """ return random_clifford_circuit