Optimize statevector simulator preprocessing#316
Conversation
…g, OpenMP support Key changes: - Remove unroll() from Simulator.run() for QasmModule input (caller responsibility) - Single-pass _preprocess() with inline gate fusion (replaces separate _fuse_gates pass) - Native gate handling in _preprocess() without requiring full AST unrolling - AST expression evaluator for raw QASM parameter expressions (pi, tau, arithmetic) - Cache unroll() results in QasmModule (skip if already unrolled) - New Cython apply_circuit() entry point with 5 specialized kernels - Optional OpenMP parallelism via PYQASM_NUM_THREADS env var (default=1/serial) - OpenMP build detection in setup.py (macOS Homebrew libomp + Linux) - Benchmark suite comparing PyQASM vs Qiskit Aer, Cirq, PennyLane Lightning - Performance regression tests with pytest benchmark marker - Profiling script for detailed per-phase timing breakdown Performance (serial, median of 5): - Random circuits: PyQASM fastest at all sizes (4-22q), beating Qiskit/Cirq/Lightning - QFT circuits: PyQASM fastest at ≤12q; kernel-bound at ≥16q (memory bandwidth limited) - With PYQASM_NUM_THREADS=8: 4.1x speedup at 22q, 2.7x at 20q Known limitation: macOS libomp conflicts with Qiskit Aer's OpenMP in same process, so parallelism is opt-in. Future work: SIMD/AVX kernels for single-threaded perf. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Port the non-cache Run 06 AlphaEvolve statevector improvements into the sv branch implementation while deliberately excluding the QASM-string instruction memoization that overfit the AE repeated-timing harness. The simulator still preprocesses every input on each run call. The speedup comes from reducing Python overhead in that preprocessing path: - add optional numba-jitted helpers for small rotation/diagonal utilities - hoist matrix constants and phase constants to module scope - keep pending diagonal one-qubit gates as phase pairs during fusion - flush diagonal gates directly to the diagonal Cython kernel - preallocate packed instruction arrays instead of growing Python lists - decompose SWAP through the optimized controlled-X kernel path - preserve diagonal pending gates across CZ/CRZ because they commute - keep the conservative capacity bound needed for swap expansion and pending-gate flushes This is the honest non-cache improvement: no _PROGRAM_INSTRUCTION_CACHE, no cross-call memoization, and no reliance on repeated identical QASM strings. Add a simulation extra for installing numba; the code falls back to pure Python helpers when numba is absent.
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR introduces a complete statevector simulator for PyQASM with Cython/OpenMP-accelerated gate kernels, a high-level NumPy-based driver, correctness tests against Qiskit Aer, performance regression tests, and comprehensive benchmarking/profiling tools for cross-simulator comparison. ChangesStatevector Simulator
🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@benchmarks/bench_simulator.py`:
- Around line 344-348: The equality checks in the checks dict compare raw
statevectors (sv_pyqasm vs sv_qiskit, sv_cirq_le, sv_pl_le) and will fail when
candidates differ only by a global phase; normalize each candidate to
sv_pyqasm's global phase before calling np.allclose. For each candidate vector
(sv_qiskit, sv_cirq_le, sv_pl_le) compute a phase factor against sv_pyqasm
(e.g., using the normalized inner product np.vdot(sv_pyqasm,
sv_candidate)/abs(...), or by matching a nonzero component) and multiply/divide
the candidate by the conjugate of that phase so its global phase matches
sv_pyqasm, then use np.allclose on the aligned vectors when constructing the
checks dict.
In `@setup.py`:
- Around line 13-14: BASE_COMPILE_ARGS and BASE_LINK_ARGS are currently
hardcoded for GCC/Clang and include host-specific ISA flags; update setup.py to
make flags toolchain-aware by removing "-march=native" (and likely
"-ffast-math") from unconditional defaults and set compile/link flags based on
detected compiler type: inspect the build compiler via the distutils/setuptools
compiler instance or compiler.compiler_type and, for 'msvc', use
MSVC-appropriate flags (e.g. '/O2' and '/openmp' when OpenMP is available) and
for 'unix' use '-O3' and '-fopenmp' only when detected; change _check_openmp()
to probe OpenMP support using the project compiler (invoke
compiler.compile/compiler.link methods or use a temporary Extension building
attempt) rather than hardcoding cmd = ["cc", ...], return correct compile and
link flags to apply to extra_compile_args and extra_link_args for each Extension
(refer to BASE_COMPILE_ARGS, BASE_LINK_ARGS, extra_compile_args, and
_check_openmp in the diff).
In `@src/pyqasm/accelerate/sv_sim.pyx`:
- Around line 351-369: In apply_circuit, avoid dereferencing &array[0] on
possibly-empty Cython buffers; first check the lengths (e.g., ensure sv,
gate_params, diag_phases, two_qubit_gates have >0 elements or that
n_instructions>0 where appropriate) and only set sv_ptr, gp_ptr, dp_ptr, tq_ptr
to &array[0] when the buffer is non-empty, otherwise set the pointer to NULL (or
skip pointer usage) to prevent invalid memory access; update uses of
sv_ptr/gp_ptr/dp_ptr/tq_ptr later in the function to handle NULL/empty cases
safely.
- Around line 22-23: sv_sim.pyx currently cimports omp_get_max_threads and
omp_set_num_threads and calls omp_set_num_threads at module import time which
forces an OpenMP dependency even when setup.py disables it; modify the file to
guard all OpenMP cimports and calls behind a compile-time macro (e.g. `#IFDEF`
HAVE_OPENMP) driven by the same check used in setup.py so the Cython module
builds without OpenMP: wrap the cimports omp_get_max_threads/omp_set_num_threads
and any omp_* calls in conditional compilation blocks and provide safe no-op
fallbacks when the macro is not defined; additionally harden apply_circuit by
adding precondition checks (or early returns) for empty/mismatched memoryviews
(the function apply_circuit and its caller _preprocess/statevector code paths)
so it never takes &sv[0], &gate_params[0], &diag_phases[0], or
&two_qubit_gates[0] when the buffers are empty.
In `@tests/test_sv_sim.py`:
- Around line 309-314: The current comparison in tests/test_sv_sim.py only
normalizes global phase when the string "global phase" appears in description,
which is brittle; change the logic in the block that references description,
sv_expected, sv_actual, idx, and phase so that the global-phase normalization is
applied unconditionally (or replace it with a phase-invariant comparator helper)
by removing the conditional check on description and always computing idx =
np.argmax(np.abs(sv_expected) > 1e-10), phase = sv_actual[idx] /
sv_expected[idx], and sv_actual = sv_actual / phase before comparing vectors.
- Around line 44-45: Change the pytest.mark.parametrize argnames from a
comma-separated string to an explicit tuple so it satisfies PT006; locate the
pytest.mark.parametrize call in tests/test_sv_sim.py that currently uses "qasm,
description" and replace the argnames with a tuple form like ("qasm",
"description") (or equivalent single-quoted tuple) while keeping the existing
parameter values and test name intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: a06ded4b-859c-4557-9d06-5128c623731b
⛔ Files ignored due to path filters (2)
benchmarks/bench_qft.pngis excluded by!**/*.pngbenchmarks/bench_random.pngis excluded by!**/*.png
📒 Files selected for processing (12)
.gitignorebenchmarks/bench_simulator.pybenchmarks/plot_benchmarks.pybenchmarks/profile_simulator.pypyproject.tomlsetup.pysrc/pyqasm/accelerate/sv_sim.pyxsrc/pyqasm/modules/base.pysrc/pyqasm/simulator/__init__.pysrc/pyqasm/simulator/statevector.pytests/test_perf_regression.pytests/test_sv_sim.py
| checks = { | ||
| "qiskit": np.allclose(sv_pyqasm, sv_qiskit, atol=1e-10), | ||
| "cirq": np.allclose(sv_pyqasm, sv_cirq_le, atol=1e-10), | ||
| "lightning": np.allclose(sv_pyqasm, sv_pl_le, atol=1e-10), | ||
| } |
There was a problem hiding this comment.
Normalize global phase before statevector equality checks.
Line 345–Line 347 compare raw vectors directly; equivalent states with different global phase will be reported as FAIL. Align each candidate vector to the PyQASM reference phase before np.allclose.
🔧 Proposed fix
+def _align_global_phase(reference, candidate):
+ """Phase-align candidate statevector to reference."""
+ idx = int(np.argmax(np.abs(reference)))
+ if np.abs(reference[idx]) == 0 or np.abs(candidate[idx]) == 0:
+ return candidate
+ phase = np.angle(candidate[idx]) - np.angle(reference[idx])
+ return candidate * np.exp(-1j * phase)
+
@@
- checks = {
- "qiskit": np.allclose(sv_pyqasm, sv_qiskit, atol=1e-10),
- "cirq": np.allclose(sv_pyqasm, sv_cirq_le, atol=1e-10),
- "lightning": np.allclose(sv_pyqasm, sv_pl_le, atol=1e-10),
- }
+ sv_qiskit_aligned = _align_global_phase(sv_pyqasm, sv_qiskit)
+ sv_cirq_aligned = _align_global_phase(sv_pyqasm, sv_cirq_le)
+ sv_pl_aligned = _align_global_phase(sv_pyqasm, sv_pl_le)
+ checks = {
+ "qiskit": np.allclose(sv_pyqasm, sv_qiskit_aligned, atol=1e-10),
+ "cirq": np.allclose(sv_pyqasm, sv_cirq_aligned, atol=1e-10),
+ "lightning": np.allclose(sv_pyqasm, sv_pl_aligned, atol=1e-10),
+ }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@benchmarks/bench_simulator.py` around lines 344 - 348, The equality checks in
the checks dict compare raw statevectors (sv_pyqasm vs sv_qiskit, sv_cirq_le,
sv_pl_le) and will fail when candidates differ only by a global phase; normalize
each candidate to sv_pyqasm's global phase before calling np.allclose. For each
candidate vector (sv_qiskit, sv_cirq_le, sv_pl_le) compute a phase factor
against sv_pyqasm (e.g., using the normalized inner product np.vdot(sv_pyqasm,
sv_candidate)/abs(...), or by matching a nonzero component) and multiply/divide
the candidate by the conjugate of that phase so its global phase matches
sv_pyqasm, then use np.allclose on the aligned vectors when constructing the
checks dict.
| "qasm, description", | ||
| [ |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify current usage and confirm PT006-relevant callsite
rg -n -C2 '`@pytest`\.mark\.parametrize\(' tests/test_sv_sim.pyRepository: qBraid/pyqasm
Length of output: 131
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find ruff/flake8 config enabling PT006 (or general pytest style rules)
rg -n --hidden --no-ignore-vcs "PT006" -S . || true
rg -n --hidden --no-ignore-vcs "(pytest.*PT|PT006|ruff.*pytest|flake8.*pytest)" -S pyproject.toml ruff.toml setup.cfg tox.ini .flake8 . || true
# Confirm whether there are other parametrize callsites in this file
rg -n "pytest\.mark\.parametrize" tests/test_sv_sim.pyRepository: qBraid/pyqasm
Length of output: 340
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect ruff config around flake8-pytest-style and any PT006 selection/ignores
sed -n '1,120p' ruff.toml
echo "----"
rg -n "PT006|flake8-pytest-style|pytest-style" ruff.toml
echo "----"
# Show any pytest-style lint tables that might define enable/disable behavior
rg -n "flake8-pytest-style" -n ruff.toml && trueRepository: qBraid/pyqasm
Length of output: 2646
Switch pytest.mark.parametrize argnames to tuple form (PT006).
Ruff enables flake8-pytest-style via select = ["PT", ...] and does not ignore PT006, while tests/test_sv_sim.py currently uses a comma-separated string argnames ("qasm, description"), which trips the rule. Change to:
Suggested change
-@pytest.mark.parametrize(
- "qasm, description",
+@pytest.mark.parametrize(
+ ("qasm", "description"),🧰 Tools
🪛 Ruff (0.15.14)
[warning] 44-44: Wrong type passed to first argument of pytest.mark.parametrize; expected tuple
Use a tuple for the first argument
(PT006)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/test_sv_sim.py` around lines 44 - 45, Change the
pytest.mark.parametrize argnames from a comma-separated string to an explicit
tuple so it satisfies PT006; locate the pytest.mark.parametrize call in
tests/test_sv_sim.py that currently uses "qasm, description" and replace the
argnames with a tuple form like ("qasm", "description") (or equivalent
single-quoted tuple) while keeping the existing parameter values and test name
intact.
| if "global phase" in description: | ||
| # Compare up to global phase: find first nonzero element and normalize | ||
| idx = np.argmax(np.abs(sv_expected) > 1e-10) | ||
| phase = sv_actual[idx] / sv_expected[idx] | ||
| sv_actual = sv_actual / phase | ||
|
|
There was a problem hiding this comment.
Global-phase normalization should not depend on description text.
Conditioning on "global phase" in description is brittle and can miss valid equivalent states in other cases. Normalize phase unconditionally (or use a phase-invariant comparator helper).
Suggested change
- if "global phase" in description:
- # Compare up to global phase: find first nonzero element and normalize
- idx = np.argmax(np.abs(sv_expected) > 1e-10)
- phase = sv_actual[idx] / sv_expected[idx]
- sv_actual = sv_actual / phase
+ # Compare up to global phase for all cases
+ idx = np.argmax(np.abs(sv_expected) > 1e-10)
+ phase = sv_actual[idx] / sv_expected[idx]
+ sv_actual = sv_actual / phase🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@tests/test_sv_sim.py` around lines 309 - 314, The current comparison in
tests/test_sv_sim.py only normalizes global phase when the string "global phase"
appears in description, which is brittle; change the logic in the block that
references description, sv_expected, sv_actual, idx, and phase so that the
global-phase normalization is applied unconditionally (or replace it with a
phase-invariant comparator helper) by removing the conditional check on
description and always computing idx = np.argmax(np.abs(sv_expected) > 1e-10),
phase = sv_actual[idx] / sv_expected[idx], and sv_actual = sv_actual / phase
before comparing vectors.
Two independent CI failures are addressed: 1. Source-distribution job (6 test failures): remove the unroll() caching shortcut in QasmModule that early-returned when the module looked already unrolled. It broke re-unrolling (e.g. unroll(external_gates=...) followed by unroll() to flush them), failing the depth/rebase/include tests. This also contradicted the PR's stated "preprocess on every run" goal. 2. Wheel-build jobs (qiskit unbuildable in manylinux containers): qiskit / qiskit-aer have no prebuilt wheels for the cibuildwheel containers and fall back to a from-source build needing Rust. Move those reference-oracle deps into a dedicated `test-sim` extra so the wheel test env (test,cli,pulse) no longer pulls them, and guard the qiskit imports in test_sv_sim.py with pytest.importorskip so the module skips cleanly where qiskit is absent. The sdist and coverage jobs install `test-sim` to keep running the comparison on the standard runners where qiskit installs fine. Also exclude the hardware-dependent benchmark perf-regression tests from the CI test runs (-m "not benchmark"), per tests/test_perf_regression.py's own docs.
The build appended -O3 -ffast-math -march=native unconditionally to every
Extension, regardless of compiler or architecture. This is wrong for
distributed wheels:
* -march=native ties the wheel to the build machine's CPU and can SIGILL on
older hardware; it is also not the right spelling on MSVC or arm64.
* MSVC silently ignores the GCC/Clang flags, so Windows wheels got none of
the intended optimization.
* -ffast-math relaxes IEEE semantics, which the statevector simulator relies
on.
Replace the static flag list with a build_ext subclass that selects flags per
compiler: -O3 for GCC/Clang, /O2 for MSVC, and OpenMP (-fopenmp / /openmp)
applied only to the sv_sim kernel. No -march (baseline ISA, matching SciPy and
scikit-learn) and no -ffast-math.
The macOS wheel jobs failed in delocate: the sv_sim OpenMP build links Homebrew's libomp, whose dylib has a minimum-macOS target newer than the wheel's macosx_11_0 tag, so delocate refuses to bundle it. (Pre-existing in this PR's OpenMP-on-macOS approach.) Disable OpenMP on macOS by default so no libomp is linked or bundled and the wheels stay portable (sv_sim builds single-threaded on macOS only; Linux and Windows keep OpenMP). A local source build can opt back in with PYQASM_MACOS_OPENMP=1. Drop the now-unnecessary `brew install libomp` step from the wheel workflows.
Disabling the -fopenmp flag on macOS was not enough: sv_sim.pyx unconditionally cimports prange/openmp, so the generated C still pulled in an unguarded <omp.h> and failed to compile on macOS (no system libomp). Gate the OpenMP imports, thread setup, and every prange branch behind a Cython compile-time IF USE_OPENMP. The serial `with nogil` paths (which already existed alongside each prange) become the sole code path when OpenMP is off, and no <omp.h> is emitted. setup.py decides USE_OPENMP once (Linux/Windows on, macOS off unless PYQASM_MACOS_OPENMP=1) and passes it to cythonize via compile_time_env, matching the compiler flags applied in build_ext. Verified: Linux/Windows OpenMP build and the serial macOS build are bit-identical, and the threaded prange path matches the serial path.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
The simulator's crz fast path used a controlled-diagonal instruction that
can only phase the |control=1, target=1> amplitude, so it implemented a
controlled-phase gate diag(1,1,1,e^{i*theta/2}) instead of a true
controlled-Rz diag(1,1,e^{-i*theta/2},e^{i*theta/2}) -- it dropped the
phase on |control=1, target=0>. Route crz through the general
controlled-gate kernel with the full Rz matrix instead.
Add tests/test_sv_sim_core.py: a qiskit-free test module (the existing
simulator tests importorskip qiskit and are skipped in wheel-build
containers, and never exercised crz). It regression-tests crz via a
self-contained differential against pyqasm's own decomposition, plus
known-action gate checks, fusion paths, error handling, and helper unit
tests. Raises statevector.py coverage from 78% to 95%; the remainder is
dead/defensive code (notably the never-called generic two-qubit path).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up: dead generic two-qubit-gate scaffoldingWhile adding coverage/tests for the statevector simulator I noticed the generic two-qubit path is fully built but never reached, so flagging it here so we don't forget to either wire it up or remove it:
These lines are the bulk of the remaining uncovered code in Context: this is separate from the |
There was a problem hiding this comment.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/main.yml:
- Line 188: The codecov/codecov-action reference in the workflow is using a
version tag (v6.0.0) which is mutable and poses a supply-chain risk. Replace the
version tag with a full commit SHA in the uses statement for
codecov/codecov-action to pin it to an immutable commit, preventing the tag from
being rewritten to malicious code.
In `@bin/cibw/test_wheel.sh`:
- Line 36: The pytest path argument $project/tests in the pytest command is not
quoted, which can cause word splitting and globbing issues if the project path
contains spaces or special characters. Add double quotes around $project/tests
to ensure the path is treated as a single argument, changing it from python -m
pytest $project/tests -m "not benchmark" to properly quote the path variable.
In `@tests/test_sv_sim_core.py`:
- Around line 161-166: The test function `test_single_qubit_gate_action` is
missing a type annotation for its parameter `gate`. Add a type annotation of
`str` to the `gate` parameter since it receives string values from the
parametrize decorator. This will comply with the coding guideline that requires
all functions, methods, and class attributes to have type annotations.
- Around line 60-64: The _embed function is missing both a docstring and type
annotations as required by the coding guidelines. Add type annotations to the
function parameters (mat, target, num_qubits) and the return type to indicate
they are numpy arrays and integers respectively. Additionally, add a
comprehensive docstring at the beginning of the _embed function that explains
its purpose (embedding an operation on a target qubit into a larger Hilbert
space), describes each parameter, and specifies what the function returns.
- Around line 67-78: The function _embed_controlled is missing both type
annotations and a docstring as required by the coding guidelines. Add type
annotations to all parameters (mat, control, target, num_qubits) and to the
return type of the function. Additionally, add a docstring at the beginning of
the function that explains its purpose, describes each parameter with its
expected type and meaning, and describes what the function returns.
- Around line 101-106: The assert_sv_close function is missing type annotations
and has an incomplete docstring. Add type annotations for all parameters
(actual, expected, atol) and the return type to the function signature. Expand
the docstring to document what each parameter represents, what values they
should contain, what the atol parameter controls, and clarify that the function
returns None and performs assertion checks rather than returning a result.
- Around line 123-136: The function test_crz_fast_path_matches_decomposition is
missing type annotations on its parameters. Add type annotations to all function
parameters: theta should be annotated as float, and both control and target
should be annotated as int. This ensures compliance with the coding guideline
requiring all functions to have type annotations.
- Around line 109-112: The `run_sv` function is missing both a docstring and
type annotations as required by coding guidelines. Add type annotations to all
parameters (qasm and external_gates) and the return type to the function
signature. Add a docstring to the function that explains its purpose (what it
does with the qasm string and external gates), describes each parameter and its
expected type, and describes what the function returns (the final statevector).
- Around line 81-98: The reference_statevector function is missing type
annotations and has an incomplete docstring that does not follow Python
conventions. Add type annotations to the function signature for the ops
parameter (a list of tuples containing operation information), the num_qubits
parameter (an integer), and the return type (numpy ndarray). Replace the current
single-line docstring with a properly formatted docstring that includes separate
Args and Returns sections, clearly documenting what each parameter represents
and what the function returns.
- Around line 349-352: The loads_unrolled function is missing both a docstring
and type annotations as required by coding guidelines. Add type annotations for
the qasm parameter and the return type of the loads_unrolled function, and add a
comprehensive docstring that explains the function's purpose, describes the qasm
parameter, and documents the return value (which should be annotated as
QasmModule or the appropriate return type).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e9fd7557-16b1-4b89-be87-9ed5cfe2ff59
⛔ Files ignored due to path filters (4)
benchmarks/bench_qft.pngis excluded by!**/*.pngbenchmarks/bench_qft_evolved.pngis excluded by!**/*.pngbenchmarks/bench_random.pngis excluded by!**/*.pngbenchmarks/bench_random_evolved.pngis excluded by!**/*.png
📒 Files selected for processing (16)
.github/workflows/main.yml.github/workflows/pre-release.yml.github/workflows/release.yml.github/workflows/test-release.ymlCHANGELOG.mdbenchmarks/bench_evolved.jsonbin/cibw/test_wheel.shbin/test_sdist.shpyproject.tomlsetup.pysrc/pyqasm/accelerate/sv_sim.pyxsrc/pyqasm/simulator/__init__.pysrc/pyqasm/simulator/statevector.pytests/test_perf_regression.pytests/test_sv_sim.pytests/test_sv_sim_core.py
✅ Files skipped from review due to trivial changes (2)
- bin/test_sdist.sh
- CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (5)
- src/pyqasm/simulator/init.py
- tests/test_sv_sim.py
- src/pyqasm/simulator/statevector.py
- src/pyqasm/accelerate/sv_sim.pyx
- tests/test_perf_regression.py
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 10
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/main.yml:
- Line 188: The codecov/codecov-action reference in the workflow is using a
version tag (v6.0.0) which is mutable and poses a supply-chain risk. Replace the
version tag with a full commit SHA in the uses statement for
codecov/codecov-action to pin it to an immutable commit, preventing the tag from
being rewritten to malicious code.
In `@bin/cibw/test_wheel.sh`:
- Line 36: The pytest path argument $project/tests in the pytest command is not
quoted, which can cause word splitting and globbing issues if the project path
contains spaces or special characters. Add double quotes around $project/tests
to ensure the path is treated as a single argument, changing it from python -m
pytest $project/tests -m "not benchmark" to properly quote the path variable.
In `@tests/test_sv_sim_core.py`:
- Around line 161-166: The test function `test_single_qubit_gate_action` is
missing a type annotation for its parameter `gate`. Add a type annotation of
`str` to the `gate` parameter since it receives string values from the
parametrize decorator. This will comply with the coding guideline that requires
all functions, methods, and class attributes to have type annotations.
- Around line 60-64: The _embed function is missing both a docstring and type
annotations as required by the coding guidelines. Add type annotations to the
function parameters (mat, target, num_qubits) and the return type to indicate
they are numpy arrays and integers respectively. Additionally, add a
comprehensive docstring at the beginning of the _embed function that explains
its purpose (embedding an operation on a target qubit into a larger Hilbert
space), describes each parameter, and specifies what the function returns.
- Around line 67-78: The function _embed_controlled is missing both type
annotations and a docstring as required by the coding guidelines. Add type
annotations to all parameters (mat, control, target, num_qubits) and to the
return type of the function. Additionally, add a docstring at the beginning of
the function that explains its purpose, describes each parameter with its
expected type and meaning, and describes what the function returns.
- Around line 101-106: The assert_sv_close function is missing type annotations
and has an incomplete docstring. Add type annotations for all parameters
(actual, expected, atol) and the return type to the function signature. Expand
the docstring to document what each parameter represents, what values they
should contain, what the atol parameter controls, and clarify that the function
returns None and performs assertion checks rather than returning a result.
- Around line 123-136: The function test_crz_fast_path_matches_decomposition is
missing type annotations on its parameters. Add type annotations to all function
parameters: theta should be annotated as float, and both control and target
should be annotated as int. This ensures compliance with the coding guideline
requiring all functions to have type annotations.
- Around line 109-112: The `run_sv` function is missing both a docstring and
type annotations as required by coding guidelines. Add type annotations to all
parameters (qasm and external_gates) and the return type to the function
signature. Add a docstring to the function that explains its purpose (what it
does with the qasm string and external gates), describes each parameter and its
expected type, and describes what the function returns (the final statevector).
- Around line 81-98: The reference_statevector function is missing type
annotations and has an incomplete docstring that does not follow Python
conventions. Add type annotations to the function signature for the ops
parameter (a list of tuples containing operation information), the num_qubits
parameter (an integer), and the return type (numpy ndarray). Replace the current
single-line docstring with a properly formatted docstring that includes separate
Args and Returns sections, clearly documenting what each parameter represents
and what the function returns.
- Around line 349-352: The loads_unrolled function is missing both a docstring
and type annotations as required by coding guidelines. Add type annotations for
the qasm parameter and the return type of the loads_unrolled function, and add a
comprehensive docstring that explains the function's purpose, describes the qasm
parameter, and documents the return value (which should be annotated as
QasmModule or the appropriate return type).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: e9fd7557-16b1-4b89-be87-9ed5cfe2ff59
⛔ Files ignored due to path filters (4)
benchmarks/bench_qft.pngis excluded by!**/*.pngbenchmarks/bench_qft_evolved.pngis excluded by!**/*.pngbenchmarks/bench_random.pngis excluded by!**/*.pngbenchmarks/bench_random_evolved.pngis excluded by!**/*.png
📒 Files selected for processing (16)
.github/workflows/main.yml.github/workflows/pre-release.yml.github/workflows/release.yml.github/workflows/test-release.ymlCHANGELOG.mdbenchmarks/bench_evolved.jsonbin/cibw/test_wheel.shbin/test_sdist.shpyproject.tomlsetup.pysrc/pyqasm/accelerate/sv_sim.pyxsrc/pyqasm/simulator/__init__.pysrc/pyqasm/simulator/statevector.pytests/test_perf_regression.pytests/test_sv_sim.pytests/test_sv_sim_core.py
✅ Files skipped from review due to trivial changes (2)
- bin/test_sdist.sh
- CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (5)
- src/pyqasm/simulator/init.py
- tests/test_sv_sim.py
- src/pyqasm/simulator/statevector.py
- src/pyqasm/accelerate/sv_sim.pyx
- tests/test_perf_regression.py
🛑 Comments failed to post (10)
.github/workflows/main.yml (1)
188-188:
⚠️ Potential issue | 🟠 Major🧩 Analysis chain
🏁 Script executed:
#!/bin/bash # Verify unpinned GitHub Actions references in workflows. rg -nP '^\s*uses:\s*\S+@(?![0-9a-fA-F]{40}\b)\S+' .github/workflowsRepository: qBraid/pyqasm
Length of output: 3426
🌐 Web query:
codecov-action v6.0.0 commit SHA GitHub💡 Result:
The codecov-action version v6.0.0 is associated with the commit SHA 57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 [1][2]. This release was merged into the main branch on March 26, 2026 [1][2]. You can reference this specific version in your GitHub Actions workflow using the following syntax: - uses: codecov/codecov-action@57e3a13 This version introduced support for Node.js 24, which may cause breaking changes for systems that do not yet support it [3][4]. It is recommended to pin your actions to specific commit SHAs for better security and stability [2].
Citations:
- 1: codecov/codecov-action@57e3a13
- 2: codecov/codecov-action#1928
- 3: https://github.com/codecov/codecov-action/releases/tag/v6.0.0
- 4: https://newreleases.io/project/github/codecov/codecov-action/release/v6.0.0
Pin GitHub Action to a full commit SHA.
codecov/codecov-action@v6.0.0is not immutable. Pinning to a commit SHA reduces supply-chain risk.Suggested hardening change
- uses: codecov/codecov-action@v6.0.0 + uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2🧰 Tools
🪛 zizmor (1.25.2)
[error] 188-188: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)
(unpinned-uses)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/main.yml at line 188, The codecov/codecov-action reference in the workflow is using a version tag (v6.0.0) which is mutable and poses a supply-chain risk. Replace the version tag with a full commit SHA in the uses statement for codecov/codecov-action to pin it to an immutable commit, preventing the tag from being rewritten to malicious code.Source: Linters/SAST tools
bin/cibw/test_wheel.sh (1)
36-36:
⚠️ Potential issue | 🟡 Minor🧩 Analysis chain
🏁 Script executed:
#!/bin/bash # Verify unquoted path usage in this script. if command -v shellcheck >/dev/null 2>&1; then shellcheck -f gcc bin/cibw/test_wheel.sh else rg -n '\$project/tests' bin/cibw/test_wheel.sh fiRepository: qBraid/pyqasm
Length of output: 241
Quote the pytest path argument.
Use quoting to avoid word splitting/globbing on
$project/tests.Suggested fix
-python -m pytest $project/tests -m "not benchmark" +python -m pytest "$project/tests" -m "not benchmark"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.python -m pytest "$project/tests" -m "not benchmark"🧰 Tools
🪛 Shellcheck (0.11.0)
[info] 36-36: Double quote to prevent globbing and word splitting.
(SC2086)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@bin/cibw/test_wheel.sh` at line 36, The pytest path argument $project/tests in the pytest command is not quoted, which can cause word splitting and globbing issues if the project path contains spaces or special characters. Add double quotes around $project/tests to ensure the path is treated as a single argument, changing it from python -m pytest $project/tests -m "not benchmark" to properly quote the path variable.Source: Linters/SAST tools
tests/test_sv_sim_core.py (8)
60-64: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add docstring and type annotations.
This function is missing both a docstring and type annotations, as required by the coding guidelines. As per coding guidelines, "Every module, class, method, and function must have a docstring explaining its purpose, parameters, and return values" and "All functions, methods, and class attributes must have type annotations."
📝 Proposed fix
-def _embed(mat, target, num_qubits): +def _embed(mat: np.ndarray, target: int, num_qubits: int) -> np.ndarray: + """ + Embed a single-qubit gate into a multi-qubit operator. + + Args: + mat: The 2x2 single-qubit gate matrix. + target: The target qubit index. + num_qubits: Total number of qubits. + + Returns: + The full (2^num_qubits x 2^num_qubits) operator matrix. + """ op = np.array([[1]], dtype=complex)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.def _embed(mat: np.ndarray, target: int, num_qubits: int) -> np.ndarray: """ Embed a single-qubit gate into a multi-qubit operator. Args: mat: The 2x2 single-qubit gate matrix. target: The target qubit index. num_qubits: Total number of qubits. Returns: The full (2^num_qubits x 2^num_qubits) operator matrix. """ op = np.array([[1]], dtype=complex) for qubit in range(num_qubits): op = np.kron(mat if qubit == target else _I, op) return op🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sv_sim_core.py` around lines 60 - 64, The _embed function is missing both a docstring and type annotations as required by the coding guidelines. Add type annotations to the function parameters (mat, target, num_qubits) and the return type to indicate they are numpy arrays and integers respectively. Additionally, add a comprehensive docstring at the beginning of the _embed function that explains its purpose (embedding an operation on a target qubit into a larger Hilbert space), describes each parameter, and specifies what the function returns.Source: Coding guidelines
67-78: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add docstring and type annotations.
This function is missing both a docstring and type annotations, as required by the coding guidelines. As per coding guidelines, "Every module, class, method, and function must have a docstring explaining its purpose, parameters, and return values" and "All functions, methods, and class attributes must have type annotations."
📝 Proposed fix
-def _embed_controlled(mat, control, target, num_qubits): +def _embed_controlled(mat: np.ndarray, control: int, target: int, num_qubits: int) -> np.ndarray: + """ + Embed a controlled single-qubit gate into a multi-qubit operator. + + Args: + mat: The 2x2 single-qubit gate matrix to apply when control is |1>. + control: The control qubit index. + target: The target qubit index. + num_qubits: Total number of qubits. + + Returns: + The full (2^num_qubits x 2^num_qubits) controlled operator matrix. + """ p0 = np.array([[1, 0], [0, 0]], dtype=complex)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.def _embed_controlled(mat: np.ndarray, control: int, target: int, num_qubits: int) -> np.ndarray: """ Embed a controlled single-qubit gate into a multi-qubit operator. Args: mat: The 2x2 single-qubit gate matrix to apply when control is |1>. control: The control qubit index. target: The target qubit index. num_qubits: Total number of qubits. Returns: The full (2^num_qubits x 2^num_qubits) controlled operator matrix. """ p0 = np.array([[1, 0], [0, 0]], dtype=complex) p1 = np.array([[0, 0], [0, 1]], dtype=complex) op0 = op1 = np.array([[1]], dtype=complex) for qubit in range(num_qubits): if qubit == control: op0, op1 = np.kron(p0, op0), np.kron(p1, op1) elif qubit == target: op0, op1 = np.kron(_I, op0), np.kron(mat, op1) else: op0, op1 = np.kron(_I, op0), np.kron(_I, op1) return op0 + op1🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sv_sim_core.py` around lines 67 - 78, The function _embed_controlled is missing both type annotations and a docstring as required by the coding guidelines. Add type annotations to all parameters (mat, control, target, num_qubits) and to the return type of the function. Additionally, add a docstring at the beginning of the function that explains its purpose, describes each parameter with its expected type and meaning, and describes what the function returns.Source: Coding guidelines
81-98: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Improve docstring and add type annotations.
The docstring does not follow Python docstring conventions with proper Args/Returns sections, and type annotations are missing. As per coding guidelines, "Every module, class, method, and function must have a docstring explaining its purpose, parameters, and return values" and "All functions, methods, and class attributes must have type annotations."
📝 Proposed fix
-def reference_statevector(ops, num_qubits): - """ops: list of (name, [qubits], [params]). Returns the final statevector.""" +def reference_statevector(ops: list[tuple[str, list[int], list[float]]], num_qubits: int) -> np.ndarray: + """ + Compute the final statevector for a sequence of gate operations. + + Args: + ops: List of (gate_name, qubit_indices, parameters) tuples. + num_qubits: Total number of qubits in the circuit. + + Returns: + The final statevector as a complex numpy array of shape (2^num_qubits,). + + Raises: + ValueError: If an unsupported gate name is encountered. + """ sv = np.zeros(2**num_qubits, dtype=complex)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sv_sim_core.py` around lines 81 - 98, The reference_statevector function is missing type annotations and has an incomplete docstring that does not follow Python conventions. Add type annotations to the function signature for the ops parameter (a list of tuples containing operation information), the num_qubits parameter (an integer), and the return type (numpy ndarray). Replace the current single-line docstring with a properly formatted docstring that includes separate Args and Returns sections, clearly documenting what each parameter represents and what the function returns.Source: Coding guidelines
101-106: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add type annotations and improve docstring.
Type annotations are missing, and the docstring could be more detailed. As per coding guidelines, "All functions, methods, and class attributes must have type annotations" and docstrings should explain parameters and return values.
📝 Proposed fix
-def assert_sv_close(actual, expected, atol=1e-9): - """Compare statevectors up to an irrelevant global phase.""" +def assert_sv_close(actual: np.ndarray, expected: np.ndarray, atol: float = 1e-9) -> None: + """ + Assert that two statevectors are equal up to a global phase. + + Args: + actual: The computed statevector. + expected: The reference statevector. + atol: Absolute tolerance for the comparison. + + Raises: + AssertionError: If the statevectors differ by more than a global phase or atol. + """ idx = int(np.argmax(np.abs(expected)))🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sv_sim_core.py` around lines 101 - 106, The assert_sv_close function is missing type annotations and has an incomplete docstring. Add type annotations for all parameters (actual, expected, atol) and the return type to the function signature. Expand the docstring to document what each parameter represents, what values they should contain, what the atol parameter controls, and clarify that the function returns None and performs assertion checks rather than returning a result.Source: Coding guidelines
109-112: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add docstring and type annotations.
This helper function is missing both a docstring and type annotations, as required by the coding guidelines. As per coding guidelines, "Every module, class, method, and function must have a docstring explaining its purpose, parameters, and return values" and "All functions, methods, and class attributes must have type annotations."
📝 Proposed fix
-def run_sv(qasm, external_gates=None): +def run_sv(qasm: str, external_gates: list[str] | None = None) -> np.ndarray: + """ + Run the simulator on a QASM string and return the final statevector. + + Args: + qasm: The OpenQASM 3 program string. + external_gates: Optional list of gate names to treat as external (not unrolled). + + Returns: + The final statevector as a complex numpy array. + """ module = loads(qasm)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sv_sim_core.py` around lines 109 - 112, The `run_sv` function is missing both a docstring and type annotations as required by coding guidelines. Add type annotations to all parameters (qasm and external_gates) and the return type to the function signature. Add a docstring to the function that explains its purpose (what it does with the qasm string and external gates), describes each parameter and its expected type, and describes what the function returns (the final statevector).Source: Coding guidelines
123-136: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add type annotations to test parameters.
The parametrized test function parameters lack type annotations. As per coding guidelines, "All functions, methods, and class attributes must have type annotations."
📝 Proposed fix
`@pytest.mark.parametrize`("theta", [0.0, 0.7, np.pi / 2, np.pi, -1.3, 2 * np.pi]) `@pytest.mark.parametrize`("control, target", [(0, 1), (1, 0)]) -def test_crz_fast_path_matches_decomposition(theta, control, target): +def test_crz_fast_path_matches_decomposition(theta: float, control: int, target: int) -> None: qasm = (📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.`@pytest.mark.parametrize`("theta", [0.0, 0.7, np.pi / 2, np.pi, -1.3, 2 * np.pi]) `@pytest.mark.parametrize`("control, target", [(0, 1), (1, 0)]) def test_crz_fast_path_matches_decomposition(theta: float, control: int, target: int) -> None: qasm = ( "OPENQASM 3;\n" 'include "stdgates.inc";\n' "qubit[2] q;\n" "h q[0];\n" "h q[1];\n" f"crz({theta}) q[{control}], q[{target}];\n" ) fast = run_sv(qasm, external_gates=["crz"]) # hits the crz fast path decomposed = run_sv(qasm) # full rz/rx/cx decomposition (oracle) assert_sv_close(fast, decomposed)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sv_sim_core.py` around lines 123 - 136, The function test_crz_fast_path_matches_decomposition is missing type annotations on its parameters. Add type annotations to all function parameters: theta should be annotated as float, and both control and target should be annotated as int. This ensures compliance with the coding guideline requiring all functions to have type annotations.Source: Coding guidelines
161-166: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add type annotation to test parameter.
The parametrized test function parameter lacks a type annotation. As per coding guidelines, "All functions, methods, and class attributes must have type annotations."
📝 Proposed fix
`@pytest.mark.parametrize`("gate", ["x", "y", "z", "h", "s", "sdg", "t", "tdg", "id"]) -def test_single_qubit_gate_action(gate): +def test_single_qubit_gate_action(gate: str) -> None: qasm = f'OPENQASM 3;\ninclude "stdgates.inc";\nqubit[1] q;\nx q[0];\n{gate} q[0];\n'🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sv_sim_core.py` around lines 161 - 166, The test function `test_single_qubit_gate_action` is missing a type annotation for its parameter `gate`. Add a type annotation of `str` to the `gate` parameter since it receives string values from the parametrize decorator. This will comply with the coding guideline that requires all functions, methods, and class attributes to have type annotations.Source: Coding guidelines
349-352: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Add docstring and type annotations.
This helper function is missing both a docstring and type annotations, as required by the coding guidelines. As per coding guidelines, "Every module, class, method, and function must have a docstring explaining its purpose, parameters, and return values" and "All functions, methods, and class attributes must have type annotations."
📝 Proposed fix
-def loads_unrolled(qasm): +def loads_unrolled(qasm: str): + """ + Load and unroll a QASM program string. + + Args: + qasm: The OpenQASM 3 program string. + + Returns: + The unrolled QasmModule. + """ module = loads(qasm)Note: You may also want to add a return type annotation (likely
QasmModulefrom pyqasm).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@tests/test_sv_sim_core.py` around lines 349 - 352, The loads_unrolled function is missing both a docstring and type annotations as required by coding guidelines. Add type annotations for the qasm parameter and the return type of the loads_unrolled function, and add a comprehensive docstring that explains the function's purpose, describes the qasm parameter, and documents the return value (which should be annotated as QasmModule or the appropriate return type).Source: Coding guidelines
Summary
This PR targets
mainfrom the currentsvbranch work plus one additional commit that ports the honest, non-cache AlphaEvolve Run 06 statevector improvements.It deliberately does not include the evolved
_PROGRAM_INSTRUCTION_CACHE/ repeated-QASM memoization path, since that overfit the AlphaEvolve repeated-timing harness. The simulator still preprocesses each input on everySimulator.run(...)call.Included from
svsv_simkernelsAdditional evolved changes
simulation = ["numba>=0.59"]optional extra; fallback shim keeps numba optionalBenchmark artifacts updated
The existing default benchmark plots were regenerated from this PR branch with the improved simulator:
benchmarks/bench_random.pngbenchmarks/bench_qft.pngThe benchmarks folder also includes explicit no-cache before/after comparison artifacts:
benchmarks/bench_evolved_no_cache.jsonbenchmarks/bench_random_evolved_no_cache.pngbenchmarks/bench_qft_evolved_no_cache.pngThe comparison artifacts use pre-unrolled
QasmModuleinputs and intentionally do not include the repeated-QASM cache path.Validation
Performed locally against the installed
svbranch kernels from the AlphaEvolve workspace:QasmModulepathshots > 0smoke testatol=1e-10Regenerated default benchmark plots with:
No-cache comparison artifacts (
PYQASM_NUM_THREADS=1, median of 5):atol=1e-10AlphaEvolve full overlay results from the experiment showed the graph-compatible non-cache path at ~1.23x random and ~1.36x QFT geomean; this PR's committed benchmark artifacts were regenerated from the PR branch implementation.
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Tests
Chores