Source code for uniqc.algorithmics.state_preparation.thermal_state

"""Thermal state (Boltzmann distribution) preparation."""

__all__ = ["thermal_state"]

from typing import List, Optional
import numpy as np
from uniqc.circuit_builder import Circuit


[docs] def thermal_state( circuit: Circuit, beta: float, hamiltonian: Optional[np.ndarray] = None, qubits: Optional[List[int]] = None, ) -> None: """Prepare a thermal (Gibbs) state for a given inverse temperature β. For the default Hamiltonian H = Σ Z_i, each qubit independently receives a Ry(θ) gate where θ = 2·arccos(√p₀) with p₀ = e^β/(e^β + e^{-β}). For a custom Hamiltonian, the function diagonalises H, computes Boltzmann weights, and uses :func:`rotation_prepare`. Args: circuit: Quantum circuit (mutated in-place). beta: Inverse temperature. ``0`` → maximally mixed; ``∞`` → ground state. hamiltonian: ``2^n × 2^n`` Hermitian matrix, or ``None`` for H = Σ Z_i. qubits: Qubit indices. ``None`` → all circuit qubits. Raises: ValueError: *beta* is negative or *hamiltonian* shape is wrong. Example: >>> from uniqc.circuit_builder import Circuit >>> from uniqc.algorithmics.state_preparation import thermal_state >>> c = Circuit() >>> thermal_state(c, beta=1.0, qubits=[0]) """ if beta < 0: raise ValueError(f"beta must be non-negative, got {beta}") if hamiltonian is None: # Default: H = Σ Z_i, independent qubits # p(0) = e^β / (e^β + e^{-β}), p(1) = e^{-β} / (e^β + e^{-β}) p0 = np.exp(beta) / (np.exp(beta) + np.exp(-beta)) if beta < 500 else 1.0 theta = 2.0 * np.arccos(np.clip(np.sqrt(p0), 0, 1)) if qubits is None: qubits = list(range(circuit.max_qubit + 1)) for q in qubits: if abs(theta) > 1e-15: circuit.ry(q, theta) else: hamiltonian = np.asarray(hamiltonian, dtype=complex) d = hamiltonian.shape[0] if hamiltonian.shape != (d, d): raise ValueError("hamiltonian must be a square matrix") if qubits is None: n = int(round(np.log2(d))) qubits = list(range(n)) else: n = len(qubits) if d != 2**n: raise ValueError( f"hamiltonian dimension ({d}) must equal 2^n = {2**n}" ) eigenvalues, eigenvectors = np.linalg.eigh(hamiltonian) shifted = -beta * eigenvalues shifted -= np.max(shifted) weights = np.exp(shifted) weights /= np.sum(weights) state_vector = eigenvectors @ np.sqrt(weights) from .rotation_prepare import rotation_prepare rotation_prepare(circuit, state_vector, qubits)