From 4db30c1ec70362fd73843ec4021e0d4840bccfe3 Mon Sep 17 00:00:00 2001 From: Orjan Ameye Date: Fri, 3 Apr 2026 17:34:14 +0200 Subject: [PATCH 1/4] Add benchmark suite --- .gitignore | 3 +- Makefile | 2 +- benchmarks/Project.toml | 8 ++ benchmarks/correlations.jl | 67 ++++++++++++++++ benchmarks/interaction_picture.jl | 88 +++++++++++++++++++++ benchmarks/pulse_couplings.jl | 42 ++++++++++ benchmarks/runbenchmarks.jl | 27 +++++++ benchmarks/slh_algebra.jl | 124 ++++++++++++++++++++++++++++++ benchmarks/translation.jl | 105 +++++++++++++++++++++++++ 9 files changed, 464 insertions(+), 2 deletions(-) create mode 100644 benchmarks/Project.toml create mode 100644 benchmarks/correlations.jl create mode 100644 benchmarks/interaction_picture.jl create mode 100644 benchmarks/pulse_couplings.jl create mode 100644 benchmarks/runbenchmarks.jl create mode 100644 benchmarks/slh_algebra.jl create mode 100644 benchmarks/translation.jl diff --git a/.gitignore b/.gitignore index 11d7f4d..8b2f032 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,5 @@ JuliaLocalPreferences.toml # additional /.vscode/ docs/src/examples/jupyter_notebooks/.ipynb_checkpoints -docs/Manifest.toml \ No newline at end of file +docs/Manifest.toml +benchmarks_output.json diff --git a/Makefile b/Makefile index 062ae45..4fa93f1 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ docs: bench: ${JULIA} --project=benchmarks -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' - ${JULIA} --project=docs benchmarks/runbenchmarks.jl + ${JULIA} --project=benchmarks benchmarks/runbenchmarks.jl all: setup format test docs diff --git a/benchmarks/Project.toml b/benchmarks/Project.toml new file mode 100644 index 0000000..59d1325 --- /dev/null +++ b/benchmarks/Project.toml @@ -0,0 +1,8 @@ +[deps] +BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +QuantumInputOutput = "18f9eda6-924c-47c7-a881-996695cfd7c6" +QuantumOptics = "6e0679c1-51ea-5a7c-ac74-d61b76210b0c" +QuantumOpticsBase = "4f57444f-1401-5e15-980d-4471b28d5678" +SecondQuantizedAlgebra = "f7aa4685-e143-4cb6-a7f3-073579757907" +SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b" diff --git a/benchmarks/correlations.jl b/benchmarks/correlations.jl new file mode 100644 index 0000000..9e47e28 --- /dev/null +++ b/benchmarks/correlations.jl @@ -0,0 +1,67 @@ +""" +Correlation function benchmarks — two-time correlation matrices. +Based on example 01-1: single photon cavity scattering. +""" +function benchmark_correlations!(SUITE) + SUITE["Correlations"] = BenchmarkGroup() + + ## Setup: single photon scattering through a cavity + hu1 = FockSpace(:u1) + hc1 = FockSpace(:c1) + hv1 = FockSpace(:v1) + h = hu1 ⊗ hc1 ⊗ hv1 + + au = Destroy(h, :a_u, 1) + c = Destroy(h, :c, 2) + av = Destroy(h, :a_v, 3) + + gu, Δ, γ = rnumbers("g_u Δ γ") + gv = cnumber("g_v") + + G_u = SLH(1, gu * au, 0) + G_c = SLH(1, √(γ) * c, Δ * c'c) + G_v = SLH(1, gv * av, 0) + G_cas = ▷(G_u, G_c, G_v) + + H_sym = get_hamiltonian(G_cas) + L_sym = get_lindblad(G_cas)[1] + + γ_ = 1.0 + σ_pulse = 1 / γ_ + T = [0:0.002:1;] * 12σ_pulse + u1(t) = 1 / (sqrt(σ_pulse) * π^(1 / 4)) * exp(-(t - 4σ_pulse)^2 / (2 * σ_pulse^2)) + gu_t = u_to_gu(u1, T) + + bu1 = FockBasis(1) + bc1 = FockBasis(1) + bv1 = FockBasis(1) + b = bu1 ⊗ bc1 ⊗ bv1 + + dict_p = Dict([γ, Δ, gv] .=> [γ_, 0.0, 0]) + dict_p_t = Dict(gu => gu_t) + + H_QO = translate_qo(H_sym, b; parameter=dict_p, time_parameter=dict_p_t) + L_QO = translate_qo(L_sym, b; parameter=dict_p, time_parameter=dict_p_t) + + function input_output(t, ρ) + Ht = H_QO(t) + J = [L_QO(t)] + return Ht, J, dagger.(J) + end + + ψ0 = fockstate(bu1, 1) ⊗ fockstate(bc1, 0) ⊗ fockstate(bv1, 0) + _, ρt = timeevolution.master_dynamic(T, ψ0, input_output) + + au_qo = translate_qo(au, b) + c_qo = translate_qo(c, b) + Ls(t) = gu_t(t) * au_qo + √(γ_) * c_qo + + ## --- Two-time correlation --- + + SUITE["Correlations"]["two-time"] = BenchmarkGroup() + + SUITE["Correlations"]["two-time"]["single photon cavity"] = + @benchmarkable two_time_corr_matrix($T, $ρt, $input_output, $Ls) + + return nothing +end diff --git a/benchmarks/interaction_picture.jl b/benchmarks/interaction_picture.jl new file mode 100644 index 0000000..b7cecec --- /dev/null +++ b/benchmarks/interaction_picture.jl @@ -0,0 +1,88 @@ +""" +Interaction picture benchmarks — computing coefficient matrices for mode transformations. +Based on Christiansen et al., PRA 107, 013706 (2023). +""" +function benchmark_interaction_picture!(SUITE) + SUITE["Interaction Picture"] = BenchmarkGroup() + + ## Setup: u -> TLS -> v system + γ_ = 1.0 + τ = 1 / γ_ + t_p = 4 / γ_ + u(t) = 1 / (sqrt(τ) * π^(1 / 4)) * exp(-0.5 * ((t - t_p) / τ)^2) + + T_end = 12.0 + T = [0:0.005:1;] * T_end + + gu_t = u_to_gu(u, T) + gv_t = v_to_gv(u, T) + A_uv = interaction_picture_A_2modes(gu_t, gv_t) + + ## --- Coupling matrix A(t) evaluation (called every ODE step) --- + + SUITE["Interaction Picture"]["coupling matrix evaluation"] = BenchmarkGroup() + + t_mid = T[length(T) ÷ 2] + + # 2-mode A(t) + SUITE["Interaction Picture"]["coupling matrix evaluation"]["2 modes"] = + @benchmarkable $A_uv($t_mid) + + # 4-mode A(t) — more realistic for multi-port systems + u2(t) = 1 / (sqrt(τ) * π^(1 / 4)) * exp(-0.5 * ((t - t_p * 1.5) / τ)^2) + g3_t = u_to_gu(u2, T) + g4_t = v_to_gv(u2, T) + A_4m = interaction_picture_A_4modes(gu_t, gv_t, g3_t, g4_t) + + SUITE["Interaction Picture"]["coupling matrix evaluation"]["4 modes"] = + @benchmarkable $A_4m($t_mid) + + ## --- Coefficient matrix M(t) --- + + SUITE["Interaction Picture"]["coefficient matrix M"] = BenchmarkGroup() + + SUITE["Interaction Picture"]["coefficient matrix M"]["numerical (ODE)"] = + @benchmarkable interaction_picture_M($A_uv, $T) + + SUITE["Interaction Picture"]["coefficient matrix M"]["analytical (2 equal modes)"] = + @benchmarkable interaction_picture_M_2modes_equal($u, $T) + + ## --- Symbolic operator substitution --- + + hu = FockSpace(:u) + hs = NLevelSpace(:s, 2) + hv = FockSpace(:v) + h = hu ⊗ hs ⊗ hv + + au_sym = Destroy(h, :a_u, 1) + av_sym = Destroy(h, :a_v, 3) + σ_sym = Transition(h, :σ, 1, 2, 2) + + gu_sym, γ_sym, gv_sym = rnumbers("gu γ gv") + + G_u = SLH(1, gu_sym' * au_sym, 0) + G_s = SLH(1, sqrt(γ_sym) * σ_sym, 0) + G_v = SLH(1, gv_sym' * av_sym, 0) + G_cas = ▷(G_u, G_s, G_v) + + H = get_hamiltonian(G_cas) + L = get_lindblad(G_cas)[1] + H_uv = get_hamiltonian(▷(G_u, G_v)) + H_int_ = simplify(H - H_uv) + + M_sym(i, j) = cnumber("M_{$(i)$(j)}") + a0_ls = [au_sym, av_sym] + la = length(a0_ls) + a_int_ls = [sum(M_sym(i, j) * a0_ls[j] for j in 1:la) for i in 1:la] + int_dict = Dict(a0_ls .=> a_int_ls) + + SUITE["Interaction Picture"]["operator substitution"] = BenchmarkGroup() + + SUITE["Interaction Picture"]["operator substitution"]["TLS cascade"] = + @benchmarkable begin + simplify(substitute_operators($H_int_, $int_dict)) + simplify(substitute_operators($L, $int_dict)) + end + + return nothing +end diff --git a/benchmarks/pulse_couplings.jl b/benchmarks/pulse_couplings.jl new file mode 100644 index 0000000..928a16c --- /dev/null +++ b/benchmarks/pulse_couplings.jl @@ -0,0 +1,42 @@ +""" +Pulse coupling benchmarks — converting temporal mode shapes to virtual cavity couplings. +""" +function benchmark_pulse_couplings!(SUITE) + SUITE["Pulse Couplings"] = BenchmarkGroup() + + ## Setup: Gaussian pulse on a realistic time grid + τ = 3.0 + σ = 0.5 + δ = 0.3 + T = [0.0:0.0001:1;] * 6 # 60001 points + + u(t) = 1 / (sqrt(σ) * π^(1 / 4)) * exp(-0.5 * ((t - τ) / σ)^2) * exp(1im * δ * t) + v(t) = u(t) + + ## --- Single-pulse coupling computation --- + + SUITE["Pulse Couplings"]["single-pulse"] = BenchmarkGroup() + + SUITE["Pulse Couplings"]["single-pulse"]["input"] = + @benchmarkable u_to_gu($u, $T) + + SUITE["Pulse Couplings"]["single-pulse"]["output"] = + @benchmarkable v_to_gv($v, $T) + + ## --- Effective multi-pulse couplings --- + + SUITE["Pulse Couplings"]["multi-pulse"] = BenchmarkGroup() + + v1(t) = 1 / (sqrt(σ) * π^(1 / 4)) * exp(-0.5 * ((t - (τ - 2σ)) / σ)^2) + v2(t) = 1 / (sqrt(σ) * π^(1 / 4)) * exp(-0.5 * ((t - (τ + 2σ)) / σ)^2) + u1(t) = 1 / (sqrt(σ) * π^(1 / 4)) * exp(-0.5 * ((t - (τ - 2σ)) / σ)^2) + u2(t) = 1 / (sqrt(σ) * π^(1 / 4)) * exp(-0.5 * ((t - (τ + 2σ)) / σ)^2) + + SUITE["Pulse Couplings"]["multi-pulse"]["output 2 modes"] = + @benchmarkable v_eff([$v1, $v2], $T, 2) + + SUITE["Pulse Couplings"]["multi-pulse"]["input 2 modes"] = + @benchmarkable u_eff([$u1, $u2], $T, 2) + + return nothing +end diff --git a/benchmarks/runbenchmarks.jl b/benchmarks/runbenchmarks.jl new file mode 100644 index 0000000..7932fe6 --- /dev/null +++ b/benchmarks/runbenchmarks.jl @@ -0,0 +1,27 @@ +using BenchmarkTools +using QuantumInputOutput +using SecondQuantizedAlgebra +using QuantumOptics +using QuantumOpticsBase +using SymbolicUtils +using LinearAlgebra + +const SUITE = BenchmarkGroup() + +include("slh_algebra.jl") +include("translation.jl") +include("pulse_couplings.jl") +include("interaction_picture.jl") +include("correlations.jl") + +benchmark_slh_algebra!(SUITE) +benchmark_translation!(SUITE) +benchmark_pulse_couplings!(SUITE) +benchmark_interaction_picture!(SUITE) +benchmark_correlations!(SUITE) + +BenchmarkTools.tune!(SUITE) +results = BenchmarkTools.run(SUITE; verbose=true) +display(median(results)) + +BenchmarkTools.save("benchmarks_output.json", median(results)) diff --git a/benchmarks/slh_algebra.jl b/benchmarks/slh_algebra.jl new file mode 100644 index 0000000..7926273 --- /dev/null +++ b/benchmarks/slh_algebra.jl @@ -0,0 +1,124 @@ +""" +SLH algebra benchmarks — symbolic and numeric composition of quantum networks. +""" +function benchmark_slh_algebra!(SUITE) + SUITE["SLH Algebra"] = BenchmarkGroup() + + ## --- Symbolic SLH (SecondQuantizedAlgebra operators) --- + + SUITE["SLH Algebra"]["symbolic"] = BenchmarkGroup() + + # Setup: 3-cavity cascade (example 01-1) + hu1 = FockSpace(:u1) + hc1 = FockSpace(:c1) + hv1 = FockSpace(:v1) + h = hu1 ⊗ hc1 ⊗ hv1 + + au = Destroy(h, :a_u, 1) + c = Destroy(h, :c, 2) + av = Destroy(h, :a_v, 3) + + gu, Δ, γ = rnumbers("g_u Δ γ") + gv = cnumber("g_v") + + G_u = SLH(1, gu * au, 0) + G_c = SLH(1, √(γ) * c, Δ * c'c) + G_v = SLH(1, gv * av, 0) + + SUITE["SLH Algebra"]["symbolic"]["3-cavity cascade"] = + @benchmarkable begin + G_cas = ▷($G_u, $G_c, $G_v) + get_hamiltonian(G_cas) + get_lindblad(G_cas) + end + + SUITE["SLH Algebra"]["symbolic"]["concatenate + cascade"] = + @benchmarkable begin + G_R = $G_u ▷ $G_c + G_L = $G_v + G_R ⊞ G_L + end + + # Feedback: coherent-feedback OPO loop (from test_feedback.jl) + hs = FockSpace(:s) + a_fb = Destroy(hs, :a, 1) + κ_fb = 0.7 + ϵ_fb = 0.45 + η_fb = 0.65 + r_fb = simplify(√(1 - η_fb^2)) + + G_opo = SLH(1, √(κ_fb) * a_fb, 1im * ϵ_fb * (a_fb'^2 - a_fb^2)) + G_bs = SLH([-r_fb η_fb; η_fb r_fb], [0, 0], 0) + G_fb_unconnected = G_opo ⊞ G_bs + + SUITE["SLH Algebra"]["symbolic"]["feedback OPO loop"] = + @benchmarkable feedback($G_fb_unconnected, 1 => 2, 2 => 1) + + ## --- Numeric SLH (SLHqo with QuantumOptics operators) --- + + SUITE["SLH Algebra"]["numeric (SLHqo)"] = BenchmarkGroup() + + # Setup: 2-QD bidirectional waveguide (example 05-2, realistic basis) + N = 2 + bu = FockBasis(20) + ba = NLevelBasis(2) + b_qds = tensor([ba for _ in 1:N]...) + b = bu ⊗ b_qds + + σ(i, j, k) = embed(b, i + 1, transition(ba, j, k)) + a_u = destroy(bu) ⊗ one(b_qds) + + γ_val = 1.0 + β = 0.9 + γRn = fill(γ_val * β / 2, N) + γLn = fill(γ_val * β / 2, N) + ϕn = [π / 10] + + σt = 0.8 + t0 = 4σt + T = [0:0.005:1;] * 3t0 + u1(t) = 1 / (sqrt(σt) * π^(1 / 4)) * exp(-(t - t0)^2 / (2 * σt^2)) + gu_t = u_to_gu(u1, T) + + G_u_qo = SLHqo(1, t -> gu_t(t) * a_u, 0 * one(b)) + G_ϕ_12 = SLHqo(exp(1im * ϕn[1]), 0 * one(b), 0 * one(b)) + G_R1 = SLHqo(1, √(γRn[1]) * σ(1, 1, 2), 0 * one(b)) + G_R2 = SLHqo(1, √(γRn[2]) * σ(2, 1, 2), 0 * one(b)) + G_L1 = SLHqo(1, √(γLn[1]) * σ(1, 1, 2), 0 * one(b)) + G_L2 = SLHqo(1, √(γLn[2]) * σ(2, 1, 2), 0 * one(b)) + + SUITE["SLH Algebra"]["numeric (SLHqo)"]["2-QD waveguide composition"] = + @benchmarkable begin + G_R_t = $G_u_qo ▷ $G_R1 ▷ $G_ϕ_12 ▷ $G_R2 + G_L_t = $G_L2 ▷ $G_ϕ_12 ▷ $G_L1 + G_t = G_R_t ⊞ G_L_t + get_hamiltonian(G_t) + get_lindblad(G_t) + end + + ## --- Time-dependent closure evaluation (the ODE hot loop) --- + + SUITE["SLH Algebra"]["closure evaluation"] = BenchmarkGroup() + + # Build the composed network once, then benchmark calling H(t) and L(t) + G_R_t = G_u_qo ▷ G_R1 ▷ G_ϕ_12 ▷ G_R2 + G_L_t = G_L2 ▷ G_ϕ_12 ▷ G_L1 + G_t = G_R_t ⊞ G_L_t + + H_f = get_hamiltonian(G_t) + L_f = get_lindblad(G_t) + + t_mid = T[length(T) ÷ 2] + + SUITE["SLH Algebra"]["closure evaluation"]["2-QD waveguide H(t)"] = + @benchmarkable $H_f($t_mid) + + SUITE["SLH Algebra"]["closure evaluation"]["2-QD waveguide L(t)"] = + @benchmarkable begin + for Li in $L_f + Li($t_mid) + end + end + + return nothing +end diff --git a/benchmarks/translation.jl b/benchmarks/translation.jl new file mode 100644 index 0000000..ad0128e --- /dev/null +++ b/benchmarks/translation.jl @@ -0,0 +1,105 @@ +""" +Translation benchmarks — converting symbolic expressions to numeric QuantumOptics operators. +""" +function benchmark_translation!(SUITE) + SUITE["Translation"] = BenchmarkGroup() + + ## Symbolic setup: atom-cavity system + @rnumbers κ_R κ_L Δ_sym + @cnumbers E + + hc = FockSpace(:cavity) + ha_ = NLevelSpace("a", 2) + h = hc ⊗ ha_ + + a = Destroy(h, :a, 1) + σ(i, j) = Transition(h, "σ", i, j, 2) + + ## Derive H and L from cascade + gu_sym, γ_sym, gv_sym = rnumbers("gu γ gv") + hu_ = FockSpace(:u) + hc_ = FockSpace(:c) + hv_ = FockSpace(:v) + h3 = hu_ ⊗ hc_ ⊗ hv_ + au_ = Destroy(h3, :a_u, 1) + c_ = Destroy(h3, :c, 2) + av_ = Destroy(h3, :a_v, 3) + + G_u = SLH(1, gu_sym * au_, 0) + G_c = SLH(1, √(γ_sym) * c_, Δ_sym * c_'c_) + G_v = SLH(1, gv_sym * av_, 0) + G_cas = ▷(G_u, G_c, G_v) + H_sym = get_hamiltonian(G_cas) + L_sym = get_lindblad(G_cas)[1] + + ## --- Static translation (no time dependence) --- + + SUITE["Translation"]["static"] = BenchmarkGroup() + + bc = FockBasis(4) + ba = NLevelBasis(2) + b_multi = bc ⊗ ba + + dict_p_static = Dict([κ_R, κ_L, Δ_sym] .=> [1.5, 1.0, 0.2]) + + # Composite expression: operator + parameter + transition + expr_composite = a * 3 + Δ_sym * σ(2, 2) + + SUITE["Translation"]["static"]["atom-cavity"] = + @benchmarkable translate_qo($expr_composite, $b_multi; parameter=$dict_p_static) + + ## --- Time-dependent translation --- + + SUITE["Translation"]["time-dependent"] = BenchmarkGroup() + + E_t(t) = 2 * t + 1im + dict_p_t = Dict(E => E_t) + + # Expression with time-dependent + static parameters and operators + expr_td = a * 3 * conj(E) + Δ_sym * σ(2, 2) + + SUITE["Translation"]["time-dependent"]["atom-cavity"] = + @benchmarkable translate_qo( + $expr_td, $b_multi; parameter=$dict_p_static, time_parameter=$dict_p_t + ) + + # Full cascade H and L translation (realistic workflow from example 06-1) + γ_ = 1.0 + σ_pulse = 1 / γ_ + T = [0:0.002:1;] * 12σ_pulse + u_pulse(t) = 1 / (sqrt(σ_pulse) * π^(1 / 4)) * exp(-(t - 4σ_pulse)^2 / (2 * σ_pulse^2)) + gu_t = u_to_gu(u_pulse, T) + gv_t = v_to_gv(u_pulse, T) + + # Realistic basis sizes from interaction picture example (n_ph=20) + bu = FockBasis(20) + bc3 = FockBasis(6) + bv = FockBasis(6) + b_cav = bu ⊗ bc3 ⊗ bv + + dict_p_cav = Dict([γ_sym, Δ_sym, gv_sym] .=> [γ_, 0.0, 0]) + dict_p_t_cav = Dict([gu_sym, gv_sym] .=> [gu_t, gv_t]) + + SUITE["Translation"]["time-dependent"]["3-cavity H+L"] = + @benchmarkable begin + translate_qo($H_sym, $b_cav; parameter=$dict_p_cav, time_parameter=$dict_p_t_cav) + translate_qo($L_sym, $b_cav; parameter=$dict_p_cav, time_parameter=$dict_p_t_cav) + end + + ## --- Closure evaluation (the ODE hot loop) --- + + SUITE["Translation"]["closure evaluation"] = BenchmarkGroup() + + H_QO = translate_qo(H_sym, b_cav; parameter=dict_p_cav, time_parameter=dict_p_t_cav) + L_QO = translate_qo(L_sym, b_cav; parameter=dict_p_cav, time_parameter=dict_p_t_cav) + + t_mid = T[length(T) ÷ 2] + + SUITE["Translation"]["closure evaluation"]["3-cavity H(t)"] = + @benchmarkable $H_QO($t_mid) + + SUITE["Translation"]["closure evaluation"]["3-cavity L(t)"] = + @benchmarkable $L_QO($t_mid) + + return nothing +end From cb905abf685f3840b9e959b3c3f850686e448a3c Mon Sep 17 00:00:00 2001 From: Orjan Ameye Date: Fri, 3 Apr 2026 17:43:58 +0200 Subject: [PATCH 2/4] format --- .github/workflows/Benchmarks.yaml | 67 +++++++++++++++++++++++++++++++ benchmarks/correlations.jl | 4 +- benchmarks/interaction_picture.jl | 4 +- benchmarks/pulse_couplings.jl | 6 +-- benchmarks/runbenchmarks.jl | 2 +- benchmarks/slh_algebra.jl | 37 ++++++++--------- benchmarks/translation.jl | 32 +++++++++------ 7 files changed, 110 insertions(+), 42 deletions(-) create mode 100644 .github/workflows/Benchmarks.yaml diff --git a/.github/workflows/Benchmarks.yaml b/.github/workflows/Benchmarks.yaml new file mode 100644 index 0000000..ebd7c6e --- /dev/null +++ b/.github/workflows/Benchmarks.yaml @@ -0,0 +1,67 @@ +name: Benchmark Tracking +on: + push: + branches: + - 'main' + paths: + - '.github/workflows/Benchmarks.yml' + - 'src/**' + - 'ext/**' + - 'test/runtests.jl' + - 'Project.toml' + pull_request: + branches: + - 'main' + paths: + - '.github/workflows/Benchmarks.yml' + - 'src/**' + - 'ext/**' + - 'test/**' + - 'Project.toml' + types: + - opened + - reopened + - synchronize + - ready_for_review + workflow_dispatch: + +permissions: + actions: write + contents: write + deployments: write + +jobs: + benchmark: + runs-on: ubuntu-latest + if: ${{ !github.event.pull_request.draft }} + steps: + - uses: actions/checkout@v6 + - uses: julia-actions/setup-julia@v2 + with: + version: '1.12' # should run on same julia version + arch: x64 + - uses: julia-actions/cache@v3 + - name: Run benchmark + run: | + cd benchmarks + julia --project --threads=2 --color=yes -e ' + using Pkg; + Pkg.develop(PackageSpec(path=joinpath(pwd(), ".."))); + Pkg.instantiate(); + include("runbenchmarks.jl")' + + # this will update benchmarks/data.js in gh-pages branch + - name: Parse & Upload Benchmark Results + uses: benchmark-action/github-action-benchmark@v1 + with: + name: Benchmark Results + tool: "julia" + output-file-path: benchmarks/benchmarks_output.json + github-token: ${{ secrets.GITHUB_TOKEN }} + alert-threshold: "130%" + fail-threshold: "170%" + comment-on-alert: true + fail-on-alert: true + benchmark-data-dir-path: benchmarks + max-items-in-chart: 100 + auto-push: ${{ github.event_name != 'pull_request' }} \ No newline at end of file diff --git a/benchmarks/correlations.jl b/benchmarks/correlations.jl index 9e47e28..a6e93d6 100644 --- a/benchmarks/correlations.jl +++ b/benchmarks/correlations.jl @@ -40,8 +40,8 @@ function benchmark_correlations!(SUITE) dict_p = Dict([γ, Δ, gv] .=> [γ_, 0.0, 0]) dict_p_t = Dict(gu => gu_t) - H_QO = translate_qo(H_sym, b; parameter=dict_p, time_parameter=dict_p_t) - L_QO = translate_qo(L_sym, b; parameter=dict_p, time_parameter=dict_p_t) + H_QO = translate_qo(H_sym, b; parameter = dict_p, time_parameter = dict_p_t) + L_QO = translate_qo(L_sym, b; parameter = dict_p, time_parameter = dict_p_t) function input_output(t, ρ) Ht = H_QO(t) diff --git a/benchmarks/interaction_picture.jl b/benchmarks/interaction_picture.jl index b7cecec..f449c37 100644 --- a/benchmarks/interaction_picture.jl +++ b/benchmarks/interaction_picture.jl @@ -22,7 +22,7 @@ function benchmark_interaction_picture!(SUITE) SUITE["Interaction Picture"]["coupling matrix evaluation"] = BenchmarkGroup() - t_mid = T[length(T) ÷ 2] + t_mid = T[length(T)÷2] # 2-mode A(t) SUITE["Interaction Picture"]["coupling matrix evaluation"]["2 modes"] = @@ -73,7 +73,7 @@ function benchmark_interaction_picture!(SUITE) M_sym(i, j) = cnumber("M_{$(i)$(j)}") a0_ls = [au_sym, av_sym] la = length(a0_ls) - a_int_ls = [sum(M_sym(i, j) * a0_ls[j] for j in 1:la) for i in 1:la] + a_int_ls = [sum(M_sym(i, j) * a0_ls[j] for j = 1:la) for i = 1:la] int_dict = Dict(a0_ls .=> a_int_ls) SUITE["Interaction Picture"]["operator substitution"] = BenchmarkGroup() diff --git a/benchmarks/pulse_couplings.jl b/benchmarks/pulse_couplings.jl index 928a16c..a0c639d 100644 --- a/benchmarks/pulse_couplings.jl +++ b/benchmarks/pulse_couplings.jl @@ -17,11 +17,9 @@ function benchmark_pulse_couplings!(SUITE) SUITE["Pulse Couplings"]["single-pulse"] = BenchmarkGroup() - SUITE["Pulse Couplings"]["single-pulse"]["input"] = - @benchmarkable u_to_gu($u, $T) + SUITE["Pulse Couplings"]["single-pulse"]["input"] = @benchmarkable u_to_gu($u, $T) - SUITE["Pulse Couplings"]["single-pulse"]["output"] = - @benchmarkable v_to_gv($v, $T) + SUITE["Pulse Couplings"]["single-pulse"]["output"] = @benchmarkable v_to_gv($v, $T) ## --- Effective multi-pulse couplings --- diff --git a/benchmarks/runbenchmarks.jl b/benchmarks/runbenchmarks.jl index 7932fe6..f8abb40 100644 --- a/benchmarks/runbenchmarks.jl +++ b/benchmarks/runbenchmarks.jl @@ -21,7 +21,7 @@ benchmark_interaction_picture!(SUITE) benchmark_correlations!(SUITE) BenchmarkTools.tune!(SUITE) -results = BenchmarkTools.run(SUITE; verbose=true) +results = BenchmarkTools.run(SUITE; verbose = true) display(median(results)) BenchmarkTools.save("benchmarks_output.json", median(results)) diff --git a/benchmarks/slh_algebra.jl b/benchmarks/slh_algebra.jl index 7926273..551b327 100644 --- a/benchmarks/slh_algebra.jl +++ b/benchmarks/slh_algebra.jl @@ -25,19 +25,17 @@ function benchmark_slh_algebra!(SUITE) G_c = SLH(1, √(γ) * c, Δ * c'c) G_v = SLH(1, gv * av, 0) - SUITE["SLH Algebra"]["symbolic"]["3-cavity cascade"] = - @benchmarkable begin - G_cas = ▷($G_u, $G_c, $G_v) - get_hamiltonian(G_cas) - get_lindblad(G_cas) - end - - SUITE["SLH Algebra"]["symbolic"]["concatenate + cascade"] = - @benchmarkable begin - G_R = $G_u ▷ $G_c - G_L = $G_v - G_R ⊞ G_L - end + SUITE["SLH Algebra"]["symbolic"]["3-cavity cascade"] = @benchmarkable begin + G_cas = ▷($G_u, $G_c, $G_v) + get_hamiltonian(G_cas) + get_lindblad(G_cas) + end + + SUITE["SLH Algebra"]["symbolic"]["concatenate + cascade"] = @benchmarkable begin + G_R = $G_u ▷ $G_c + G_L = $G_v + G_R ⊞ G_L + end # Feedback: coherent-feedback OPO loop (from test_feedback.jl) hs = FockSpace(:s) @@ -62,7 +60,7 @@ function benchmark_slh_algebra!(SUITE) N = 2 bu = FockBasis(20) ba = NLevelBasis(2) - b_qds = tensor([ba for _ in 1:N]...) + b_qds = tensor([ba for _ = 1:N]...) b = bu ⊗ b_qds σ(i, j, k) = embed(b, i + 1, transition(ba, j, k)) @@ -108,17 +106,16 @@ function benchmark_slh_algebra!(SUITE) H_f = get_hamiltonian(G_t) L_f = get_lindblad(G_t) - t_mid = T[length(T) ÷ 2] + t_mid = T[length(T)÷2] SUITE["SLH Algebra"]["closure evaluation"]["2-QD waveguide H(t)"] = @benchmarkable $H_f($t_mid) - SUITE["SLH Algebra"]["closure evaluation"]["2-QD waveguide L(t)"] = - @benchmarkable begin - for Li in $L_f - Li($t_mid) - end + SUITE["SLH Algebra"]["closure evaluation"]["2-QD waveguide L(t)"] = @benchmarkable begin + for Li in $L_f + Li($t_mid) end + end return nothing end diff --git a/benchmarks/translation.jl b/benchmarks/translation.jl index ad0128e..c48f3c5 100644 --- a/benchmarks/translation.jl +++ b/benchmarks/translation.jl @@ -46,7 +46,7 @@ function benchmark_translation!(SUITE) expr_composite = a * 3 + Δ_sym * σ(2, 2) SUITE["Translation"]["static"]["atom-cavity"] = - @benchmarkable translate_qo($expr_composite, $b_multi; parameter=$dict_p_static) + @benchmarkable translate_qo($expr_composite, $b_multi; parameter = $dict_p_static) ## --- Time-dependent translation --- @@ -58,10 +58,12 @@ function benchmark_translation!(SUITE) # Expression with time-dependent + static parameters and operators expr_td = a * 3 * conj(E) + Δ_sym * σ(2, 2) - SUITE["Translation"]["time-dependent"]["atom-cavity"] = - @benchmarkable translate_qo( - $expr_td, $b_multi; parameter=$dict_p_static, time_parameter=$dict_p_t - ) + SUITE["Translation"]["time-dependent"]["atom-cavity"] = @benchmarkable translate_qo( + $expr_td, + $b_multi; + parameter = $dict_p_static, + time_parameter = $dict_p_t, + ) # Full cascade H and L translation (realistic workflow from example 06-1) γ_ = 1.0 @@ -80,20 +82,24 @@ function benchmark_translation!(SUITE) dict_p_cav = Dict([γ_sym, Δ_sym, gv_sym] .=> [γ_, 0.0, 0]) dict_p_t_cav = Dict([gu_sym, gv_sym] .=> [gu_t, gv_t]) - SUITE["Translation"]["time-dependent"]["3-cavity H+L"] = - @benchmarkable begin - translate_qo($H_sym, $b_cav; parameter=$dict_p_cav, time_parameter=$dict_p_t_cav) - translate_qo($L_sym, $b_cav; parameter=$dict_p_cav, time_parameter=$dict_p_t_cav) - end + SUITE["Translation"]["time-dependent"]["3-cavity H+L"] = @benchmarkable begin + translate_qo($H_sym, $b_cav; parameter = $dict_p_cav, time_parameter = $dict_p_t_cav) + translate_qo( + $L_sym, + $b_cav; + parameter = $dict_p_cav, + time_parameter = $dict_p_t_cav, + ) + end ## --- Closure evaluation (the ODE hot loop) --- SUITE["Translation"]["closure evaluation"] = BenchmarkGroup() - H_QO = translate_qo(H_sym, b_cav; parameter=dict_p_cav, time_parameter=dict_p_t_cav) - L_QO = translate_qo(L_sym, b_cav; parameter=dict_p_cav, time_parameter=dict_p_t_cav) + H_QO = translate_qo(H_sym, b_cav; parameter = dict_p_cav, time_parameter = dict_p_t_cav) + L_QO = translate_qo(L_sym, b_cav; parameter = dict_p_cav, time_parameter = dict_p_t_cav) - t_mid = T[length(T) ÷ 2] + t_mid = T[length(T)÷2] SUITE["Translation"]["closure evaluation"]["3-cavity H(t)"] = @benchmarkable $H_QO($t_mid) From a576bd0234eeee662e3726e4ed1781145686d8e6 Mon Sep 17 00:00:00 2001 From: Orjan Ameye Date: Fri, 3 Apr 2026 17:45:54 +0200 Subject: [PATCH 3/4] Add benchmarks directory to workflow trigger paths --- .github/workflows/Benchmarks.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/Benchmarks.yaml b/.github/workflows/Benchmarks.yaml index ebd7c6e..e79e2f2 100644 --- a/.github/workflows/Benchmarks.yaml +++ b/.github/workflows/Benchmarks.yaml @@ -17,6 +17,7 @@ on: - 'src/**' - 'ext/**' - 'test/**' + - 'benchmarks/**' - 'Project.toml' types: - opened From fa4fc540fc17115f5c5036ae49c7538377261625 Mon Sep 17 00:00:00 2001 From: Orjan Ameye Date: Fri, 3 Apr 2026 17:46:23 +0200 Subject: [PATCH 4/4] Remove test directory from benchmark workflow trigger paths --- .github/workflows/Benchmarks.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/Benchmarks.yaml b/.github/workflows/Benchmarks.yaml index e79e2f2..debe9d7 100644 --- a/.github/workflows/Benchmarks.yaml +++ b/.github/workflows/Benchmarks.yaml @@ -16,7 +16,6 @@ on: - '.github/workflows/Benchmarks.yml' - 'src/**' - 'ext/**' - - 'test/**' - 'benchmarks/**' - 'Project.toml' types: