Source code for sim.hamiltonian.terms.mw_drive

# ==============================================================================
#  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.
# ==============================================================================
"""
Microwave drive for NV center spin control.

Microwave fields at ~2.87 GHz drive transitions between the ms=0
and ms=±1 spin states. This is the primary control mechanism for
NV spin manipulation.

Physics
-------
In the rotating frame (rotating wave approximation), the MW drive
Hamiltonian is:

    H_MW = (Ω/2) · [S₊ e^(-iφ) + S₋ e^(+iφ)] + Δ · Sz

where:
- Ω(t) is the Rabi frequency (proportional to MW amplitude)
- φ(t) is the phase of the MW field
- Δ is the detuning from resonance
- S± = Sx ± i·Sy are the raising/lowering operators

The Rabi frequency is related to the MW magnetic field by:

    Ω = γ_e · B_MW

where γ_e ≈ 28 GHz/T is the electron gyromagnetic ratio.

Typical Values
--------------
- Rabi frequency: 1-50 MHz (depends on MW power)
- π-pulse duration: 10-500 ns
- Detuning: 0 for resonant driving

References
----------
[1] Childress & Hanson, MRS Bulletin 38, 134 (2013)
[2] Dréau et al., Phys. Rev. B 84, 195204 (2011)
"""

import numpy as np
from typing import Optional, Callable, Union
from numpy.typing import ArrayLike

from .base import HamiltonianTerm
from ...core.operators import spin1_operators, extend_electron_only
from ...core.constants import MHZ


[docs] class MicrowaveDrive(HamiltonianTerm): """ Time-dependent microwave drive in rotating frame. Parameters ---------- omega : float or callable Rabi frequency in MHz. Can be: - float: constant Rabi frequency - callable: omega(t) returning Rabi frequency at time t phase : float or callable Phase in radians. Can be: - float: constant phase - callable: phase(t) returning phase at time t detuning : float Frequency detuning from resonance in MHz. Default: 0 manifold : str Which electronic manifold: "ground", "excited", or "both". Default: "ground" name : str, optional Custom name for the term Attributes ---------- omega : float or callable Rabi frequency parameter phase : float or callable Phase parameter detuning : float Detuning in MHz manifold : str Target manifold Examples -------- >>> from sim import HamiltonianBuilder >>> from sim.hamiltonian.terms import ZFS, MicrowaveDrive >>> >>> # Constant drive >>> H = HamiltonianBuilder() >>> H.add(ZFS(D=2.87)) >>> H.add(MicrowaveDrive(omega=10, phase=0)) # 10 MHz Rabi frequency >>> >>> # Time-dependent Rabi frequency (pulsed) >>> def rabi_pulse(t): ... if 0 <= t < 100e-9: # 100 ns pulse ... return 10.0 # 10 MHz ... return 0.0 >>> >>> H.add(MicrowaveDrive(omega=rabi_pulse)) >>> >>> # Gaussian pulse shape >>> def gaussian_rabi(t): ... t0, sigma = 50e-9, 20e-9 ... return 20 * np.exp(-(t - t0)**2 / (2 * sigma**2)) >>> >>> H.add(MicrowaveDrive(omega=gaussian_rabi)) Notes ----- The MW drive is always considered time-dependent even if omega and phase are constants, because it represents an oscillating field. The rotating wave approximation (RWA) assumes near-resonant driving. For large detuning, the RWA breaks down. """
[docs] def __init__( self, omega: Union[float, Callable] = 0.0, # MHz phase: Union[float, Callable] = 0.0, # radians detuning: float = 0.0, # MHz manifold: str = "ground", name: Optional[str] = None ): super().__init__(name=name) self._omega_param = omega self._phase_param = phase self.detuning = detuning self.manifold = manifold # Determine if omega/phase are functions or constants self._omega_is_func = callable(omega) self._phase_is_func = callable(phase)
@property def is_time_dependent(self) -> bool: """MW drive is always time-dependent.""" return True def _get_omega(self, t: float) -> float: """Get Rabi frequency at time t in MHz.""" if self._omega_is_func: return self._omega_param(t) return self._omega_param def _get_phase(self, t: float) -> float: """Get phase at time t in radians.""" if self._phase_is_func: return self._phase_param(t) return self._phase_param def _build_3x3(self, Omega: float, phi: float, Delta: float) -> np.ndarray: """ Build 3×3 MW drive Hamiltonian. Parameters ---------- Omega : float Rabi frequency in rad/s phi : float Phase in radians Delta : float Detuning in rad/s Returns ------- np.ndarray 3×3 complex matrix in rad/s """ S = spin1_operators() # H = (Ω/2)(S₊ e^(-iφ) + S₋ e^(+iφ)) + Δ Sz # S₊ = Sx + i·Sy, S₋ = Sx - i·Sy H = ( (Omega / 2) * (S.Sp * np.exp(-1j * phi) + S.Sm * np.exp(1j * phi)) + Delta * S.Sz ) return H
[docs] def build(self, t: float = 0.0) -> np.ndarray: """ Build the 18×18 MW drive Hamiltonian at time t. Parameters ---------- t : float Time in seconds Returns ------- np.ndarray 18×18 complex matrix in rad/s Notes ----- The MW drive typically only acts on the ground state manifold in standard ODMR experiments. """ # Get time-dependent parameters Omega = self._get_omega(t) * MHZ # Convert to rad/s phi = self._get_phase(t) Delta = self.detuning * MHZ # Build 3×3 matrix H_3x3 = self._build_3x3(Omega, phi, Delta) # Extend to 18×18 if self.manifold == "both": # Act on both g and e manifolds I2 = np.eye(2, dtype=np.complex128) I3 = np.eye(3, dtype=np.complex128) return np.kron(np.kron(I2, H_3x3), I3) else: # Act only on ground or excited manifold return extend_electron_only(H_3x3, self.manifold)
[docs] def pi_time(self) -> Optional[float]: """ Calculate π-pulse duration for constant Rabi frequency. Returns ------- float or None π-pulse duration in seconds, or None if Rabi freq is time-dependent """ if self._omega_is_func: return None if self._omega_param == 0: return None # π = Ω * t_π => t_π = π / Ω return np.pi / (self._omega_param * MHZ)
def __repr__(self) -> str: if self._omega_is_func: return f"MicrowaveDrive(omega=func, manifold={self.manifold})" else: return f"MicrowaveDrive(omega={self._omega_param} MHz, manifold={self.manifold})"