Source code for sim.hamiltonian.builder

# ==============================================================================
#  QUSIM - Quantum Simulator for NV Centers
#  Leon Kaiser, MSQC Goethe University, Frankfurt, Germany
#  https://msqc.cgi-host6.rz.uni-frankfurt.de
#  I.kaiser[at]em.uni-frankfurt.de
#
#  This software is provided for scientific and educational purposes.
#  Free to use, modify, and distribute with attribution.
# ==============================================================================
"""
Hamiltonian builder for NV center simulation.

The builder combines multiple Hamiltonian terms into a
total Hamiltonian and provides analysis methods.

Architecture
------------
The builder follows the Composite pattern:

    HamiltonianBuilder
    ├── ZFS(D=2.87)
    ├── Zeeman(B=10)
    ├── Hyperfine(...)
    └── ...

    H_total(t) = Σ term.build(t)

Units
-----
- Input: Practical units (GHz, mT, ...)
- Output: The matrix H is in rad/s (energy with ℏ=1)
- Eigenvalues can be retrieved in GHz via eigenvalues_ghz()

Example
-------
>>> from sim import HamiltonianBuilder
>>> from sim.hamiltonian.terms import ZFS, Zeeman
>>>
>>> H = HamiltonianBuilder()
>>> H.add(ZFS(D=2.87))        # 2.87 GHz ZFS
>>> H.add(Zeeman(B=10))       # 10 mT magnetic field
>>>
>>> # Matrix at t=0
>>> H_matrix = H.build(t=0.0)
>>> print(H_matrix.shape)
(18, 18)
>>>
>>> # Eigenvalues in GHz
>>> eigvals = H.eigenvalues_ghz()
>>> print(f"Levels: {np.unique(np.round(eigvals, 2))}")
"""

import numpy as np
from typing import List, Optional

from .terms.base import HamiltonianTerm


[docs] class HamiltonianBuilder: """ Builder for constructing the NV center Hamiltonian. Collects individual terms (ZFS, Zeeman, Hyperfine, ...) and combines them into the total matrix. Attributes ---------- terms : list List of added HamiltonianTerm objects Methods ------- add(term) Add a term build(t=0.0) Build the 18×18 matrix at time t eigenvalues(t=0.0) Compute eigenvalues in rad/s eigenvalues_ghz(t=0.0) Compute eigenvalues in GHz Examples -------- Simple NV center with ZFS and magnetic field: >>> H = HamiltonianBuilder() >>> H.add(ZFS(D=2.87)) >>> H.add(Zeeman(B=10)) >>> print(len(H)) 2 >>> print(H) HamiltonianBuilder([ZFS, Zeeman]) Analyze eigenvalues: >>> eigvals = H.eigenvalues_ghz() >>> print(f"Ground state: {min(eigvals):.3f} GHz") >>> print(f"Highest state: {max(eigvals):.3f} GHz") Method chaining: >>> H = HamiltonianBuilder().add(ZFS()).add(Zeeman(B=5)) """ # Hilbert space dimension (fixed for NV center) DIM = 18
[docs] def __init__(self): """Initialize an empty builder.""" self._terms: List[HamiltonianTerm] = []
[docs] def add(self, term: HamiltonianTerm) -> "HamiltonianBuilder": """ Add a Hamiltonian term. Parameters ---------- term : HamiltonianTerm A term object (e.g., ZFS, Zeeman, Hyperfine) Returns ------- HamiltonianBuilder self, for method chaining Raises ------ TypeError If term is not a HamiltonianTerm Examples -------- >>> H = HamiltonianBuilder() >>> H.add(ZFS(D=2.87)).add(Zeeman(B=10)) HamiltonianBuilder([ZFS, Zeeman]) """ if not isinstance(term, HamiltonianTerm): raise TypeError( f"Expected HamiltonianTerm, got {type(term).__name__}. " f"Use terms like ZFS(...), Zeeman(...), etc." ) self._terms.append(term) return self
[docs] def clear(self) -> "HamiltonianBuilder": """ Remove all terms. Returns ------- HamiltonianBuilder self, for method chaining """ self._terms.clear() return self
@property def terms(self) -> List[HamiltonianTerm]: """ List of added terms (copy). Returns ------- list Copy of the term list """ return self._terms.copy() @property def is_time_dependent(self) -> bool: """ Whether any term is time-dependent. Returns ------- bool True if at least one term is time-dependent """ return any(term.is_time_dependent for term in self._terms)
[docs] def build(self, t: float = 0.0) -> np.ndarray: """ Build the total Hamiltonian matrix at time t. H_total(t) = Σ_i term_i.build(t) Parameters ---------- t : float Time in seconds Returns ------- np.ndarray 18×18 complex Hermitian matrix in rad/s Notes ----- The unit rad/s corresponds to energy with ℏ=1. To convert to Hz: f = ω / (2π) To GHz: f_GHz = ω / (2π × 10⁹) """ H_total = np.zeros((self.DIM, self.DIM), dtype=np.complex128) for term in self._terms: H_total += term.build(t) return H_total
[docs] def eigenvalues(self, t: float = 0.0) -> np.ndarray: """ Compute eigenvalues of the Hamiltonian. Parameters ---------- t : float Time in seconds Returns ------- np.ndarray 18 eigenvalues in rad/s, sorted ascending Notes ----- Uses np.linalg.eigvalsh (for Hermitian matrices). The eigenvalues are real. """ H = self.build(t) return np.linalg.eigvalsh(H)
[docs] def eigenvalues_ghz(self, t: float = 0.0) -> np.ndarray: """ Compute eigenvalues in GHz. Parameters ---------- t : float Time in seconds Returns ------- np.ndarray 18 eigenvalues in GHz, sorted ascending Examples -------- >>> H = HamiltonianBuilder().add(ZFS(D=2.87)) >>> eigvals = H.eigenvalues_ghz() >>> np.unique(np.round(eigvals, 2)) array([0. , 2.87]) """ return self.eigenvalues(t) / (2 * np.pi * 1e9)
[docs] def eigenvalues_mhz(self, t: float = 0.0) -> np.ndarray: """ Compute eigenvalues in MHz. Parameters ---------- t : float Time in seconds Returns ------- np.ndarray 18 eigenvalues in MHz, sorted ascending """ return self.eigenvalues(t) / (2 * np.pi * 1e6)
[docs] def eigenstates(self, t: float = 0.0) -> tuple: """ Compute eigenvalues and eigenvectors. Parameters ---------- t : float Time in seconds Returns ------- eigenvalues : np.ndarray 18 eigenvalues in rad/s eigenvectors : np.ndarray 18×18 matrix, columns are eigenvectors """ H = self.build(t) return np.linalg.eigh(H)
def __repr__(self) -> str: if not self._terms: return "HamiltonianBuilder([])" term_names = ", ".join(term.name for term in self._terms) return f"HamiltonianBuilder([{term_names}])" def __len__(self) -> int: """Number of terms.""" return len(self._terms) def __bool__(self) -> bool: """True if at least one term is present.""" return len(self._terms) > 0