diff --git a/Cargo.lock b/Cargo.lock index 98a4ff5..2ab9514 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,6 +20,12 @@ dependencies = [ "cc", ] +[[package]] +name = "allocation-counter" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb9e990c0a33699f1984d85a6abead615ccc72dd8130bf3e15dcabe2ca149c9" + [[package]] name = "anes" version = "0.1.6" @@ -91,7 +97,7 @@ checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -628,9 +634,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.104" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" dependencies = [ "cfg-if", "futures-util", @@ -641,6 +647,7 @@ dependencies = [ name = "la-stack" version = "0.4.5" dependencies = [ + "allocation-counter", "approx", "criterion", "faer", @@ -1204,7 +1211,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.4", + "syn 3.0.5", ] [[package]] @@ -1268,9 +1275,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" dependencies = [ "proc-macro2", "quote", @@ -1388,9 +1395,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" dependencies = [ "cfg-if", "once_cell", @@ -1401,9 +1408,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1411,31 +1418,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.119", + "syn 3.0.5", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.127" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" dependencies = [ "unicode-ident", ] [[package]] name = "web-sys" -version = "0.3.104" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" +checksum = "9fbddc4a036f00ec4f18c83445bd3115cb306a91da554919a099d9222fe4a7f8" dependencies = [ "js-sys", "wasm-bindgen", diff --git a/Cargo.toml b/Cargo.toml index 25a6a0d..c1adb68 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ num-rational = { version = "0.4.2", features = [ "num-bigint-std" ], optional = num-traits = { version = "0.2.19", optional = true } [dev-dependencies] +allocation-counter = "0.8.1" approx = "0.5.1" criterion = { version = "0.8.2", features = [ "html_reports" ] } faer = { version = "0.24.4", default-features = false, features = [ "std", "linalg" ] } diff --git a/benches/common/rational.rs b/benches/common/rational.rs new file mode 100644 index 0000000..e8597d0 --- /dev/null +++ b/benches/common/rational.rs @@ -0,0 +1,223 @@ +#![forbid(unsafe_code)] + +//! Independently validated rational inputs shared by timing and allocation probes. + +use la_stack::{BigInt, BigRational, DeterminantSign, RationalMatrix, RationalVector}; + +use super::bench_utils::OrAbort; + +/// Fixed rational component-size families. +#[derive(Clone, Copy, Debug)] +pub enum RationalInputKind { + /// Original small-integer components from the rational-input benchmark. + Small, + /// Row factors with at least 256-bit numerator/denominator inputs. + Wide256, + /// Row factors with at least 1024-bit numerator/denominator inputs. + Wide1024, +} + +impl RationalInputKind { + /// Stable fixture families, in registration order. + pub const ALL: [Self; 3] = [Self::Small, Self::Wide256, Self::Wide1024]; + + /// Stable family name used in benchmark and allocation output. + #[must_use] + pub const fn name(self) -> &'static str { + match self { + Self::Small => "small", + Self::Wide256 => "wide256", + Self::Wide1024 => "wide1024", + } + } + + const fn component_bits(self) -> Option { + match self { + Self::Small => None, + Self::Wide256 => Some(256), + Self::Wide1024 => Some(1024), + } + } +} + +/// Independently validated exact-input rational benchmark fixture. +#[must_use] +pub struct ValidatedRationalInput { + matrix: RationalMatrix, + rhs: RationalVector, +} + +impl ValidatedRationalInput { + /// Borrow the independently checked matrix. + pub const fn matrix(&self) -> &RationalMatrix { + &self.matrix + } + + /// Borrow the independently checked right-hand side. + pub const fn rhs(&self) -> &RationalVector { + &self.rhs + } +} + +/// Deterministic, strictly diagonally-dominant exact-input rational fixture. +/// +/// # Panics +/// Panics if construction or the independent determinant/solve checks fail. +pub fn rational_input(kind: RationalInputKind) -> ValidatedRationalInput { + let mut rows = std::array::from_fn(|row| { + std::array::from_fn(|col| { + if row == col { + let diagonal = 2 * D + row + 1; + BigRational::from_integer(BigInt::from(diagonal)) + } else { + let raw_numerator = (row * 3 + col * 5) % 5; + let numerator = i64::try_from(raw_numerator).or_abort("rational numerator") - 2; + let denominator = (row + col) % 7 + 2; + BigRational::new(BigInt::from(numerator), BigInt::from(denominator)) + } + }) + }); + if let Some(bits) = kind.component_bits() { + for (row, entries) in rows.iter_mut().enumerate() { + let shift = bits + 16 * row; + let factor = BigRational::new( + (BigInt::from(1) << shift) + BigInt::from(1), + (BigInt::from(1) << (shift + 1)) - BigInt::from(1), + ); + for entry in entries { + *entry *= &factor; + } + } + } + let expected_solution = std::array::from_fn(|index| { + BigRational::new(BigInt::from(index + 1), BigInt::from(index + 2)) + }); + let rhs_data = rational_matvec(&rows, &expected_solution); + let matrix = RationalMatrix::try_from_rows(rows.clone()) + .or_abort("rational benchmark matrix construction"); + let rhs = + RationalVector::try_new(rhs_data.clone()).or_abort("rational benchmark RHS construction"); + + let reference_determinant = rational_determinant_gaussian(rows.clone()); + assert_eq!(matrix.det(), reference_determinant); + assert_eq!(matrix.det_sign(), determinant_sign(&reference_determinant)); + let reference_solution = rational_solve_gaussian(rows, rhs_data) + .or_abort("rational Gaussian benchmark validation solve"); + assert_eq!(reference_solution, expected_solution); + assert_eq!( + matrix + .solve(&rhs) + .or_abort("row-cleared Bareiss benchmark validation solve") + .into_array(), + expected_solution + ); + + ValidatedRationalInput { matrix, rhs } +} + +/// Exact rational matrix-vector multiplication used only to assemble and +/// validate benchmark fixtures. +fn rational_matvec( + rows: &[[BigRational; D]; D], + vector: &[BigRational; D], +) -> [BigRational; D] { + std::array::from_fn(|row| { + rows[row] + .iter() + .zip(vector.iter()) + .map(|(coefficient, component)| coefficient * component) + .sum() + }) +} + +/// Straightforward cubic rational Gaussian determinant reference. +#[must_use] +pub fn rational_determinant_gaussian( + mut rows: [[BigRational; D]; D], +) -> BigRational { + let zero = BigRational::from_integer(BigInt::from(0)); + let mut determinant = BigRational::from_integer(BigInt::from(1)); + let mut odd_swaps = false; + + for pivot_col in 0..D { + let Some(pivot_row) = (pivot_col..D).find(|&row| rows[row][pivot_col] != zero) else { + return zero; + }; + if pivot_row != pivot_col { + rows.swap(pivot_col, pivot_row); + odd_swaps = !odd_swaps; + } + + let (pivot_rows, rows_below) = rows.split_at_mut(pivot_col + 1); + let pivot_entries = &pivot_rows[pivot_col]; + let pivot = &pivot_entries[pivot_col]; + determinant *= pivot; + for row_entries in rows_below { + let factor = &row_entries[pivot_col] / pivot; + for (entry, pivot_entry) in row_entries + .iter_mut() + .zip(pivot_entries.iter()) + .skip(pivot_col + 1) + { + *entry -= &factor * pivot_entry; + } + row_entries[pivot_col] = zero.clone(); + } + } + + if odd_swaps { -determinant } else { determinant } +} + +/// Straightforward cubic rational Gaussian solve reference. +#[must_use] +pub fn rational_solve_gaussian( + mut rows: [[BigRational; D]; D], + mut rhs: [BigRational; D], +) -> Option<[BigRational; D]> { + let zero = BigRational::from_integer(BigInt::from(0)); + for pivot_col in 0..D { + let pivot_row = (pivot_col..D).find(|&row| rows[row][pivot_col] != zero)?; + if pivot_row != pivot_col { + rows.swap(pivot_col, pivot_row); + rhs.swap(pivot_col, pivot_row); + } + + let (pivot_rows, rows_below) = rows.split_at_mut(pivot_col + 1); + let pivot_entries = &pivot_rows[pivot_col]; + let pivot = &pivot_entries[pivot_col]; + let (pivot_rhs_entries, rhs_below) = rhs.split_at_mut(pivot_col + 1); + let pivot_rhs = &pivot_rhs_entries[pivot_col]; + for (row_entries, rhs_entry) in rows_below.iter_mut().zip(rhs_below) { + let factor = &row_entries[pivot_col] / pivot; + for (entry, pivot_entry) in row_entries + .iter_mut() + .zip(pivot_entries.iter()) + .skip(pivot_col + 1) + { + *entry -= &factor * pivot_entry; + } + let rhs_update = &factor * pivot_rhs; + *rhs_entry -= rhs_update; + row_entries[pivot_col] = zero.clone(); + } + } + + let mut solution = std::array::from_fn(|_| zero.clone()); + for row in (0..D).rev() { + let mut value = rhs[row].clone(); + for (coefficient, component) in rows[row].iter().zip(solution.iter()).skip(row + 1) { + value -= coefficient * component; + } + solution[row] = value / &rows[row][row]; + } + Some(solution) +} + +fn determinant_sign(value: &BigRational) -> DeterminantSign { + let zero = BigRational::from_integer(BigInt::from(0)); + match value.cmp(&zero) { + std::cmp::Ordering::Less => DeterminantSign::Negative, + std::cmp::Ordering::Equal => DeterminantSign::Zero, + std::cmp::Ordering::Greater => DeterminantSign::Positive, + } +} diff --git a/benches/exact.rs b/benches/exact.rs index 2bc2335..22aaaf7 100644 --- a/benches/exact.rs +++ b/benches/exact.rs @@ -37,8 +37,6 @@ use std::hint::black_box; use criterion::BatchSize; use criterion::{BenchmarkGroup, Criterion, Throughput, measurement::WallTime}; -#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] -use la_stack::{BigInt, BigRational, DeterminantSign, RationalMatrix, RationalVector}; use la_stack::{Matrix, Vector}; #[path = "common/bench_utils.rs"] @@ -46,6 +44,15 @@ mod bench_utils; #[path = "common/exact.rs"] pub mod exact_bench; +#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] +#[path = "common/rational.rs"] +pub mod rational_bench; + +#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] +use rational_bench::{ + RationalInputKind, rational_determinant_gaussian, rational_input, rational_solve_gaussian, +}; + use bench_utils::OrAbort; use exact_bench::{ ExactInput, RANDOM_INPUT_ARRAY_LEN, ValidatedExactInput, hilbert_input, @@ -98,188 +105,35 @@ const CORPUS_AND_EXTREME_OPERATIONS: &[ExactOperation] = &[ ExactOperation::SolveExactRoundedF64, ]; -/// Independently validated exact-input rational benchmark fixture. -#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] -#[must_use] -struct ValidatedRationalInput { - matrix: RationalMatrix, - rhs: RationalVector, -} - -/// Deterministic, strictly diagonally-dominant exact-input rational fixture. -#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] -fn rational_input() -> ValidatedRationalInput { - let rows = std::array::from_fn(|row| { - std::array::from_fn(|col| { - if row == col { - let diagonal = 2 * D + row + 1; - BigRational::from_integer(BigInt::from(diagonal)) - } else { - let raw_numerator = (row * 3 + col * 5) % 5; - let numerator = i64::try_from(raw_numerator).or_abort("rational numerator") - 2; - let denominator = (row + col) % 7 + 2; - BigRational::new(BigInt::from(numerator), BigInt::from(denominator)) - } - }) - }); - let expected_solution = std::array::from_fn(|index| { - BigRational::new(BigInt::from(index + 1), BigInt::from(index + 2)) - }); - let rhs_data = rational_matvec(&rows, &expected_solution); - let matrix = RationalMatrix::try_from_rows(rows.clone()) - .or_abort("rational benchmark matrix construction"); - let rhs = - RationalVector::try_new(rhs_data.clone()).or_abort("rational benchmark RHS construction"); - - let reference_determinant = rational_determinant_gaussian(rows.clone()); - assert_eq!(matrix.det(), reference_determinant); - assert_eq!(matrix.det_sign(), determinant_sign(&reference_determinant)); - let reference_solution = rational_solve_gaussian(rows, rhs_data) - .or_abort("rational Gaussian benchmark validation solve"); - assert_eq!(reference_solution, expected_solution); - assert_eq!( - matrix - .solve(&rhs) - .or_abort("row-cleared Bareiss benchmark validation solve") - .into_array(), - expected_solution - ); - - ValidatedRationalInput { matrix, rhs } -} - -/// Exact rational matrix-vector multiplication used only to assemble and -/// validate benchmark fixtures. -#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] -fn rational_matvec( - rows: &[[BigRational; D]; D], - vector: &[BigRational; D], -) -> [BigRational; D] { - std::array::from_fn(|row| { - rows[row] - .iter() - .zip(vector.iter()) - .map(|(coefficient, component)| coefficient * component) - .sum() - }) -} - -/// Straightforward cubic rational Gaussian determinant reference. -#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] -fn rational_determinant_gaussian(mut rows: [[BigRational; D]; D]) -> BigRational { - let zero = BigRational::from_integer(BigInt::from(0)); - let mut determinant = BigRational::from_integer(BigInt::from(1)); - let mut odd_swaps = false; - - for pivot_col in 0..D { - let Some(pivot_row) = (pivot_col..D).find(|&row| rows[row][pivot_col] != zero) else { - return zero; - }; - if pivot_row != pivot_col { - rows.swap(pivot_col, pivot_row); - odd_swaps = !odd_swaps; - } - - let (pivot_rows, rows_below) = rows.split_at_mut(pivot_col + 1); - let pivot_entries = &pivot_rows[pivot_col]; - let pivot = &pivot_entries[pivot_col]; - determinant *= pivot; - for row_entries in rows_below { - let factor = &row_entries[pivot_col] / pivot; - for (entry, pivot_entry) in row_entries - .iter_mut() - .zip(pivot_entries.iter()) - .skip(pivot_col + 1) - { - *entry -= &factor * pivot_entry; - } - row_entries[pivot_col] = zero.clone(); - } - } - - if odd_swaps { -determinant } else { determinant } -} - -/// Straightforward cubic rational Gaussian solve reference. -#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] -fn rational_solve_gaussian( - mut rows: [[BigRational; D]; D], - mut rhs: [BigRational; D], -) -> Option<[BigRational; D]> { - let zero = BigRational::from_integer(BigInt::from(0)); - for pivot_col in 0..D { - let pivot_row = (pivot_col..D).find(|&row| rows[row][pivot_col] != zero)?; - if pivot_row != pivot_col { - rows.swap(pivot_col, pivot_row); - rhs.swap(pivot_col, pivot_row); - } - - let (pivot_rows, rows_below) = rows.split_at_mut(pivot_col + 1); - let pivot_entries = &pivot_rows[pivot_col]; - let pivot = &pivot_entries[pivot_col]; - let (pivot_rhs_entries, rhs_below) = rhs.split_at_mut(pivot_col + 1); - let pivot_rhs = &pivot_rhs_entries[pivot_col]; - for (row_entries, rhs_entry) in rows_below.iter_mut().zip(rhs_below) { - let factor = &row_entries[pivot_col] / pivot; - for (entry, pivot_entry) in row_entries - .iter_mut() - .zip(pivot_entries.iter()) - .skip(pivot_col + 1) - { - *entry -= &factor * pivot_entry; - } - let rhs_update = &factor * pivot_rhs; - *rhs_entry -= rhs_update; - row_entries[pivot_col] = zero.clone(); - } - } - - let mut solution = std::array::from_fn(|_| zero.clone()); - for row in (0..D).rev() { - let mut value = rhs[row].clone(); - for (coefficient, component) in rows[row].iter().zip(solution.iter()).skip(row + 1) { - value -= coefficient * component; - } - solution[row] = value / &rows[row][row]; - } - Some(solution) -} - -#[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] -fn determinant_sign(value: &BigRational) -> DeterminantSign { - let zero = BigRational::from_integer(BigInt::from(0)); - match value.cmp(&zero) { - std::cmp::Ordering::Less => DeterminantSign::Negative, - std::cmp::Ordering::Equal => DeterminantSign::Zero, - std::cmp::Ordering::Greater => DeterminantSign::Positive, - } -} - /// Compare exact-input row clearing and Bareiss elimination with direct /// `BigRational` Gaussian elimination. /// /// The consuming Gaussian references clone their inputs in Criterion's untimed /// setup phase, so both sides estimate computation over already-accepted input. #[cfg(not(any(la_stack_pre_rational_input_api, la_stack_v0_4_3_api)))] -fn bench_rational_input(criterion: &mut Criterion) { - let input = rational_input::(); - let mut group = criterion.benchmark_group(format!("rational_input_d{D}")); +fn bench_rational_input(criterion: &mut Criterion, kind: RationalInputKind) { + let input = rational_input::(kind); + let group_name = match kind { + RationalInputKind::Small => format!("rational_input_d{D}"), + _ => format!("rational_input_{}_d{D}", kind.name()), + }; + let mut group = criterion.benchmark_group(group_name); group.bench_function("det_sign_row_cleared_bareiss", |bencher| { bencher.iter(|| { - let sign = black_box(&input.matrix).det_sign(); + let sign = black_box(input.matrix()).det_sign(); let _ = black_box(sign); }); }); group.bench_function("det_row_cleared_bareiss", |bencher| { bencher.iter(|| { - let determinant = black_box(&input.matrix).det(); + let determinant = black_box(input.matrix()).det(); black_box(determinant); }); }); group.bench_function("det_big_rational_gaussian", |bencher| { bencher.iter_batched( - || black_box(input.matrix.as_rows()).clone(), + || black_box(input.matrix().as_rows()).clone(), |rows| { let determinant = rational_determinant_gaussian(rows); black_box(determinant); @@ -289,8 +143,8 @@ fn bench_rational_input(criterion: &mut Criterion) { }); group.bench_function("solve_row_cleared_bareiss", |bencher| { bencher.iter(|| { - let solution = black_box(&input.matrix) - .solve(black_box(&input.rhs)) + let solution = black_box(input.matrix()) + .solve(black_box(input.rhs())) .or_abort("row-cleared Bareiss rational benchmark solve"); let _ = black_box(solution); }); @@ -299,8 +153,8 @@ fn bench_rational_input(criterion: &mut Criterion) { bencher.iter_batched( || { ( - black_box(input.matrix.as_rows()).clone(), - black_box(input.rhs.as_array()).clone(), + black_box(input.matrix().as_rows()).clone(), + black_box(input.rhs().as_array()).clone(), ) }, |(rows, rhs)| { @@ -557,13 +411,15 @@ fn main() { // These compare the production row-cleared integer Bareiss backend with // straightforward BigRational Gaussian elimination on dimensions needed // by downstream geometric predicates and runtime-selected basis systems. - bench_rational_input::<2>(&mut c); - bench_rational_input::<3>(&mut c); - bench_rational_input::<4>(&mut c); - bench_rational_input::<5>(&mut c); - bench_rational_input::<6>(&mut c); - bench_rational_input::<7>(&mut c); - bench_rational_input::<8>(&mut c); + for kind in RationalInputKind::ALL { + bench_rational_input::<2>(&mut c, kind); + bench_rational_input::<3>(&mut c, kind); + bench_rational_input::<4>(&mut c, kind); + bench_rational_input::<5>(&mut c, kind); + bench_rational_input::<6>(&mut c, kind); + bench_rational_input::<7>(&mut c, kind); + bench_rational_input::<8>(&mut c, kind); + } } // === Adversarial / extreme-input groups === diff --git a/docs/BENCHMARKING.md b/docs/BENCHMARKING.md index e80e586..69708de 100644 --- a/docs/BENCHMARKING.md +++ b/docs/BENCHMARKING.md @@ -525,8 +525,13 @@ random-corpus groups, and adversarial-input groups: calls using row-denominator clearing plus integer Bareiss elimination with straightforward cubic `BigRational` Gaussian determinant and solve references on identical matrices and right-hand sides \[7, 11-12\]. +- `rational_input_wide{256,1024}_d{2..8}` — diagnostic rational groups with + large positive rational row factors. These retain exact solutions and strict + diagonal dominance while exercising heap-backed numerator and denominator + storage. See the [row-clearing study](performance/rational-row-clearing.md) + for fixtures, allocation evidence, focused timing commands, and limitations. -Each random-corpus and adversarial group runs the same exact-arithmetic +The f64-input random-corpus and adversarial groups run the same exact-arithmetic benches (`det_sign_exact`, `det_exact`, `solve_exact`, `solve_exact_f64_result`, `solve_exact_rounded_f64`) so tables are comparable across input classes. @@ -542,7 +547,10 @@ and first failing component. These checks run outside timed Criterion closures. Any disagreement or unexpected error fails setup instead of becoming an artificially fast measurement. -The rational-input groups are part of the canonical exact release signal. +The original `rational_input_d{2..8}` groups are part of the canonical exact +release signal. The wide-component +diagnostics remain local Criterion evidence and are not included in the release +report's group registry. Releases produced with the rational-input harness include their Criterion point estimates and confidence intervals. When the comparison baseline predates the rational-input API, the report retains current-only rows with an explicit @@ -570,8 +578,8 @@ being compared to the historical lossy `*_exact_f64` benchmark. Rows such as `det_exact_f64_result (vs det_exact_f64)` intentionally show the overhead of the new strict conversion contract against that same historical baseline. -The default `release-signal` scope includes all exact-arithmetic groups because -their inputs and execution order are fixed across revisions. Historical +The default `release-signal` scope includes the canonical exact-arithmetic +groups because their inputs and execution order are fixed across revisions. Historical baselines created before the `exact_random_corpus_d*` names were introduced do not have comparable full-corpus rows, so those rows appear once both sides of a comparison provide the stable group. diff --git a/docs/assets/rational-row-clearing-allocations.csv b/docs/assets/rational-row-clearing-allocations.csv new file mode 100644 index 0000000..770b967 --- /dev/null +++ b/docs/assets/rational-row-clearing-allocations.csv @@ -0,0 +1,190 @@ +family,dimension,operation,cloned_allocations,borrowed_allocations,cloned_bytes,borrowed_bytes +small,2,det,0,0,0,0 +small,2,det_sign,0,0,0,0 +small,2,solve,0,0,0,0 +small,2,matrix_component_clones,0,0,0,0 +small,2,matrix_scaled_cloned,0,0,0,0 +small,2,matrix_scaled_borrowed,0,0,0,0 +small,2,augmented_component_clones,0,0,0,0 +small,2,augmented_scaled_cloned,0,0,0,0 +small,2,augmented_scaled_borrowed,0,0,0,0 +wide256,2,det,51,47,2376,2216 +wide256,2,det_sign,43,39,1896,1736 +wide256,2,solve,103,97,4952,4712 +wide256,2,matrix_component_clones,8,8,320,320 +wide256,2,matrix_scaled_cloned,16,16,672,672 +wide256,2,matrix_scaled_borrowed,12,12,512,512 +wide256,2,augmented_component_clones,12,12,480,480 +wide256,2,augmented_scaled_cloned,28,28,1184,1184 +wide256,2,augmented_scaled_borrowed,22,22,944,944 +wide1024,2,det,55,51,8136,7592 +wide1024,2,det_sign,43,39,6216,5672 +wide1024,2,solve,104,98,16592,15776 +wide1024,2,matrix_component_clones,8,8,1088,1088 +wide1024,2,matrix_scaled_cloned,16,16,2208,2208 +wide1024,2,matrix_scaled_borrowed,12,12,1664,1664 +wide1024,2,augmented_component_clones,12,12,1632,1632 +wide1024,2,augmented_scaled_cloned,28,28,3872,3872 +wide1024,2,augmented_scaled_borrowed,22,22,3056,3056 +small,3,det,0,0,0,0 +small,3,det_sign,0,0,0,0 +small,3,solve,0,0,0,0 +small,3,matrix_component_clones,0,0,0,0 +small,3,matrix_scaled_cloned,0,0,0,0 +small,3,matrix_scaled_borrowed,0,0,0,0 +small,3,augmented_component_clones,0,0,0,0 +small,3,augmented_scaled_cloned,0,0,0,0 +small,3,augmented_scaled_borrowed,0,0,0,0 +wide256,3,det,164,155,7864,7504 +wide256,3,det_sign,153,144,7032,6672 +wide256,3,solve,274,262,15064,14584 +wide256,3,matrix_component_clones,18,18,720,720 +wide256,3,matrix_scaled_cloned,54,54,2304,2304 +wide256,3,matrix_scaled_borrowed,45,45,1944,1944 +wide256,3,augmented_component_clones,24,24,960,960 +wide256,3,augmented_scaled_cloned,64,64,2720,2720 +wide256,3,augmented_scaled_borrowed,52,52,2240,2240 +wide1024,3,det,166,157,25872,24648 +wide1024,3,det_sign,153,144,22872,21648 +wide1024,3,solve,294,282,53912,52280 +wide1024,3,matrix_component_clones,18,18,2448,2448 +wide1024,3,matrix_scaled_cloned,54,54,7488,7488 +wide1024,3,matrix_scaled_borrowed,45,45,6264,6264 +wide1024,3,augmented_component_clones,24,24,3264,3264 +wide1024,3,augmented_scaled_cloned,64,64,8864,8864 +wide1024,3,augmented_scaled_borrowed,52,52,7232,7232 +small,4,det,0,0,0,0 +small,4,det_sign,0,0,0,0 +small,4,solve,5,5,80,80 +small,4,matrix_component_clones,0,0,0,0 +small,4,matrix_scaled_cloned,0,0,0,0 +small,4,matrix_scaled_borrowed,0,0,0,0 +small,4,augmented_component_clones,0,0,0,0 +small,4,augmented_scaled_cloned,0,0,0,0 +small,4,augmented_scaled_borrowed,0,0,0,0 +wide256,4,det,334,318,17592,16952 +wide256,4,det_sign,322,306,16384,15744 +wide256,4,solve,517,497,32976,32176 +wide256,4,matrix_component_clones,32,32,1280,1280 +wide256,4,matrix_scaled_cloned,96,96,4096,4096 +wide256,4,matrix_scaled_borrowed,80,80,3456,3456 +wide256,4,augmented_component_clones,40,40,1600,1600 +wide256,4,augmented_scaled_cloned,108,108,4592,4592 +wide256,4,augmented_scaled_borrowed,88,88,3792,3792 +wide1024,4,det,338,322,57624,55448 +wide1024,4,det_sign,322,306,53152,50976 +wide1024,4,solve,586,566,124696,121976 +wide1024,4,matrix_component_clones,32,32,4352,4352 +wide1024,4,matrix_scaled_cloned,96,96,13312,13312 +wide1024,4,matrix_scaled_borrowed,80,80,11136,11136 +wide1024,4,augmented_component_clones,40,40,5440,5440 +wide1024,4,augmented_scaled_cloned,108,108,14960,14960 +wide1024,4,augmented_scaled_borrowed,88,88,12240,12240 +small,5,det,2,2,32,32 +small,5,det_sign,2,2,32,32 +small,5,solve,17,17,272,272 +small,5,matrix_component_clones,0,0,0,0 +small,5,matrix_scaled_cloned,0,0,0,0 +small,5,matrix_scaled_borrowed,0,0,0,0 +small,5,augmented_component_clones,0,0,0,0 +small,5,augmented_scaled_cloned,0,0,0,0 +small,5,augmented_scaled_borrowed,0,0,0,0 +wide256,5,det,489,468,32416,31568 +wide256,5,det_sign,474,453,30704,29856 +wide256,5,solve,723,697,52768,51712 +wide256,5,matrix_component_clones,42,42,1696,1696 +wide256,5,matrix_scaled_cloned,126,126,5408,5408 +wide256,5,matrix_scaled_borrowed,105,105,4560,4560 +wide256,5,augmented_component_clones,52,52,2112,2112 +wide256,5,augmented_scaled_cloned,140,140,6032,6032 +wide256,5,augmented_scaled_borrowed,114,114,4976,4976 +wide1024,5,det,572,551,127272,124408 +wide1024,5,det_sign,555,534,121336,118472 +wide1024,5,solve,859,833,206416,202864 +wide1024,5,matrix_component_clones,42,42,5728,5728 +wide1024,5,matrix_scaled_cloned,126,126,17504,17504 +wide1024,5,matrix_scaled_borrowed,105,105,14640,14640 +wide1024,5,augmented_component_clones,52,52,7104,7104 +wide1024,5,augmented_scaled_cloned,140,140,19472,19472 +wide1024,5,augmented_scaled_borrowed,114,114,15920,15920 +small,6,det,24,24,384,384 +small,6,det_sign,24,24,384,384 +small,6,solve,78,78,1368,1368 +small,6,matrix_component_clones,0,0,0,0 +small,6,matrix_scaled_cloned,0,0,0,0 +small,6,matrix_scaled_borrowed,0,0,0,0 +small,6,augmented_component_clones,0,0,0,0 +small,6,augmented_scaled_cloned,0,0,0,0 +small,6,augmented_scaled_borrowed,0,0,0,0 +wide256,6,det,774,743,58192,56896 +wide256,6,det_sign,758,727,56016,54720 +wide256,6,solve,1124,1087,86984,85432 +wide256,6,matrix_component_clones,62,62,2592,2592 +wide256,6,matrix_scaled_cloned,187,187,8304,8304 +wide256,6,matrix_scaled_borrowed,156,156,7008,7008 +wide256,6,augmented_component_clones,74,74,3104,3104 +wide256,6,augmented_scaled_cloned,215,215,9584,9584 +wide256,6,augmented_scaled_borrowed,178,178,8032,8032 +wide1024,6,det,1022,991,260440,256168 +wide1024,6,det_sign,1002,971,252792,248520 +wide1024,6,solve,1521,1484,397856,392752 +wide1024,6,matrix_component_clones,62,62,8544,8544 +wide1024,6,matrix_scaled_cloned,187,187,26256,26256 +wide1024,6,matrix_scaled_borrowed,156,156,21984,21984 +wide1024,6,augmented_component_clones,74,74,10208,10208 +wide1024,6,augmented_scaled_cloned,215,215,30224,30224 +wide1024,6,augmented_scaled_borrowed,178,178,25120,25120 +small,7,det,72,72,1184,1184 +small,7,det_sign,70,70,1152,1152 +small,7,solve,254,254,5120,5120 +small,7,matrix_component_clones,0,0,0,0 +small,7,matrix_scaled_cloned,0,0,0,0 +small,7,matrix_scaled_borrowed,0,0,0,0 +small,7,augmented_component_clones,0,0,0,0 +small,7,augmented_scaled_cloned,0,0,0,0 +small,7,augmented_scaled_borrowed,0,0,0,0 +wide256,7,det,1149,1106,96504,94664 +wide256,7,det_sign,1132,1089,93768,91928 +wide256,7,solve,1631,1581,149976,147832 +wide256,7,matrix_component_clones,86,86,3680,3680 +wide256,7,matrix_scaled_cloned,260,260,11808,11808 +wide256,7,matrix_scaled_borrowed,217,217,9968,9968 +wide256,7,augmented_component_clones,100,100,4296,4296 +wide256,7,augmented_scaled_cloned,291,291,13304,13304 +wide256,7,augmented_scaled_borrowed,241,241,11160,11160 +wide1024,7,det,1764,1721,519136,513168 +wide1024,7,det_sign,1743,1700,509696,503728 +wide1024,7,solve,2563,2513,799792,792848 +wide1024,7,matrix_component_clones,86,86,11936,11936 +wide1024,7,matrix_scaled_cloned,260,260,36768,36768 +wide1024,7,matrix_scaled_borrowed,217,217,30800,30800 +wide1024,7,augmented_component_clones,100,100,13896,13896 +wide1024,7,augmented_scaled_cloned,291,291,41336,41336 +wide1024,7,augmented_scaled_borrowed,241,241,34392,34392 +small,8,det,141,141,2464,2464 +small,8,det_sign,139,139,2432,2432 +small,8,solve,461,461,10240,10240 +small,8,matrix_component_clones,0,0,0,0 +small,8,matrix_scaled_cloned,0,0,0,0 +small,8,matrix_scaled_borrowed,0,0,0,0 +small,8,augmented_component_clones,0,0,0,0 +small,8,augmented_scaled_cloned,0,0,0,0 +small,8,augmented_scaled_borrowed,0,0,0,0 +wide256,8,det,1644,1587,162992,160520 +wide256,8,det_sign,1626,1569,159640,157168 +wide256,8,solve,2226,2161,232192,229368 +wide256,8,matrix_component_clones,114,114,4952,4952 +wide256,8,matrix_scaled_cloned,345,345,15856,15856 +wide256,8,matrix_scaled_borrowed,288,288,13384,13384 +wide256,8,augmented_component_clones,130,130,5664,5664 +wide256,8,augmented_scaled_cloned,371,371,17144,17144 +wide256,8,augmented_scaled_borrowed,306,306,14320,14320 +wide1024,8,det,2959,2902,1039840,1031896 +wide1024,8,det_sign,2937,2880,1028424,1020480 +wide1024,8,solve,4203,4138,1561032,1551968 +wide1024,8,matrix_component_clones,114,114,15896,15896 +wide1024,8,matrix_scaled_cloned,345,345,48976,48976 +wide1024,8,matrix_scaled_borrowed,288,288,41032,41032 +wide1024,8,augmented_component_clones,130,130,18144,18144 +wide1024,8,augmented_scaled_cloned,371,371,52952,52952 +wide1024,8,augmented_scaled_borrowed,306,306,43888,43888 diff --git a/docs/assets/rational-row-clearing-timings.csv b/docs/assets/rational-row-clearing-timings.csv new file mode 100644 index 0000000..5084feb --- /dev/null +++ b/docs/assets/rational-row-clearing-timings.csv @@ -0,0 +1,66 @@ +run,family,dimension,operation,cloned_mean_ns,cloned_lower_95_ns,cloned_upper_95_ns,borrowed_mean_ns,borrowed_lower_95_ns,borrowed_upper_95_ns,change_pct +initial,small,2,det_row_cleared_bareiss,253.57996602286676,251.96233109059892,255.04279379555084,240.09172332068007,239.68896652674255,240.497109951427,-5.31912789237079 +initial,small,2,det_sign_row_cleared_bareiss,148.14185815945484,147.6334757091275,148.65083771167528,142.18300290610006,141.4753237356683,142.9555694001452,-4.022398076673827 +initial,small,2,solve_row_cleared_bareiss,934.8261988000057,931.5700652199237,938.3629864625598,935.1289940035239,932.1982732004009,938.6304464889907,0.0323905346156117 +initial,small,3,det_row_cleared_bareiss,692.8203575919379,689.1890648032097,696.3196203901136,684.3753106575886,682.6786084312726,686.0754923596298,-1.2189374694043464 +initial,small,3,det_sign_row_cleared_bareiss,483.9731143083831,480.6555687245251,487.33367139791756,470.25877061014023,468.9244820488141,471.73477895412253,-2.8336994954443306 +initial,small,3,solve_row_cleared_bareiss,2428.1495869333858,2415.5923180930426,2440.6810646904914,2356.626156687433,2349.201778640446,2364.172751396604,-2.9455940701035077 +initial,small,4,det_row_cleared_bareiss,1421.6166379336328,1417.5998579343288,1425.6836793333596,1423.7600908964494,1421.2002068412194,1426.3283118111437,0.15077573697590552 +initial,small,4,det_sign_row_cleared_bareiss,1049.4218658115506,1042.1732456845484,1057.3132069056956,1033.9447768550845,1030.9407562154147,1037.4259512358142,-1.474820514102515 +initial,small,4,solve_row_cleared_bareiss,5240.309187965997,5222.235035716053,5260.991009197907,5239.541006748381,5219.774497195444,5260.018951147271,-0.014659081936996277 +initial,small,5,det_row_cleared_bareiss,2206.486054640564,2194.872216601879,2218.0293565014185,2136.3396833684014,2132.1194427147243,2140.4354701036186,-3.1790987812786886 +initial,small,5,det_sign_row_cleared_bareiss,1648.5518483486487,1646.14546041875,1650.902536307032,1638.659555086847,1635.7774740124396,1641.507365025709,-0.6000595778477225 +initial,small,5,solve_row_cleared_bareiss,9358.726122771854,9311.279654313166,9414.174220386622,9087.67277903307,9057.693035825745,9117.086275658014,-2.8962632326556825 +initial,small,6,det_row_cleared_bareiss,3742.6766210949822,3722.258597008504,3764.9707740236,3709.3586507024793,3702.95943450328,3716.227941921543,-0.8902177175744153 +initial,small,6,det_sign_row_cleared_bareiss,3096.8749833531574,3083.4692392212646,3110.7771110991725,3066.1203151986165,3060.230529540134,3072.2637455138793,-0.993087170772422 +initial,small,6,solve_row_cleared_bareiss,17921.256426024887,17837.570604368004,18018.398889566866,16845.38343614974,16764.349665624974,16935.64994028919,-6.003334611700472 +initial,small,7,det_row_cleared_bareiss,5984.321715079504,5976.7222336995965,5992.614175568864,6079.288623048817,6064.0040892789,6095.369331214466,1.5869285190669435 +initial,small,7,det_sign_row_cleared_bareiss,5137.968719307963,5130.350889156592,5146.419128014786,5179.314648764177,5158.480119822258,5202.670065907994,0.8047135300929487 +initial,small,7,solve_row_cleared_bareiss,28748.124722412427,28680.189422873147,28821.275748153945,28842.567433066582,28781.916352354525,28903.681667646855,0.32851781313070205 +initial,small,8,det_row_cleared_bareiss,9027.476654049626,8993.09945625195,9064.87673649973,9081.470098171674,9045.937224412008,9119.54735671373,0.5981011770086031 +initial,small,8,det_sign_row_cleared_bareiss,8294.164705598829,8259.575585194452,8330.68695396005,8334.572391224432,8314.869134246012,8353.771486188693,0.48718209801557055 +initial,small,8,solve_row_cleared_bareiss,42235.52081951432,42127.08141069668,42353.47654831486,43754.96919474227,43498.03367093075,44046.820845477334,3.597560408266376 +initial,wide1024,2,det_row_cleared_bareiss,43253.44432465209,43187.45136477649,43320.63902083123,43591.13156752431,43486.737269063786,43694.18934031898,0.7807175778594688 +initial,wide1024,2,det_sign_row_cleared_bareiss,1493.0154725671891,1490.4472021981742,1495.6659211730023,1469.0053076038164,1465.859973030293,1472.1389103847846,-1.6081658498882212 +initial,wide1024,2,solve_row_cleared_bareiss,4264.834356562572,4247.291910909457,4286.479593315413,4245.171639132766,4232.841149608623,4259.653064259881,-0.4610429335795696 +initial,wide1024,3,det_row_cleared_bareiss,71279.0930240481,71190.98384093125,71374.31283523676,71799.40166597803,71676.13258594867,71922.51656188311,0.7299596836261468 +initial,wide1024,3,det_sign_row_cleared_bareiss,5497.708936902366,5485.331174732604,5509.94737240965,5427.316146931495,5416.158122274215,5438.550828192502,-1.2804022689955952 +initial,wide1024,3,solve_row_cleared_bareiss,16158.020254805919,16137.049956237013,16178.91394917461,16205.757122182202,16153.220788965784,16262.983199300976,0.29543760079200343 +initial,wide1024,4,det_row_cleared_bareiss,114503.67813268071,114343.13198047389,114669.28278828884,114301.53373736136,114167.42569507592,114438.78369704935,-0.17653965236393843 +initial,wide1024,4,det_sign_row_cleared_bareiss,12404.378821985136,12389.041481368724,12420.351806275245,12306.046504870448,12286.63630180919,12326.163087398034,-0.7927226225984518 +initial,wide1024,4,solve_row_cleared_bareiss,46957.78917919817,46853.121442492076,47045.570018731625,47035.30032516616,46923.36355916813,47145.623546080555,0.16506557766635677 +initial,wide1024,5,det_row_cleared_bareiss,195164.91849691936,194079.50060597592,196309.77789348917,192784.26525094974,192637.23776634166,192956.97138595063,-1.219816176136801 +initial,wide1024,5,det_sign_row_cleared_bareiss,46703.79071479752,46588.87439223618,46821.73781921861,46629.29767066524,46288.513862910804,47060.13003920405,-0.1595010661707974 +initial,wide1024,5,solve_row_cleared_bareiss,99104.11678958892,98856.47135997326,99355.47036390926,99258.95235200635,99079.17371145758,99425.98083513007,0.15623524777095277 +initial,wide1024,6,det_row_cleared_bareiss,309651.21876115206,309367.36619823833,309919.42197307193,312392.3382663883,311877.920727207,312962.43881766626,0.8852280692460468 +initial,wide1024,6,det_sign_row_cleared_bareiss,111420.10904309618,111157.52372176565,111750.82376146427,110604.06871975167,110355.64709187369,110951.0941229689,-0.7323995016275564 +initial,wide1024,6,solve_row_cleared_bareiss,221022.28187242235,220249.63872861385,221815.9275382091,220216.88740298862,219928.42430983591,220531.5277608695,-0.3643951472271034 +initial,wide1024,7,det_row_cleared_bareiss,516574.66151775816,515824.41605357826,517315.9037611137,521815.28600350936,520237.413944002,523955.94087080914,1.0144950722812407 +initial,wide1024,7,det_sign_row_cleared_bareiss,250808.2211784118,249001.17383792633,252684.879869306,243364.77694905738,242591.57168354775,244251.95496619339,-2.9677831908307084 +initial,wide1024,7,solve_row_cleared_bareiss,451145.5924282504,450405.69652945816,451842.03678534547,454615.76148771425,453729.32061763975,455574.30738555937,0.7691905047295222 +initial,wide1024,8,det_row_cleared_bareiss,854057.6782677597,850531.375414877,858055.2965622818,848247.5956232335,847142.7226192092,849372.3769559221,-0.6802916000135384 +initial,wide1024,8,det_sign_row_cleared_bareiss,485427.1053772989,484688.5148746054,486163.51031838043,480891.031090378,479327.6646398089,482344.6072261043,-0.934450144351795 +initial,wide1024,8,solve_row_cleared_bareiss,872855.6975649443,868457.8054307268,876848.7696881484,855996.6228438397,854521.3119870181,857309.7746720805,-1.9314847537957647 +initial,wide256,2,det_row_cleared_bareiss,9996.067227363446,9941.263786843734,10051.344621287762,10330.675304415905,10269.861275277328,10392.901538649916,3.347397225745885 +initial,wide256,2,det_sign_row_cleared_bareiss,1165.0279960489554,1159.7640801549271,1170.423228960023,1113.853347499166,1109.291589290403,1118.6752918419945,-4.392568137705 +initial,wide256,2,solve_row_cleared_bareiss,3558.7792081803223,3502.023771461229,3621.5609032203733,3309.5228163818965,3302.301257468759,3316.69410383133,-7.003985839455207 +initial,wide256,3,det_row_cleared_bareiss,15091.340882542921,15063.144398690873,15119.532802665939,15211.898248103882,15166.780503446134,15249.283539272928,0.7988512518487756 +initial,wide256,3,det_sign_row_cleared_bareiss,4329.312080635126,4315.244958686591,4343.482852380629,4267.239110699776,4242.399707269363,4293.060311504038,-1.4337836769264367 +initial,wide256,3,solve_row_cleared_bareiss,10624.208302421386,10579.985479512608,10671.391771825865,10456.667371303733,10436.07916775459,10479.025522238,-1.5769733268451547 +initial,wide256,4,det_row_cleared_bareiss,25352.323355173943,25267.77191581821,25448.246916435804,25037.640689938828,24973.884450922378,25101.549364207294,-1.2412379758121594 +initial,wide256,4,det_sign_row_cleared_bareiss,9430.921035213689,9377.132331574598,9497.354867126516,9115.643351213552,9093.660430146396,9138.988007981305,-3.3430211410204413 +initial,wide256,4,solve_row_cleared_bareiss,25285.26194400333,25168.53977769464,25430.734186889767,24825.860375934244,24785.51199214454,24867.445033740274,-1.8168748620697506 +initial,wide256,5,det_row_cleared_bareiss,38252.50404843747,38180.790806119345,38331.56197656211,37992.04108929013,37900.54499752998,38080.20857128972,-0.6809043371841184 +initial,wide256,5,det_sign_row_cleared_bareiss,17621.665126750704,17567.21707640123,17678.9420169646,17347.705683297558,17266.435677339636,17434.311109382827,-1.5546739850212044 +initial,wide256,5,solve_row_cleared_bareiss,46680.67589099785,46539.23674886819,46835.28584005561,46638.58752762666,46489.869365577644,46790.69660247468,-0.0901622835741911 +initial,wide256,6,det_row_cleared_bareiss,59533.005932178196,59432.83857947927,59640.08643419085,58985.41158823068,58874.96078086254,59093.54160011237,-0.9198163865123066 +initial,wide256,6,det_sign_row_cleared_bareiss,33023.2444010067,32936.518247991975,33118.18543809107,32627.98068194596,32539.086815338476,32718.193364538743,-1.1969257601130434 +initial,wide256,6,solve_row_cleared_bareiss,87622.78002780314,87427.941335558,87810.97532440156,88824.76264105625,88682.9902878769,88968.99210965159,1.3717695476812342 +initial,wide256,7,det_row_cleared_bareiss,94040.02652774118,93734.90168518898,94405.43648443207,93056.29027932379,92923.96714340567,93192.84596302501,-1.0460824871494467 +initial,wide256,7,det_sign_row_cleared_bareiss,59848.71974345715,59740.22457442797,59960.21900966058,59271.00755393324,59118.60860265242,59431.173828763945,-0.9652874647950505 +initial,wide256,7,solve_row_cleared_bareiss,161160.49889090724,160932.17417696677,161381.93275767018,164003.36100295276,163493.94548583557,164555.4348477847,1.7639943606589936 +initial,wide256,8,det_row_cleared_bareiss,148409.984607296,147336.76823238845,149650.54109030575,145935.82405576986,145775.91488136782,146105.51459694983,-1.6671119251665978 +initial,wide256,8,det_sign_row_cleared_bareiss,104036.49845363051,103829.41331378683,104250.44637532049,103602.19807549684,103321.91316432835,103904.15791994733,-0.4174500147438587 +initial,wide256,8,solve_row_cleared_bareiss,285550.81273397425,284954.9646277508,286193.4876907683,291018.82417213346,289828.5814214202,292384.94267804286,1.914899623575339 +repeat,small,8,solve_row_cleared_bareiss,42048.84657584045,41926.943649787136,42174.46105046291,41833.37102734208,41617.73126706454,42060.07120394692,-0.5124410442739036 +repeat,wide256,2,det_row_cleared_bareiss,9571.799337584047,9524.037117801725,9619.550393113404,9698.927944880726,9621.179023807637,9781.197317790604,1.328157881428882 diff --git a/docs/performance/rational-row-clearing.md b/docs/performance/rational-row-clearing.md new file mode 100644 index 0000000..e095b29 --- /dev/null +++ b/docs/performance/rational-row-clearing.md @@ -0,0 +1,214 @@ +# Rational row-clearing allocation study (#233) + +## Decision and scope + +The component borrowing proposed in #233 was already implemented in +`a62e72a` (#219). This study isolates that change, supplies missing allocation +evidence, and adds reproducible wide-component fixtures. It makes no further +production-code or public-API change. + +The baseline is current source with only `integer_at_scale` replaced by the +former positive-canonical cloning expression: + +```rust +let denominator = value.denom().clone(); +let multiplier = scale / denominator; +let numerator = value.numer().clone(); +numerator * multiplier +``` + +The comparison uses the existing production expression: + +```rust +value.numer() * (scale / value.denom()) +``` + +Both use canonical positive denominators and the same exactly divisible row +scales. The GCD/LCM calculation, elimination, pivoting, reconstruction, and error +contracts are identical. This is an isolated component-cloning experiment, not +a comparison against the entire revision preceding #219 (which also changed +denominator handling in the LCM calculation). + +## Fixtures and independent validation + +Every family covers D=2 through D=8: + +- `small`: the unchanged `rational_input_d{2..8}` diagonally-dominant fixture. +- `wide256`: multiply row `r` by `(2^k + 1) / (2^(k+1) - 1)`, with + `k = 256 + 16r`. +- `wide1024`: the same construction with `k = 1024 + 16r`. + +The positive row factors preserve strict diagonal dominance and existing zeros. +Non-zero entries stress large canonical numerators and denominators; reduction +can remove small factors. Right-hand sides are rebuilt exactly from the known +solution `x[i] = (i+1)/(i+2)`. The shared private-field +`ValidatedRationalInput` is constructed only after independent rational Gaussian +determinant/sign and solve checks agree with the production results and known +solution. Construction and validation run outside measured closures. + +These deterministic families isolate component size. They are not a random +rational corpus or a survey of unrelated large coprime denominators, and they +do not establish performance for all exact-input workloads. + +## Allocation evidence + +The [complete allocation CSV](../assets/rational-row-clearing-allocations.csv) +retains total allocation counts and requested bytes for both implementations. +`det`, `det_sign`, and `solve` rows measure complete public calls, including +dropping their results. The remaining rows isolate component cloning and +integer scaling, with matrix-only or augmented matrix/RHS row scales computed +before measurement. Those scaling rows model the private expressions above; +they are not public row-clearing APIs and exclude LCM construction. + +The small family allocates nothing for either component cloning or isolated +scaling. The pinned num-bigint 0.4.8 supports inline single-digit storage, which +these fixtures use, so counting `.clone()` calls would overstate their heap +cost. Larger small-family public calls still allocate during arithmetic. + +For both wide families, borrowing saves the following allocations per public +call. The public-call differences exactly match the isolated scaling +differences, separating component copies from the remaining arithmetic cost. + +| D | Saved per determinant/sign | Saved per solve | +|---|----------------------------|-----------------| +| 2 | 4 | 6 | +| 3 | 9 | 12 | +| 4 | 16 | 20 | +| 5 | 21 | 26 | +| 6 | 31 | 37 | +| 7 | 43 | 50 | +| 8 | 57 | 65 | + +The net saving is smaller than the two explicit component clones per non-zero +wide entry: changing multiplication operand ownership also changes its storage +reuse. The measured difference, rather than a source-level clone count, is the +relevant result. At D=8, for example, wide1024 determinant allocations decrease +from 2,959 to 2,902, while solve allocations decrease from 4,203 to 4,138. Most +allocation traffic remains in exact arithmetic. + +The allocation-counter 0.8.1 dependency is used only by the separate +`rational_allocations` test executable. It does not replace the allocator in +Criterion timing executables or the library. Each measured closure is checked +three times for identical counts and zero retained allocations/bytes. Counts +are observations for the pinned dependencies and platform, not public API +guarantees or fixed cross-version test expectations. + +## Timing evidence + +The [complete timing CSV](../assets/rational-row-clearing-timings.csv) retains +all 63 initial comparisons and two repeats, including Criterion mean estimates +and their marginal 95% confidence intervals in nanoseconds. Percentage change +is `100 × (borrowed mean / cloned mean - 1)`; negative values mean less time. + +| Family | Cases | Median change | Minimum | Maximum | +|--------|-------|---------------|---------|---------| +| small | 21 | -0.89% | -6.00% | +3.60% | +| wide256 | 21 | -1.05% | -7.00% | +3.35% | +| wide1024 | 21 | -0.36% | -2.97% | +1.01% | + +These are descriptive summaries of per-case mean ratios, not a pooled workload +speedup or a paired confidence interval for the implementation change. Small +movements on a working desktop can reflect run conditions. The two cases whose +initial means increased by more than 2% were repeated in reverse implementation +order (borrowed, then cloned), with identical Criterion settings: + +| Case | Initial change | Repeat change | +|------|----------------|---------------| +| small D=8 solve | +3.60% | -0.51% | +| wide256 D=2 determinant | +3.35% | +1.33% | + +The initial slowdown magnitudes did not persist. This does not prove identical +timing, but supplies no stable material regression supporting a production +change. The allocation reduction is repeatable, while the overall timing +benefit is modest and workload-dependent. Retain the existing borrowed +expression; these results do not justify another row-clearing ownership design +or a general speedup claim. + +## Reproduction + +Record allocation evidence with: + +```bash +cargo test --locked --release --features bench,exact \ + --test rational_allocations -- --nocapture --test-threads=1 +``` + +Each `allocation,...` record contains family, dimension, operation, allocation +count, and requested bytes. The isolated cloned and borrowed rows are available +in the same run. For complete public-call comparisons, run once with the +baseline expression above and once with the existing borrowed expression. + +Record focused timings with the counting executable stopped: + +```bash +CRITERION_HOME=target/criterion-issue-233 \ + cargo bench --locked --features bench,exact --bench exact -- \ + 'rational_input.*/(det_sign|det|solve)_row_cleared_bareiss' \ + --sample-size 30 --warm-up-time 1 --measurement-time 2 --noplot \ + --save-baseline issue233-cloned +``` + +Repeat the identical command with the borrowed expression and baseline name +`issue233-borrowed`. Only the expression and output baseline name change. +`bencher.iter` measures each complete public operation; fixture creation and +oracle validation remain outside timing. The Gaussian reference timings are +excluded from this focused comparison. The separate Criterion directory avoids +overwriting release measurements. + +For the two focused repeats, use the filter below with the same settings and +baseline names `issue233-borrowed-repeat` and `issue233-cloned-repeat`, in that +order: + +```text +^(rational_input_d8/solve_row_cleared_bareiss|rational_input_wide256_d2/det_row_cleared_bareiss)$ +``` + +## Environment + +The study ran on 2026-09-05 UTC (2026-09-04 PDT), starting from +`25aa4c02beda16c5efa66f23d1ebfb8b54532ab1`, with the shared harness and allocation +probe in this patch. Hardware was an Apple M4 Max MacBook Pro, 16 CPU cores +(12 performance, 4 efficiency), 64 GB memory, running macOS 26.6.2 (25G83). +Rust was 1.98.0 (`88d9e12ae`, LLVM 22.1.8), targeting aarch64-apple-darwin. +Cargo's release profile used fat LTO and one codegen unit; no custom Rust flags +or target override were set. Criterion was 0.8.2, num-bigint 0.4.8, and +num-rational 0.4.2. Timing runs were sequential, without concurrent builds or +tests; the desktop environment was not otherwise isolated. + +SHA-256 identities of the measured source and executables: + +```text +Cargo.toml +353e511917fbde3e73f6a10bdd05ca4a6a44cf94a91995689868a1a9714913db +Cargo.lock +f07e3a6a4366712f5a96d1b806e5c1562542e9af2087b58b00b800d8f02080ee +benches/exact.rs +8302e233df39f4ed5931afd94f5e774f30e7224912619bc7e426b4624f9c3c70 +benches/common/rational.rs +fb835a8c859f48d16f612165ff5ab18ca5313b968c36dcc0f8399109ca6c548c +tests/rational_allocations.rs +6bb87fd3b7e90ed3c42a7ebf0354d88f2f76d2b232314a85cc53aafd1d867239 +src/rational.rs (cloned experiment) +af0aeb30059a61922b1ef78662a9ad45e12447f6eedf40fa8f84ea25dedff93c +src/rational.rs (existing borrowed implementation) +c707a22ab9abae32d92a9a781563ddfd22d9ec6b2cba77666c0bc7c85160df2d +exact benchmark executable (cloned experiment) +59b6f1bd13398276c57a744450e42b22be95163958c4fe89e6860ada45b0bb51 +exact benchmark executable (borrowed implementation) +41f7337ba4ee2245d226901b690a13f7401fd2b40159fb2b40ff8527fcebccfd +``` + +## Validation + +- `just test-exact`: 753 tests passed, both before the experiment and on the + restored production implementation. +- The standalone allocation command: 10 tests passed for each implementation; + the final borrowed run reproduced all 189 recorded allocation rows exactly. +- Focused Criterion runs: 63 cases per implementation, plus the two reverse-order + repeats. All measured fixtures passed their independent checks. +- `just ci`: passed, including 824 Rust tests, 612 Python tests, default and + exact-feature doctests, static checks, example execution, and benchmark + compilation. +- Both CSVs were checked for complete row counts, valid timing intervals and + ratios, and equality between public-call allocation reductions and the + isolated scaling reductions. The production `src/` tree has no final diff. diff --git a/justfile b/justfile index 679e5e1..95838df 100644 --- a/justfile +++ b/justfile @@ -26,7 +26,7 @@ clippy_sarif_version := "0.8.0" dprint_version := "0.57.1" git_cliff_version := "2.14.1" just_version := "1.58.0" -rumdl_version := "0.2.64" +rumdl_version := "0.2.65" sarif_fmt_version := "0.8.0" taplo_version := "0.10.0" typos_version := "1.50.1" diff --git a/pyproject.toml b/pyproject.toml index aa5295f..fee3c26 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -155,7 +155,7 @@ dev = [ "actionlint-py==1.7.12.24", "pytest==9.1.1", "ruff==0.16.6", - "semgrep==1.176.0", + "semgrep==1.176.1", "shellcheck-py==0.11.0.1", "shfmt-py==4.1.0", "ty==0.0.78", diff --git a/tests/rational_allocations.rs b/tests/rational_allocations.rs new file mode 100644 index 0000000..df57291 --- /dev/null +++ b/tests/rational_allocations.rs @@ -0,0 +1,138 @@ +//! Allocation evidence for canonical rational row clearing (issue #233). +//! +//! The counting allocator is linked only into this test executable; Criterion +//! timing executables retain their normal allocator. All fixture construction, +//! row scales, and exact oracle checks precede the measured closures. + +#![cfg(all(feature = "bench", feature = "exact"))] +#![forbid(unsafe_code)] + +#[path = "../benches/common/bench_utils.rs"] +mod bench_utils; +#[path = "../benches/common/rational.rs"] +pub mod rational_bench; + +use std::{array::from_fn, hint::black_box}; + +use allocation_counter::{AllocationInfo, measure}; +use la_stack::{BigInt, BigRational}; +use pastey::paste; +use rational_bench::{RationalInputKind, rational_input}; + +/// Reproduce the positive-canonical branch before a62e72a removed its clones. +fn scaled_cloned(value: &BigRational, scale: &BigInt) -> BigInt { + let denominator = value.denom().clone(); + let multiplier = scale / denominator; + let numerator = value.numer().clone(); + numerator * multiplier +} + +/// Model the expression used by the private production `integer_at_scale`. +fn scaled_borrowed(value: &BigRational, scale: &BigInt) -> BigInt { + value.numer() * (scale / value.denom()) +} + +/// Compute LCM via rational reduction, independently of production's GCD loop. +fn row_scale<'a>(values: impl Iterator) -> BigInt { + values.fold(BigInt::from(1), |scale, value| { + let reduced = BigRational::new(scale, value.denom().clone()); + reduced.numer() * value.denom() + }) +} + +fn record(kind: RationalInputKind, operation: &str, run: impl Fn()) { + let counts = measure(&run); + // Counts must be repeatable, and each complete call drops its outputs. + assert_eq!(counts.count_current, 0); + assert_eq!(counts.bytes_current, 0); + for _ in 0..2 { + assert_eq!(measure(&run), counts); + } + let AllocationInfo { + count_total, + bytes_total, + .. + } = counts; + println!( + "allocation,{},{D},{operation},{count_total},{bytes_total}", + kind.name() + ); +} + +fn rational_allocation_evidence() { + for kind in RationalInputKind::ALL { + let input = rational_input::(kind); + let matrix = input.matrix(); + let rhs = input.rhs(); + record::(kind, "det", || { + black_box(black_box(matrix).det()); + }); + record::(kind, "det_sign", || { + let _ = black_box(black_box(matrix).det_sign()); + }); + record::(kind, "solve", || { + let _ = black_box(black_box(matrix).solve(black_box(rhs)).unwrap()); + }); + + for augmented in [false, true] { + let values_in_row = |row: usize| { + matrix.as_rows()[row] + .iter() + .chain(augmented.then_some(&rhs.as_array()[row])) + }; + let scales: [BigInt; D] = from_fn(|row| row_scale(values_in_row(row))); + let entries: Vec<_> = scales + .iter() + .enumerate() + .flat_map(|(row, scale)| values_in_row(row).map(move |value| (value, scale))) + .collect(); + for &(value, scale) in &entries { + let expected = value * BigRational::from_integer(scale.clone()); + assert_eq!( + BigRational::from_integer(scaled_cloned(value, scale)), + expected + ); + assert_eq!( + BigRational::from_integer(scaled_borrowed(value, scale)), + expected + ); + } + + let prefix = if augmented { "augmented" } else { "matrix" }; + record::(kind, &format!("{prefix}_component_clones"), || { + for &(value, _) in black_box(&entries) { + black_box((value.numer().clone(), value.denom().clone())); + } + }); + record::(kind, &format!("{prefix}_scaled_cloned"), || { + for &(value, scale) in black_box(&entries) { + black_box(scaled_cloned(value, scale)); + } + }); + record::(kind, &format!("{prefix}_scaled_borrowed"), || { + for &(value, scale) in black_box(&entries) { + black_box(scaled_borrowed(value, scale)); + } + }); + } + } +} + +macro_rules! gen_rational_allocation_tests { + ($d:literal) => { + paste! { + #[test] + fn []() { + rational_allocation_evidence::<$d>(); + } + } + }; +} + +gen_rational_allocation_tests!(2); +gen_rational_allocation_tests!(3); +gen_rational_allocation_tests!(4); +gen_rational_allocation_tests!(5); +gen_rational_allocation_tests!(6); +gen_rational_allocation_tests!(7); +gen_rational_allocation_tests!(8); diff --git a/uv.lock b/uv.lock index 09bd9e8..d4efedb 100644 --- a/uv.lock +++ b/uv.lock @@ -476,7 +476,7 @@ dev = [ { name = "actionlint-py", specifier = "==1.7.12.24" }, { name = "pytest", specifier = "==9.1.1" }, { name = "ruff", specifier = "==0.16.6" }, - { name = "semgrep", specifier = "==1.176.0" }, + { name = "semgrep", specifier = "==1.176.1" }, { name = "shellcheck-py", specifier = "==0.11.0.1" }, { name = "shfmt-py", specifier = "==4.1.0" }, { name = "ty", specifier = "==0.0.78" }, @@ -1057,7 +1057,7 @@ wheels = [ [[package]] name = "semgrep" -version = "1.176.0" +version = "1.176.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "attrs" }, @@ -1088,15 +1088,15 @@ dependencies = [ { name = "urllib3" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/77/85/89619e12d2d6b7143d412f799f783219de3a5f72d165436ac64f42062acd/semgrep-1.176.0.tar.gz", hash = "sha256:9c0506af2d9a81629907ce7b0cee572c07ef713aa27026a709b8ba7ba2147f34", size = 507672, upload-time = "2026-09-01T19:53:40.073Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/f3/04449eca8f3f249c3a0943e4bdf2cd8d5bd8b1a3f0fe10124dfe9a790a4e/semgrep-1.176.1.tar.gz", hash = "sha256:670e2dc84cc9b7a3b42e42e0b7137b06638cad49cb3166a57377257137d21bad", size = 507827, upload-time = "2026-09-04T20:28:13.453Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/89/d0276a8ed48c5e1c73c51451eaef2b7fd7fc6b25cda6168e9f99e861a98e/semgrep-1.176.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_10_14_x86_64.whl", hash = "sha256:631f53831a4657703196ec119f48efaaf546c8be192134d1f79cd959c3ee0448", size = 45918119, upload-time = "2026-09-01T19:55:29.562Z" }, - { url = "https://files.pythonhosted.org/packages/4b/71/f60dcbf69aedac0f9211c6717e30c870725b79aa1ed253b5d42de6065a29/semgrep-1.176.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_11_0_arm64.whl", hash = "sha256:d930fe18e1a49e8287afff194268ee423a765a2d4a5bcbbd580bdae504e8dd79", size = 49900866, upload-time = "2026-09-01T19:55:33.06Z" }, - { url = "https://files.pythonhosted.org/packages/90/14/79805eade15ae5e3b2b71f5bfba1cc0f062c17e4526959631ed61573f726/semgrep-1.176.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux_2_34_aarch64.whl", hash = "sha256:ce1b606112aec85460359e556cc8f5ed66d85fe685d2e2632245401a78927eb4", size = 72643084, upload-time = "2026-09-01T19:55:48.713Z" }, - { url = "https://files.pythonhosted.org/packages/a3/5f/dc2f52ef4d8af70931e08ac7756577aeb49674b60cc1adc172dbe7031020/semgrep-1.176.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux_2_34_x86_64.whl", hash = "sha256:9d823543758b3c44916648b9dbffd253e883e3ae5d7500b19e1aa6cb31c0c457", size = 70575745, upload-time = "2026-09-01T19:55:53.397Z" }, - { url = "https://files.pythonhosted.org/packages/9b/99/251463bf6cab0dd5b46f5325110db970f7b7fcfe56398ed48e043f1bb059/semgrep-1.176.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_2_aarch64.whl", hash = "sha256:4ffd032cd7c337f316e8a6a6fde6c7110a674577cb0f7996db4cabe03eadfde5", size = 79569854, upload-time = "2026-09-01T19:55:58.518Z" }, - { url = "https://files.pythonhosted.org/packages/56/9c/260e263de67b26b19fb5e54581edf5e898a3387cfac823f41b42f8c6d6b3/semgrep-1.176.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_2_x86_64.whl", hash = "sha256:85de63294e7a7af8a016c906784c2f2849867df20dd5ea927bc58e5a97025302", size = 77155991, upload-time = "2026-09-01T19:56:04.495Z" }, - { url = "https://files.pythonhosted.org/packages/06/76/715a3125c39a98c4bb3143f1c6a4e1bc928533aed8b391c933fda85ae1a7/semgrep-1.176.0-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-win_amd64.whl", hash = "sha256:cbcb1b4ed7d98ced18cb211afd6b7a9b3437683181c4b00b19ebfe091e87bcba", size = 58117151, upload-time = "2026-09-01T19:56:10.447Z" }, + { url = "https://files.pythonhosted.org/packages/d0/1e/2b73dfe585aeee782f08c722d53a2fbedf71f04e812af872dbe0c56d80c8/semgrep-1.176.1-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_10_14_x86_64.whl", hash = "sha256:e1f78275f13c11bd9b6af1143befd79d333d948bd4af96768be3494ab342d8f3", size = 45920492, upload-time = "2026-09-04T20:29:48.859Z" }, + { url = "https://files.pythonhosted.org/packages/cc/a2/f10735897b51b0721a1c239fd1fdb4ac8f9e08dae8698da0bc1737ccf554/semgrep-1.176.1-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-macosx_11_0_arm64.whl", hash = "sha256:ce35dc0b9c34bb95e16f487699646a461e9b2aceda073e773526e0836dbf73ba", size = 49902432, upload-time = "2026-09-04T20:29:51.852Z" }, + { url = "https://files.pythonhosted.org/packages/8e/c9/c1a89c9bb7489199bca3cf3f4c8339c09fd28e4c58a81eed9cbb1c5edaba/semgrep-1.176.1-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux_2_34_aarch64.whl", hash = "sha256:9baed01491cbe1de00f862f73349bf1c9045598bae2923f6a19ef453b3092015", size = 72644931, upload-time = "2026-09-04T20:29:54.826Z" }, + { url = "https://files.pythonhosted.org/packages/17/97/ee08082667af4c3f0da56ad2af4f1e4cbd0b375200235c4ea6e1095b6742/semgrep-1.176.1-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-manylinux_2_34_x86_64.whl", hash = "sha256:00bc0f167564443d7ce4ad7a896d2251ed3c5c17e0b176de9b39bd0fb6abc2a9", size = 70574159, upload-time = "2026-09-04T20:29:58.512Z" }, + { url = "https://files.pythonhosted.org/packages/98/f5/50835e1c5e79a4db04d06cf6f52419dfd065158daaea96bf49692901081b/semgrep-1.176.1-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_2_aarch64.whl", hash = "sha256:2214b71919b825766844ce3dc8c868965d98c3109628882357461ba2a1b8def7", size = 79570338, upload-time = "2026-09-04T20:30:01.807Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e4/53b3478a6ec1ee8129131a216ef763c8850c321ac787e4d66d99bc710cb1/semgrep-1.176.1-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-musllinux_1_2_x86_64.whl", hash = "sha256:5f1127deb8df4e671bae2ec6c811cb069df29fae66bf4880bdad135f38629b23", size = 77161773, upload-time = "2026-09-04T20:30:06.664Z" }, + { url = "https://files.pythonhosted.org/packages/61/d5/6eecdbb00740d801f4e58a16355dd72746171cb7607dac1156d475496a39/semgrep-1.176.1-cp310.cp311.cp312.cp313.cp314.py310.py311.py312.py313.py314-none-win_amd64.whl", hash = "sha256:5ca9822b9b8d645b6f078160139b966bd66a3829b87c4e63e1bb5f8d7f71732f", size = 58115254, upload-time = "2026-09-04T20:30:09.998Z" }, ] [[package]]