General Example

Contents

General Example#

This is a generic example demonstrating basic usage patterns of the latex_parser library. It serves as a minimal end-to-end demo.

Source#

  1#!/usr/bin/env python
  2# coding: utf-8
  3# flake8: noqa
  4
  5import sys
  6from pathlib import Path
  7
  8import matplotlib.pyplot as plt
  9import numpy as np
 10from qutip import (
 11    about,
 12    basis,
 13    destroy,
 14    mesolve,
 15    qeye,
 16    tensor,
 17)
 18
 19ROOT = Path(__file__).resolve().parents[1]
 20if str(ROOT) not in sys.path:
 21    sys.path.insert(0, str(ROOT))
 22
 23from latex_parser.latex_api import compile_model as compile_latex_model
 24
 25
 26def run_vacuum_rabi():
 27    """
 28    Reproduce the QuTiP vacuum Rabi oscillations tutorial, but build
 29    H and c_ops from your LaTeX DSL instead of manual Qobj expressions.
 30    """
 31    # === Problem parameters (same physics as the QuTiP example) ===
 32    N = 15  # number of cavity Fock states
 33    omega_c = 1.0 * 2 * np.pi  # \omega_c: cavity frequency
 34    omega_a = 1.0 * 2 * np.pi  # \omega_a: atom frequency
 35    g = 0.05 * 2 * np.pi  # g: coupling strength
 36    kappa = 0.005  # \kappa: cavity dissipation rate
 37    gamma = 0.05  # \gamma: atom dissipation rate
 38    n_th_a = 0.0  # thermal photon number (environment)
 39    use_rwa = True
 40
 41    tlist = np.linspace(0, 40, 100)
 42
 43    # === Build model at T = 0 using the simple API ===
 44    if use_rwa:
 45        H_latex = r"""
 46            \omega_c \hat{n}_{1}
 47            + \frac{\omega_a}{2} \sigma_{z,1}
 48            + g \left( a_{1}^{\dagger} \sigma_{-,1}
 49                     + a_{1} \sigma_{+,1} \right)
 50        """
 51    else:
 52        H_latex = r"""
 53            \omega_c \hat{n}_{1}
 54            + \frac{\omega_a}{2} \sigma_{z,1}
 55            + g \left( a_{1}^{\dagger} + a_{1} \right)
 56                \left( \sigma_{-,1} + \sigma_{+,1} \right)
 57        """
 58
 59    c_ops_latex = [
 60        r"\sqrt{\kappa (1 + n_{th})} \, a_{1}",
 61        r"\sqrt{\kappa n_{th}} \, a_{1}^{\dagger}",
 62        r"\sqrt{\gamma} \, \sigma_{-,1}",
 63    ]
 64
 65    params = {
 66        "omega_c": omega_c,
 67        "omega_a": omega_a,
 68        "g": g,
 69        "kappa": kappa,
 70        "gamma": gamma,
 71        "n_th": n_th_a,
 72    }
 73
 74    compiled_0 = compile_latex_model(
 75        H_latex,
 76        params,
 77        c_ops_latex=c_ops_latex,
 78        qubits=1,
 79        bosons=[N],
 80        t_name="t",
 81    )
 82
 83    H_0 = compiled_0.H  # Qobj (static)
 84    c_ops_0 = compiled_0.c_ops
 85    args_0 = compiled_0.args  # currently just the params dict
 86
 87    # === Initial state ===
 88    # Hilbert ordering: [qubit, boson] -> 2 ⊗ N
 89    # "Atom excited, cavity ground" -> |e> ⊗ |0>.
 90    # QuTiP tutorial uses tensor(basis(N, 0), basis(2, 0)) with different ordering;
 91    # we swap to match [qubit, boson].
 92    psi0 = tensor(basis(2, 0), basis(N, 0))
 93
 94    # === Operators for expectation values (cavity & atom excitation) ===
 95    # Cavity annihilation in our ordering: I_qubit ⊗ a
 96    a = tensor(qeye(2), destroy(N))
 97    n_cav = a.dag() * a
 98
 99    # Atomic excitation projector (σ_+ σ_- in our ordering)
100    sm_raising = tensor(destroy(2).dag(), qeye(N))  # σ_+
101    atom_excited_proj = sm_raising.dag() * sm_raising  # σ_- σ_+
102
103    e_ops = [n_cav, atom_excited_proj]
104
105    # === Evolve at T = 0 ===
106    output_0 = mesolve(H_0, psi0, tlist, c_ops_0, e_ops, args=args_0)
107
108    # === Now finite temperature: n_th_a > 0 ===
109    n_th_a = 2.0
110
111    params_T = dict(params)
112    params_T["n_th"] = n_th_a
113    compiled_T = compile_latex_model(
114        H_latex,
115        params_T,
116        c_ops_latex=c_ops_latex,
117        qubits=1,
118        bosons=[N],
119        t_name="t",
120    )
121
122    H_T = compiled_T.H
123    c_ops_T = compiled_T.c_ops
124    args_T = compiled_T.args
125
126    output_T = mesolve(H_T, psi0, tlist, c_ops_T, e_ops, args=args_T)
127
128    # === Plot T = 0 ===
129    fig, ax = plt.subplots(figsize=(8, 5))
130    ax.plot(tlist, output_0.expect[0], label="Cavity")
131    ax.plot(tlist, output_0.expect[1], label="Atom excited state")
132    ax.legend()
133    ax.set_xlabel("Time")
134    ax.set_ylabel("Occupation probability")
135    ax.set_title("Vacuum Rabi oscillations at T = 0")
136    plt.tight_layout()
137    plt.show()
138
139    # === Plot T > 0 ===
140    fig, ax = plt.subplots(figsize=(8, 5))
141    ax.plot(tlist, output_T.expect[0], label="Cavity")
142    ax.plot(tlist, output_T.expect[1], label="Atom excited state")
143    ax.legend()
144    ax.set_xlabel("Time")
145    ax.set_ylabel("Occupation probability")
146    ax.set_title(f"Vacuum Rabi oscillations at T = {n_th_a}")
147    plt.tight_layout()
148    plt.show()
149
150    # === Optional: analytic test in RWA, no collapse operators ===
151    if use_rwa:
152        output_no_cops = mesolve(H_0, psi0, tlist, [], e_ops, args=args_0)
153        freq = 0.25 * np.sqrt(g**2 * (N + 1))
154        atom_pop = np.array(output_no_cops.expect[1])
155        analytic = (np.cos(tlist * freq)) ** 2
156        assert np.allclose(atom_pop, analytic, atol=1e-3)
157
158    print()
159    print("QuTiP / environment info:")
160    about()
161
162
163if __name__ == "__main__":
164    run_vacuum_rabi()

Run#

python examples/example.py

Notes#

  • This example is a good starting point for understanding the overall workflow.