Source code for sim.core.operators

# ==============================================================================
#  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.
# ==============================================================================
"""
Spin-1 operators for NV center simulation.

This module provides the spin-1 matrices required for describing
the electron spin (S=1) and N14 nuclear spin (I=1) in the NV center.

Physical Background
-------------------
The NV center ground state is a spin triplet (S=1) with states
|ms=+1>, |ms=0>, |ms=-1>. The spin operators satisfy the standard
commutation relations:

    [Sx, Sy] = i*Sz  (and cyclic permutations)
    S² = S(S+1) = 2  for S=1

Basis Convention
----------------
All matrices are represented in the {|+1>, |0>, |-1>} basis, where
Sz is diagonal with eigenvalues +1, 0, -1 on the diagonal.

18-Dimensional Hilbert Space
----------------------------
The full NV center Hilbert space is the tensor product:

    |ψ> = |g/e> ⊗ |ms> ⊗ |mI>

    - |g/e>: Ground/Excited state (2 dim)
    - |ms>:  Electron spin ms ∈ {+1, 0, -1} (3 dim)
    - |mI>:  N14 nuclear spin mI ∈ {+1, 0, -1} (3 dim)

    Total: 2 × 3 × 3 = 18 dimensions

The basis states are ordered as:
    |0>  = |g, +1, +1>
    |1>  = |g, +1,  0>
    |2>  = |g, +1, -1>
    |3>  = |g,  0, +1>
    ...
    |17> = |e, -1, -1>
"""

import numpy as np
from dataclasses import dataclass


[docs] @dataclass(frozen=True) class Spin1Ops: """ Container for spin-1 operators. Attributes ---------- Sx : np.ndarray Spin-x operator (3×3 complex) Sy : np.ndarray Spin-y operator (3×3 complex) Sz : np.ndarray Spin-z operator (3×3 complex, diagonal) Sp : np.ndarray Raising operator S+ = Sx + i*Sy Sm : np.ndarray Lowering operator S- = Sx - i*Sy I : np.ndarray Identity matrix (3×3) """ Sx: np.ndarray Sy: np.ndarray Sz: np.ndarray Sp: np.ndarray Sm: np.ndarray I: np.ndarray
[docs] def spin1_operators() -> Spin1Ops: """ Create spin-1 operators in the {|+1>, |0>, |-1>} basis. The matrices satisfy: - [Sx, Sy] = i*Sz (and cyclic permutations) - S² = Sx² + Sy² + Sz² = 2*I (for S=1) - Sz|m> = m|m> for m ∈ {+1, 0, -1} Returns ------- Spin1Ops Dataclass containing Sx, Sy, Sz, S+, S-, and identity matrix Examples -------- >>> ops = spin1_operators() >>> np.allclose(ops.Sx @ ops.Sy - ops.Sy @ ops.Sx, 1j * ops.Sz) True >>> np.linalg.eigvalsh(ops.Sz) array([-1., 0., 1.]) """ # Sx in the {|+1>, |0>, |-1>} basis # <m'|Sx|m> = (1/2) * sqrt(S(S+1) - m*m') * (δ_{m',m+1} + δ_{m',m-1}) # For S=1 this gives the factor 1/√2 Sx = (1.0 / np.sqrt(2.0)) * np.array([ [0, 1, 0], [1, 0, 1], [0, 1, 0] ], dtype=np.complex128) # Sy = -i * [raising - lowering] / 2 Sy = (1.0 / np.sqrt(2.0)) * np.array([ [0, -1j, 0], [1j, 0, -1j], [0, 1j, 0] ], dtype=np.complex128) # Sz is diagonal with eigenvalues +1, 0, -1 Sz = np.array([ [1, 0, 0], [0, 0, 0], [0, 0, -1] ], dtype=np.complex128) # Raising and lowering operators # S+|m> = sqrt(S(S+1) - m(m+1)) |m+1> # S-|m> = sqrt(S(S+1) - m(m-1)) |m-1> Sp = Sx + 1j * Sy Sm = Sx - 1j * Sy # Identity matrix I = np.eye(3, dtype=np.complex128) return Spin1Ops(Sx=Sx, Sy=Sy, Sz=Sz, Sp=Sp, Sm=Sm, I=I)
[docs] def extend_to_18x18(H_3x3: np.ndarray) -> np.ndarray: """ Extend a 3×3 electron spin Hamiltonian to the full 18×18 space. The operator acts on the electron spin in BOTH manifolds (ground and excited) and leaves the nuclear spin unchanged. Mathematically: H_18 = I_2 ⊗ H_3 ⊗ I_3 where: - I_2: Identity on |g/e> (2×2) - H_3: Electron spin operator (3×3) - I_3: Identity on |mI> (3×3) Parameters ---------- H_3x3 : np.ndarray 3×3 Hamiltonian in the electron spin subspace Returns ------- np.ndarray 18×18 Hamiltonian in the full Hilbert space Notes ----- This extension is correct for terms like ZFS and Zeeman that act only on the electron spin and are identical in both electronic states (g and e). Examples -------- >>> from sim.hamiltonian.terms import ZFS >>> zfs = ZFS(D=2.87) >>> H_3 = zfs._build_3x3() >>> H_18 = extend_to_18x18(H_3) >>> H_18.shape (18, 18) """ I3 = np.eye(3, dtype=np.complex128) # Nuclear spin identity I2 = np.eye(2, dtype=np.complex128) # g/e identity # Tensor product: I_2 ⊗ H_3 ⊗ I_3 return np.kron(np.kron(I2, H_3x3), I3)
[docs] def extend_electron_only(H_3x3: np.ndarray, manifold: str = "ground") -> np.ndarray: """ Extend 3×3 to 18×18, acting only on ONE electronic manifold. Unlike extend_to_18x18(), this operator acts ONLY on the ground state (|g>) OR the excited state (|e>), not both. This is needed for terms that differ between g and e, e.g., different ZFS parameters in the excited state. Parameters ---------- H_3x3 : np.ndarray 3×3 electron spin Hamiltonian manifold : str "ground" for |g> manifold (indices 0-8) "excited" for |e> manifold (indices 9-17) Returns ------- np.ndarray 18×18 Hamiltonian acting only on the chosen manifold Examples -------- >>> # ZFS only in ground state >>> H_gs = extend_electron_only(H_zfs_3x3, manifold="ground") >>> # Different ZFS in excited state >>> H_es = extend_electron_only(H_zfs_excited_3x3, manifold="excited") >>> H_total = H_gs + H_es """ I3 = np.eye(3, dtype=np.complex128) # First extend to 9×9 (electron spin ⊗ nuclear spin) H_9x9 = np.kron(H_3x3, I3) # Embed in 18×18 H_18x18 = np.zeros((18, 18), dtype=np.complex128) if manifold == "ground": # Ground state: first 9 states (index 0-8) H_18x18[:9, :9] = H_9x9 elif manifold == "excited": # Excited state: last 9 states (index 9-17) H_18x18[9:, 9:] = H_9x9 else: raise ValueError(f"manifold must be 'ground' or 'excited', not '{manifold}'") return H_18x18
# Module-level operator instances for convenience # Usage: from sim.core.operators import Sx, Sy, Sz _ops = spin1_operators() Sx = _ops.Sx Sy = _ops.Sy Sz = _ops.Sz Sp = _ops.Sp Sm = _ops.Sm I3 = _ops.I # Nuclear spin operators (also S=1) _nuc = spin1_operators() Ix = _nuc.Sx Iy = _nuc.Sy Iz = _nuc.Sz Ip = _nuc.Sp Im = _nuc.Sm