Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
ceec96f
perf: port the paper-aligned layer-build engine + operator sharding r…
diagonal-hamiltonian Jul 18, 2026
7b7bb3d
test(cpp): update C++ unit tests for the sharded layer-build engine
diagonal-hamiltonian Jul 18, 2026
ced7c3c
test(python): adapt white-box and upper_atol tests to the sharded engine
diagonal-hamiltonian Jul 18, 2026
d0d50ea
refactor(threading): ♻️ rewrite pool-dispatch sites as plain serial l…
diagonal-hamiltonian Jul 19, 2026
a40b71c
test(cpp): ♻️ drop the thread-pool dimension from the upper-atol resc…
diagonal-hamiltonian Jul 19, 2026
b454338
refactor(threading)!: 🔥 delete the in-repo thread pool; shards are th…
diagonal-hamiltonian Jul 19, 2026
d366cdb
refactor(profiling): 🔥 make the region profiler wall-clock only
diagonal-hamiltonian Jul 19, 2026
e9cf968
refactor(shard): ♻️ wall Linux topology/pinning behind the CpuTopolog…
diagonal-hamiltonian Jul 19, 2026
2ac06fa
chore: 🔥 drop stale oneTBB references
diagonal-hamiltonian Jul 19, 2026
dea2122
docs: 📝 document the shard-only parallelism model
diagonal-hamiltonian Jul 19, 2026
1210588
feat(benches): 📈 sweepable pauli chain topology + per-model stats
diagonal-hamiltonian Jul 19, 2026
b4cafad
fix(benches): 🐛 skip operator storage accounting on sharded propagators
diagonal-hamiltonian Jul 19, 2026
90af5da
feat(operator): 📊 operator memory/popcount diagnostics + split inline…
diagonal-hamiltonian Jul 19, 2026
31f94dd
merge: 🔀 bench pauli-chain harness + operator diagnostics into pauli-…
diagonal-hamiltonian Jul 19, 2026
e6afdd3
feat(pauli)!: ⚡ store PauliPropagator in the native local symplectic …
diagonal-hamiltonian Jul 19, 2026
22bd3ff
merge: 🔀 JW-free native-Pauli PauliPropagator into align
diagonal-hamiltonian Jul 19, 2026
e93b4b9
perf(evolution)!: 🔥 drop persistent fold-cache, always recompute cos
diagonal-hamiltonian Jul 20, 2026
769db75
Merge branch 'main' into perf/align-with-paper
matteoacrossi Jul 20, 2026
eca8e90
simplifications
diagonal-hamiltonian Jul 20, 2026
31c6c42
fixed benches
diagonal-hamiltonian Jul 20, 2026
e46a5f5
simp
diagonal-hamiltonian Jul 20, 2026
3260a38
perf(scan): ⚡ zero-postings fold early-out + lazy sparse-pivot expansion
diagonal-hamiltonian Jul 21, 2026
a192598
test(cpp): 🧹 extract shared test helpers, merge single-case files
diagonal-hamiltonian Jul 21, 2026
d587c9c
refactor(validation)!: 🔥 delete dead validators and EvolutionMode
diagonal-hamiltonian Jul 21, 2026
a369891
test(cpp): ✅ unit-test previously-uncovered data structures
diagonal-hamiltonian Jul 21, 2026
3c540dc
build,ci: 🔧 wire the wide-term-index (64-bit) build
diagonal-hamiltonian Jul 21, 2026
db8dcd2
docs(cpp): list the new unit-test files in the suite README
diagonal-hamiltonian Jul 21, 2026
cc2e08f
refactor(core)!: ♻️ recast the C++ core as a generic monomial/algebra…
diagonal-hamiltonian Jul 21, 2026
c8256c6
Merge branch 'main' into perf/align-with-paper
matteoacrossi Jul 21, 2026
bc03263
Merge branch 'main' into perf/align-with-paper
SimoneGasperini Jul 22, 2026
d1011b5
test(cpp): ✅ unit-test the graph-encoding packing/layout functions
diagonal-hamiltonian Jul 21, 2026
18a68e6
test(cpp): ✅ white-box MPGraph transform + MPOperator branch coverage
diagonal-hamiltonian Jul 21, 2026
12563c0
test(cpp): ✅ multi-rank Schrödinger fresh-insert resolve equivalence
diagonal-hamiltonian Jul 21, 2026
a9c353e
refactor: 🔥 delete verified-dead symbols (Tier 1 + Tier 2)
diagonal-hamiltonian Jul 21, 2026
30cc1af
lint
diagonal-hamiltonian Jul 22, 2026
9381d7f
fix(cmake): 🐛 recreate Threads imported target for downstream consumers
diagonal-hamiltonian Jul 22, 2026
88dcaa4
test(cpp): 🐛 make allreduce_sum_inplace check reduce identical stored…
diagonal-hamiltonian Jul 22, 2026
0390856
style: 🎨 formatter-clean branch files and add missing license headers
diagonal-hamiltonian Jul 22, 2026
8b8ae09
chore: 🔀 merge main into perf/align-with-paper
Copilot Jul 22, 2026
a0a5886
docs: 📝 update evolved_operator examples for the PauliOperator API
diagonal-hamiltonian Jul 22, 2026
e6c7e5e
refactor: ♻️ address safe SonarQube code smells
diagonal-hamiltonian Jul 22, 2026
a8dc05d
chore: 🔀 merge main into perf/align-with-paper
diagonal-hamiltonian Jul 22, 2026
016aab5
sharding tests
diagonal-hamiltonian Jul 22, 2026
1bb2002
fix macos sharding test
diagonal-hamiltonian Jul 22, 2026
ecba31c
annihilation: comments
diagonal-hamiltonian Jul 22, 2026
48f646b
Merge branch 'main' into perf/align-with-paper
ludmilaasb Jul 23, 2026
96fecaf
docs
diagonal-hamiltonian Jul 23, 2026
47fe393
refactor(operator): ♻️ simplify operator storage to the single-writer…
diagonal-hamiltonian Jul 23, 2026
8af2097
small fixes
diagonal-hamiltonian Jul 23, 2026
2671389
Merge branch 'refactor/simplify-operator-objects' into perf/align-wit…
diagonal-hamiltonian Jul 23, 2026
5fd5053
docs
diagonal-hamiltonian Jul 23, 2026
fe2ec4c
chore: update benchmark scripts and plots
Jul 24, 2026
acf4302
Merge branch 'perf/align-with-paper' of https://github.com/Algorithmi…
Jul 24, 2026
58f5ea9
chore: update benchmark documentation
Jul 24, 2026
8280fd1
chore: revert changes in benchmark script and documentation, moving t…
ludmilaasb Jul 24, 2026
0c6c3fb
chore: update the benchmark scripts and documentation
ludmilaasb Jul 24, 2026
8b2603b
docs
diagonal-hamiltonian Jul 24, 2026
1894f08
Merge branch 'perf/align-with-paper' into chore/update-benchmarks
ludmilaasb Jul 24, 2026
a043390
refact: major refactoring of script to more sensible memory accounting
Jul 24, 2026
9d3fe1f
refactor(evolution): ♻️ unify cross-rank resolve twins behind a sink …
diagonal-hamiltonian Jul 23, 2026
7f68a65
chore: 🔥 drop stale wide-TermIndex preset, threads comment, and bench…
diagonal-hamiltonian Jul 24, 2026
eb396d6
refactor(graph): ♻️ simplify the graph object, removing dead code and…
diagonal-hamiltonian Jul 24, 2026
8fa6e6a
refactor(evolution): ♻️ fold self-resolve + finalize into the sink po…
diagonal-hamiltonian Jul 24, 2026
45e1aff
simplifications
diagonal-hamiltonian Jul 24, 2026
129a867
refactor(graph): ♻️ harden the lazy derivative exchange layout
diagonal-hamiltonian Jul 24, 2026
ea64000
refact: updated how results.json is created
Jul 24, 2026
89010b6
chore: update benchmark figures
Jul 24, 2026
a867068
Merge branch 'perf/align-with-paper' into chore/update-benchmarks
ludmilaasb Jul 24, 2026
456b997
cleaning: remove some bits
diagonal-hamiltonian Jul 26, 2026
31c3704
feat(operator): 📈 add per-field operator memory diagnostics
diagonal-hamiltonian Jul 26, 2026
339c133
perf(operator): 🗜️ store the Heisenberg reference state sparsely
diagonal-hamiltonian Jul 26, 2026
aa0ddb6
docs(comments): 🔥 drop narration of superseded simulator versions
diagonal-hamiltonian Jul 26, 2026
7d10826
refactor(operator): 🔥 drop the delta-codec oracle diagnostics
diagonal-hamiltonian Jul 26, 2026
8571c35
merge: 🗜️ sparse Heisenberg reference state (L1)
diagonal-hamiltonian Jul 26, 2026
bc6865e
merge: origin/perf/align-with-paper
diagonal-hamiltonian Jul 26, 2026
d361d6b
fix(cmake): 🐛 actually enable MPI in the ctest matrix
diagonal-hamiltonian Jul 26, 2026
26a072d
fix(qiskit)!: 🐛 negate the generator across the qiskit boundary
diagonal-hamiltonian Jul 26, 2026
d54a7fa
fix(circuit): 🐛 close four front-end validation gaps
diagonal-hamiltonian Jul 26, 2026
75fc23b
fix(engine): 🐛 bounds-check indices and validate the cutoff config in…
diagonal-hamiltonian Jul 26, 2026
481b1ef
fix(evolution): 🐛 stop retaining a pointer into the mutable row-parit…
diagonal-hamiltonian Jul 26, 2026
d286d56
fix(mpi): 🐛 own the posted request, guard the count arithmetic, join …
diagonal-hamiltonian Jul 26, 2026
cc264c6
fix(mpi): 🐛 abort instead of hanging when a rank cannot reach a colle…
diagonal-hamiltonian Jul 26, 2026
043afb5
fix(graph): 🐛 report the real cosine-index count in graph_size
diagonal-hamiltonian Jul 26, 2026
927bfcc
refactor(algebra): ♻️ put the slot bound on the cutoff and de-duplica…
diagonal-hamiltonian Jul 26, 2026
b1c1cbf
test(mpi): 🐛 take serial_comm for the rank-local assertions
diagonal-hamiltonian Jul 26, 2026
2da5cb7
refactor(pare): ♻️ delete the no-op cross-rank keep-set exchange
diagonal-hamiltonian Jul 26, 2026
6c91972
refactor(notation)!: ♻️ name the initial state and monomials algebra-…
diagonal-hamiltonian Jul 27, 2026
4954507
perf(eval)!: 🗜️ materialize the dense reference state only for the gr…
diagonal-hamiltonian Jul 27, 2026
7ee4872
docs(comments): 📝 make comments state what the code cannot
diagonal-hamiltonian Jul 27, 2026
859ac37
style(cpp): 🎨 clang-format three files the last two commits left unfo…
diagonal-hamiltonian Jul 27, 2026
a4fa97c
Merge branch 'perf/align-with-paper' into chore/update-benchmarks
ludmilaasb Jul 27, 2026
724f365
Merge branch 'main' into chore/update-benchmarks
ludmilaasb Jul 28, 2026
4640053
fix: fixing merging errors
ludmilaasb Jul 28, 2026
d932f7b
lint: fix liting
ludmilaasb Jul 28, 2026
13d4550
Merge branch 'main' into chore/update-benchmarks
ludmilaasb Jul 29, 2026
fb16dee
Merge branch 'main' into chore/update-benchmarks
ludmilaasb Jul 29, 2026
463476a
Merge branch 'main' into chore/update-benchmarks
robertodr Jul 29, 2026
19aa477
Update benches/third_party/majorana_prop/monoprop_hubbard1d_benchmark.py
ludmilaasb Jul 30, 2026
c8cd105
Merge branch 'main' into chore/update-benchmarks
robertodr Jul 31, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions benches/third_party/bench_common.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright 2026 Algorithmiq
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Shared across the third-party Julia benchmark scripts in this project (pauli_prop,
# majorana_prop). Included via `include(joinpath(@__DIR__, "..", "bench_common.jl"))`.

if Threads.nthreads() == 1
@warn "Julia is running with only 1 thread: the peak-memory sampler runs as a background " *
"task, but a single-threaded Julia can only switch to it between steps, not while the " *
"propagation call itself is running, so the memory figures will undercount any " *
"transient spike freed before that call returns. Re-run with `julia --threads=auto` " *
"(or set JULIA_NUM_THREADS) for accurate peak-memory measurements."
end

# Linux-only: reads the kernel's live RSS for this process directly, in kB.
function current_rss_bytes()::Int
for line in eachline("/proc/self/status")
if startswith(line, "VmRSS:")
return parse(Int, split(line)[2]) * 1024
end
end
return 0
end

# Tracks this process's peak RSS within a resettable window: the kernel's own high-water mark
# (Sys.maxrss()) is monotonic for the whole process and can't be reset per step, so this instead
# polls the *current* RSS from a background task and keeps the max seen since the last reset!.
mutable struct RssPeakSampler
peak_bytes::Threads.Atomic{Int}
running::Threads.Atomic{Bool}
end

function start_sampler(interval_s::Float64=1e-3)
sampler = RssPeakSampler(Threads.Atomic{Int}(current_rss_bytes()), Threads.Atomic{Bool}(true))
Threads.@spawn begin
while sampler.running[]
rss = current_rss_bytes()
if rss > sampler.peak_bytes[]
sampler.peak_bytes[] = rss
end
sleep(interval_s)
end
end
return sampler
end

reset!(sampler::RssPeakSampler) = (sampler.peak_bytes[] = current_rss_bytes())
peak_mb(sampler::RssPeakSampler) = sampler.peak_bytes[] / 1024^2
stop!(sampler::RssPeakSampler) = (sampler.running[] = false)
65 changes: 65 additions & 0 deletions benches/third_party/bench_common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2026 Algorithmiq
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Shared across the third-party benchmark scripts in this project (pauli_prop, majorana_prop)."""

from __future__ import annotations

import threading

import psutil


class RssPeakSampler:
"""Tracks this process's peak resident set size within a resettable window.

The kernel-tracked high-water mark (``resource.getrusage().ru_maxrss``) is monotonic for the
whole process lifetime and can never be reset, so it cannot isolate a single step's peak from
the steps around it. This instead polls the *current* RSS from a background thread at a high
fixed frequency and keeps the max seen since the last :meth:`reset`, so each step gets its own
peak, independent of what earlier or later steps did.

Caveat: this can only sample while the calling thread doesn't hold the GIL — a C extension
call that never releases it (e.g. monoprop's ``propagate()``) blocks this thread out for its
whole duration, so the sampler only catches whatever RSS growth is already visible by the time
control returns to Python.
"""

def __init__(self, interval_s: float = 1e-3) -> None:
self._process = psutil.Process()
self._interval_s = interval_s
self._peak_bytes = 0
self._stop_event = threading.Event()
self._thread = threading.Thread(target=self._poll_loop, daemon=True)

def _poll_loop(self) -> None:
while not self._stop_event.wait(self._interval_s):
rss = self._process.memory_info().rss
if rss > self._peak_bytes:
self._peak_bytes = rss

def __enter__(self) -> RssPeakSampler:
self._thread.start()
return self

def __exit__(self, *exc_info: object) -> None:
self._stop_event.set()
self._thread.join()

def reset(self) -> None:
"""Start a new measurement window, floored at the current RSS."""
self._peak_bytes = self._process.memory_info().rss

def peak_mb(self) -> float:
return self._peak_bytes / 1024**2
111 changes: 71 additions & 40 deletions benches/third_party/majorana_prop/julia_hubbard1d_benchmark.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ using BenchmarkTools
using ArgParse
using JSON

include(joinpath(@__DIR__, "..", "bench_common.jl"))


function experiment(N_spinful_sites, fock_state, circ_single, thetas_single, n_layers)
site_index = N_spinful_sites ÷ 2
Expand All @@ -26,33 +28,64 @@ function experiment(N_spinful_sites, fock_state, circ_single, thetas_single, n_l

obs = VectorMajoranaSum(MajoranaSum(N_spinful_sites, :nup, site_index))

res = zeros(n_layers + 1)
res[1] = overlapwithfock(obs, fock_state)
values = zeros(n_layers + 1)
term_counts = zeros(Int, n_layers + 1)
cumulative_runtimes = zeros(n_layers + 1)
memory_size = zeros(n_layers + 1)
native_memory_size = zeros(n_layers + 1)

sampler = start_sampler()

reset!(sampler)
cumulative_runtimes[1] = @elapsed (values[1] = overlapwithfock(obs, fock_state))
term_counts[1] = length(obs)
memory_size[1] = peak_mb(sampler)
native_memory_size[1] = Base.summarysize(obs) / 1024^2
for k = 1:n_layers
reset!(sampler)
step_runtime = @elapsed propagate!(circ_single, obs, thetas_single, min_abs_coeff=min_abs_coeff, max_unpaired=max_unpaired)
values[k+1] = overlapwithfock(obs, fock_state)
term_counts[k+1] = length(obs)
cumulative_runtimes[k+1] = cumulative_runtimes[k] + step_runtime
memory_size[k+1] = peak_mb(sampler)
native_memory_size[k+1] = Base.summarysize(obs) / 1024^2
end

stop!(sampler)

loop_elapsed = @elapsed for k = 1:n_layers
propagate!(circ_single, obs, thetas_single, min_abs_coeff=min_abs_coeff, max_unpaired=max_unpaired)
res[k+1] = overlapwithfock(obs, fock_state)
end
memory_size = Base.summarysize(obs) / 1024^2
return res, length(obs), loop_elapsed, memory_size
return values, term_counts, cumulative_runtimes, memory_size, native_memory_size


end
function save_result(output_path, N_spinful_sites, n_layers, obs_length, final_res, loop_elapsed, memory_size)
"""Append one benchmark result as a JSON line, creating the parent directory if needed."""
record = Dict(
"n_spinful_sites" => N_spinful_sites,
"n_layers" => n_layers,
"num_terms" => obs_length,
"final_overlap" => final_res,
"runtime_seconds" => loop_elapsed,
"memory_MB" => memory_size,
)

open(output_path, "a") do io
JSON.print(io, record)
println(io)
function save_result(output_path, source, N_spinful_sites, n_layers, term_counts, values, cumulative_runtimes, memory_size, native_memory_size, num_threads)
"""Merge this run's per-step data into the shared results JSON file, keyed by source label."""
data = if isfile(output_path)
JSON.parsefile(output_path)
else
Dict(
"n_spinful_sites" => N_spinful_sites,
"n_layers" => n_layers,
"step_range" => collect(0:n_layers),
"num_threads" => Dict(),
"runtime_seconds" => Dict(),
"expectation_value" => Dict(),
"num_terms" => Dict(),
"memory_MB" => Dict(),
"native_memory_MB" => Dict(),
)
end
data["num_threads"] = get(data, "num_threads", Dict())
data["num_threads"][source] = num_threads
data["runtime_seconds"][source] = cumulative_runtimes
data["expectation_value"][source] = values
data["num_terms"][source] = term_counts
data["memory_MB"][source] = memory_size
data["native_memory_MB"] = get(data, "native_memory_MB", Dict())
data["native_memory_MB"][source] = native_memory_size

mkpath(dirname(output_path))
open(output_path, "w") do io
JSON.print(io, data, 4)
end
end

Expand All @@ -61,28 +94,27 @@ function main(args)
s = ArgParseSettings(description="Arguments for the 1D Hubbard model benchmark.")

@add_arg_table! s begin
"--case", "-c"
help = "Case pair to run."
"--n-spins", "-n"
help = "Number of spinful sites."
arg_type = Int
default = 1
default = 60
dest_name = "n_spins"
"--max-layers", "-l"
help = "Number of Trotter layers."
arg_type = Int
default = 20
dest_name = "max_layers"
"--output", "-o"
help = "Path to the JSONL file results are appended to."
help = "Path to the shared JSON file results are merged into."
arg_type = String
default = joinpath(@__DIR__, "julia_hubbard1d_benchmark_results.jsonl")
default = joinpath(@__DIR__, "results.json")

end

parsed_args = parse_args(s)

spin_layers_pairs = []
for i in [20, 40, 60]
for j in 10:2:18
push!(spin_layers_pairs, (i, j))
end
end

case_pair = parsed_args["case"]
N_spinful_sites, n_layers = spin_layers_pairs[case_pair]
N_spinful_sites = parsed_args["n_spins"]
n_layers = parsed_args["max_layers"]

t = 1.
U = 1.5
Expand Down Expand Up @@ -117,11 +149,10 @@ function main(args)

println("Number of threads: $(Threads.nthreads())")

res, obs_length, loop_elapsed, memory_size = experiment(N_spinful_sites, fock_state, circ_single, thetas_single, n_layers)
final_res = res[end]
println("$N_spinful_sites n_spin $n_layers layers $obs_length num_terms $final_res final overlap $loop_elapsed seconds")
values, term_counts, cumulative_runtimes, memory_size, native_memory_size = experiment(N_spinful_sites, fock_state, circ_single, thetas_single, n_layers)
println("$N_spinful_sites n_spin $n_layers layers $(term_counts[end]) num_terms $(values[end]) final overlap $(cumulative_runtimes[end]) seconds")

save_result(parsed_args["output"], N_spinful_sites, n_layers, obs_length, final_res, loop_elapsed, memory_size)
save_result(parsed_args["output"], "MajoranaPropagation.jl", N_spinful_sites, n_layers, term_counts, values, cumulative_runtimes, memory_size, native_memory_size, Threads.nthreads())

end

Expand Down

This file was deleted.

Loading
Loading