Add dense/recursive NEGF switch, audit performance, parallelize with rayon - #2
Merged
reneotten merged 1 commit intoJul 25, 2026
Conversation
Explains and implements both NEGF algorithms behind a runtime switch
(GreenFunctionAlgorithm::{Recursive, Dense} in Rust, algorithm=
"recursive"|"dense" in Python), rather than only having the O(N)
recursive path:
- Dense: literal O(N^3) full-matrix inversion, matching the original
MATLAB inv() call. Simple to trust (no tridiagonal-structure
assumption, no recursion formula to get wrong), so it now doubles as
the reference the recursive path is validated against, and as a
fallback if the Hamiltonian ever gains longer-range hopping.
- Recursive (default): the existing O(N) left-connected recursive
Green's-function sweep. The one to use for anything performance-
sensitive.
Audited performance and parallelized where the structure actually
allows it:
- The NEGF energy loop is embarrassingly parallel (each energy point
only reads the same fixed Hamiltonian) -- parallelized with rayon,
benefiting both algorithms.
- Bias sweeps (sweep_v_g/sweep_v_ds) are parallel per bias point, since
set_v_g/set_v_ds already reset device state from scratch. This
changes their signature from &mut Device to &Device (each point runs
on its own clone), which also fixes a surprising side effect where a
sweep silently left the device parked at its last bias point.
- Self-consistent loop iterations remain sequential (each depends on
the previous iteration's potential) -- documented as the one thing
that can't be parallelized here.
Added crates/negforge-core/examples/benchmark.rs with wall-clock
numbers (dense vs. recursive scaling, parallel vs. serial speedup),
quoted in the README's new "Performance" section. Added tests cross-
validating both algorithms against each other (single sweep and
through the full self-consistent loop) and confirming sweeps no longer
mutate the input device. Notebook updated with a live dense-vs-
recursive comparison cell.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QVNo3bky8MhMCth48hZT4m
There was a problem hiding this comment.
Pull request overview
This PR adds a selectable NEGF Green’s-function backend (dense reference vs. recursive fast path) and introduces Rayon-based parallelism over independent energy points and bias-sweep points, with corresponding updates to the Rust core, PyO3 bindings, Python convenience wrapper, benchmark tooling, and documentation/notebook examples.
Changes:
- Introduces
GreenFunctionAlgorithm::{Recursive, Dense}and wires it through NEGF sweeps and the self-consistent solver (Rust + Python API surface). - Parallelizes NEGF energy-point evaluation and bias sweeps using
rayon, and makes Rust sweep APIs non-mutating (&Device). - Adds a benchmark example and expands README/notebook documentation with performance guidance and usage examples.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
README.md |
Documents the dense/recursive switch, parallelism approach, and benchmark numbers. |
python/negforge/__init__.py |
Exposes algorithm= in the Python wrapper for self-consistent solves, LDOS, and sweeps. |
notebooks/negforge_demo.ipynb |
Demonstrates dense vs. recursive behavior and timing in the notebook flow. |
crates/negforge-py/src/lib.rs |
Implements Python-facing parsing/validation of algorithm and updates bindings to new Rust APIs. |
crates/negforge-core/src/sweep.rs |
Makes sweeps non-mutating and parallel per bias point via cloning + Rayon. |
crates/negforge-core/src/selfconsistent.rs |
Threads the algorithm selection into the self-consistent loop via options. |
crates/negforge-core/src/negf.rs |
Adds GreenFunctionAlgorithm, dense backend, and parallel energy loop execution. |
crates/negforge-core/src/lib.rs |
Re-exports GreenFunctionAlgorithm publicly. |
crates/negforge-core/examples/benchmark.rs |
Adds a benchmark harness for algorithm and parallelism comparisons. |
crates/negforge-core/Cargo.toml |
Adds rayon dependency for parallel execution. |
Cargo.lock |
Locks new transitive dependencies introduced by rayon. |
Comments suppressed due to low confidence (1)
crates/negforge-py/src/lib.rs:206
- Same issue as
sweep_v_g:algorithmis parsed even whenself_consistent=false, so invalid strings raiseValueErrordespite being documented as irrelevant unless self-consistency is enabled. Only parse/validate whenself_consistentis true.
let opts = SelfConsistentOptions {
algorithm: parse_algorithm(algorithm)?,
..Default::default()
};
let sc = if self_consistent { Some(&opts) } else { None };
let points = negforge_core::sweep::sweep_v_ds(&self.inner, v_min, v_max, step, sc)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
5 tasks
reneotten
pushed a commit
that referenced
this pull request
Jul 25, 2026
Follow-up to the review comments on #1 and #2. - tridiag: `solve_real`/`solve_complex` no longer panic on degenerate sizes — `n == 0` now asserts with a clear message and `n == 1` is handled as a scalar division instead of indexing empty off-diagonals. Also removed a dead bounds check in the forward sweep. `Device` asserts up front that the grid spacing yields at least two points, so a coarse `a` fails with an explanation rather than an index panic. - device: `set_v_ds`/`set_v_g`/`set_l_ch` (and `Device::new`) now re-solve the electrostatic potential, so `psi_f`/`psi_0` can never be left over from the previous bias point. Callers no longer have to remember an explicit `calc_potential()` before `calc_current()` or the NEGF entry points. - sweep: `inclusive_range` truncates instead of rounding the step count, so a span that isn't a whole multiple of the step stops inside the range instead of overshooting past `v_max`; ranges within floating-point noise of a whole multiple (0.4/0.05) still keep their endpoint. Invalid ranges (non-positive/non-finite step, reversed bounds) are now errors. - sweep/python: `subthreshold_swing` validates that the currents in the fit window are strictly positive and finite before taking `log10`, instead of silently fitting `-inf`/`nan`, and rejects a degenerate (flat-current) fit. - python bindings: engine `InvalidParameter` errors now surface as `ValueError` rather than `RuntimeError`, matching the `algorithm` validation. Documented why `algorithm` is validated even when `self_consistent=False` (catching a typo'd name is the point). cargo test --workspace: 24 tests pass; cargo clippy --workspace --all-targets clean; Python API smoke-tested against a maturin build. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pf4rT4UcjJtZgA7Meiq3Tc
reneotten
merged commit Jul 25, 2026
4ae294f
into
claude/rust-rewrite-python-frontend-a70ids
2 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds on #1 (not yet merged, hence that branch as the base here).
GreenFunctionAlgorithm::{Recursive, Dense}in Rust,algorithm="recursive"|"dense"in Python) so both are available and validated against each other — dense as an easy-to-trust reference / fallback, recursive as the fast default.crates/negforge-core/examples/benchmark.rsand used it to find and exploit the real parallelism in this problem:rayon, benefiting both algorithms.sweep_v_g/sweep_v_ds) are parallel per bias point, sinceset_v_g/set_v_dsalready reset device state from scratch — nothing carries over between points. This changes their signature from&mut Deviceto&Device(each point runs on its own clone), which also fixes a surprising side effect where a sweep used to silently leave the device parked at its last bias point.Test plan
cargo test --workspace— 19 tests pass, including new cross-checks:negf::tests::dense_and_recursive_algorithms_agree_end_to_end,selfconsistent::tests::dense_and_recursive_converge_to_the_same_potential,sweep::tests::sweep_does_not_mutate_the_input_devicecargo clippy --workspace --all-targets— cleancargo run --release --example benchmark -p negforge-core— produces the numbers quoted in the READMEmaturin develop --releaserebuilds; verifiedalgorithm="dense"/"recursive"give matching results at different speeds, and an invalid algorithm string raisesValueErrornotebooks/negforge_demo.ipynbre-executed end-to-end with the new section included, no errorsGenerated by Claude Code