diff --git a/benches/linalg.rs b/benches/linalg.rs index f834514d..ded46bb9 100644 --- a/benches/linalg.rs +++ b/benches/linalg.rs @@ -11,7 +11,7 @@ use criterion::{BatchSize, BenchmarkId, Criterion, criterion_group, criterion_main}; #[path = "../src"] -#[allow(dead_code, unused_imports)] +#[expect(dead_code, unused_imports)] mod source { pub mod linalg; } diff --git a/python/src/fitness.rs b/python/src/fitness.rs index 9bdae238..406cb9f6 100644 --- a/python/src/fitness.rs +++ b/python/src/fitness.rs @@ -593,7 +593,7 @@ fn values_with_gradients( // whether `object` is referenced by the caller only: Python kept no reference to it. (pyo3 // deprecates `get_refcnt` for `ffi::Py_REFCNT`, which is unsafe, and this crate has no unsafe // code.) -#[allow(deprecated)] +#[expect(deprecated)] fn only_reference(object: &Bound<'_, PyAny>) -> bool { object.get_refcnt() == 1 } diff --git a/python/src/model.rs b/python/src/model.rs index 9cd2cb07..54ffbe06 100644 --- a/python/src/model.rs +++ b/python/src/model.rs @@ -168,7 +168,7 @@ impl PyGaussianProcess { } /// The posterior mean and variance at `point`, with their gradients. - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] fn predict_with_gradient<'py>( &self, py: Python<'py>, diff --git a/python/src/problems.rs b/python/src/problems.rs index eb507813..410bc225 100644 --- a/python/src/problems.rs +++ b/python/src/problems.rs @@ -699,7 +699,7 @@ fn check_wfg( if distance == 0 { return Err(format!("{name} needs at least 1 distance parameter")); } - if even && distance % 2 == 1 { + if even && !distance.is_multiple_of(2) { return Err(format!( "{name} needs an even number of distance parameters, not {distance}" )); diff --git a/python/src/run.rs b/python/src/run.rs index 50de1ddb..e5ebc98c 100644 --- a/python/src/run.rs +++ b/python/src/run.rs @@ -67,7 +67,7 @@ type Result = std::result::Result; /// stage did and its last point. Returns the result as a dict. #[pyfunction] #[pyo3(signature = (config, fitness, batch = false, parallel = false, on_generation = None, problem = None, control = None, checkpoint = None, checkpoint_every = None, resume = None, gradient = None, combined_gradient = false, constraints = 0, on_stage = None, on_stage_finished = None))] -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub fn run<'py>( py: Python<'py>, config: &str, @@ -1101,7 +1101,7 @@ fn real_algorithm<'py>( } // a gradient method, built from its settings: once per run, its size doesn't matter -#[allow(clippy::large_enum_variant)] +#[expect(clippy::large_enum_variant)] enum GradientMethod { FirstOrder(FirstOrder), Lbfgsb(Lbfgsb), @@ -1275,7 +1275,7 @@ fn gradient_method( } // OpenAI's evolution strategy, with its settings -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn build_open_es( real: Real, population_size: usize, diff --git a/python/src/trees.rs b/python/src/trees.rs index 3a5b61ec..23951f61 100644 --- a/python/src/trees.rs +++ b/python/src/trees.rs @@ -433,7 +433,6 @@ impl PyTree { hasher.finish() } - #[allow(clippy::type_complexity)] fn __reduce__<'py>(&self, py: Python<'py>) -> PyResult<(Bound<'py, PyAny>, (String, String))> { let load = py.get_type::().getattr("_from_json")?; let set = serde_json::to_string(self.set.as_ref()) diff --git a/src/algorithm/bo/space.rs b/src/algorithm/bo/space.rs index 5d6ec5ea..925c952c 100644 --- a/src/algorithm/bo/space.rs +++ b/src/algorithm/bo/space.rs @@ -2,7 +2,7 @@ //! with their genes rounded inside the kernel. // the sealed trait's methods take the crate's own unit-cube map: unnameable outside the crate -#![allow(private_interfaces)] +#![expect(private_interfaces)] use crate::genome::{Integer, Integers, Real, Reals, Representation}; use crate::model::gp::Scaling; diff --git a/src/algorithm/cmaes.rs b/src/algorithm/cmaes.rs index 2ecb8aae..ea92a95a 100644 --- a/src/algorithm/cmaes.rs +++ b/src/algorithm/cmaes.rs @@ -1147,9 +1147,9 @@ mod tests { } fn rosenbrock(x: &Reals) -> f64 { - x.windows(2) - .map(|w| { - let (a, b) = (w[1] - w[0] * w[0], 1.0 - w[0]); + x.array_windows() + .map(|&[xi, next]| { + let (a, b) = (next - xi * xi, 1.0 - xi); 100.0 * a * a + b * b }) .sum() diff --git a/src/algorithm/de.rs b/src/algorithm/de.rs index 5fd99693..c873a6f0 100644 --- a/src/algorithm/de.rs +++ b/src/algorithm/de.rs @@ -2765,9 +2765,9 @@ mod tests { // the Rosenbrock function with only +, − and ×, which is the same on every platform fn rosenbrock(x: &Reals) -> f64 { - x.windows(2) - .map(|w| { - let (a, b) = (w[1] - w[0] * w[0], 1.0 - w[0]); + x.array_windows() + .map(|&[xi, next]| { + let (a, b) = (next - xi * xi, 1.0 - xi); 100.0 * a * a + b * b }) .sum() diff --git a/src/algorithm/es.rs b/src/algorithm/es.rs index 99995099..d5461e40 100644 --- a/src/algorithm/es.rs +++ b/src/algorithm/es.rs @@ -1139,9 +1139,9 @@ mod tests { // the Rosenbrock function with only +, − and ×, which is the same on every platform fn rosenbrock(x: &Reals) -> f64 { - x.windows(2) - .map(|w| { - let (a, b) = (w[1] - w[0] * w[0], 1.0 - w[0]); + x.array_windows() + .map(|&[xi, next]| { + let (a, b) = (next - xi * xi, 1.0 - xi); 100.0 * a * a + b * b }) .sum() diff --git a/src/algorithm/ga.rs b/src/algorithm/ga.rs index ef159ffb..7f05374f 100644 --- a/src/algorithm/ga.rs +++ b/src/algorithm/ga.rs @@ -616,7 +616,7 @@ where ) { let end = offspring.len() + wanted; // `as_chunks` measured a few instructions more per generation on real-valued genomes - #[allow(clippy::chunks_exact_to_as_chunks)] + #[expect(clippy::chunks_exact_to_as_chunks)] for pair in parents.chunks_exact(2) { let parents = [&self.population[pair[0]], &self.population[pair[1]]]; let mut a = spare.copy(parents[0].genome()); @@ -1721,9 +1721,9 @@ mod tests { generations: u64, ) -> (Population, Option>, u64) { let rosenbrock = |x: &Reals| { - x.windows(2) - .map(|w| { - let (a, b) = (w[1] - w[0] * w[0], 1.0 - w[0]); + x.array_windows() + .map(|&[xi, next]| { + let (a, b) = (next - xi * xi, 1.0 - xi); 100.0 * a * a + b * b }) .sum::() diff --git a/src/algorithm/lbfgsb.rs b/src/algorithm/lbfgsb.rs index 1044115e..7371253b 100644 --- a/src/algorithm/lbfgsb.rs +++ b/src/algorithm/lbfgsb.rs @@ -272,7 +272,7 @@ impl Lbfgsb { /// current run, never [`Gradients::Auto`]. Before a run, as an ask without an engine would /// resolve it: forward differences for `Auto`. // the setting resolved, not the setting itself - #[allow(clippy::misnamed_getters)] + #[expect(clippy::misnamed_getters)] pub fn gradients(&self) -> Gradients { self.resolved } @@ -426,9 +426,11 @@ impl Lbfgsb { if alpha == 1.0 { point.copy_from_slice(&self.target); } else { + // resliced to n: no bounds checks in the loop + let (point, x, direction) = (&mut point[..n], &x[..n], &self.direction[..n]); + let (lower, upper) = (&self.lower[..n], &self.upper[..n]); for i in 0..n { - point[i] = - (x[i] + alpha * self.direction[i]).clamp(self.lower[i], self.upper[i]); + point[i] = (x[i] + alpha * direction[i]).clamp(lower[i], upper[i]); } } } @@ -573,11 +575,14 @@ impl Lbfgsb { &mut self.target, ) .ok()?; + let n = x.len(); + let (target, gradient) = (&self.target[..n], &self.gradient[..n]); + let direction = &mut self.direction[..n]; let mut slope = 0.0; - for i in 0..x.len() { - let d = self.target[i] - x[i]; - self.direction[i] = d; - slope += self.gradient[i] * d; + for i in 0..n { + let d = target[i] - x[i]; + direction[i] = d; + slope += gradient[i] * d; } (slope < 0.0 && slope.is_finite()).then_some(slope) } @@ -588,13 +593,16 @@ impl Lbfgsb { let max_step = if self.first_iteration { 1.0 } else { + let n = x.len(); + let (direction, lower, upper) = + (&self.direction[..n], &self.lower[..n], &self.upper[..n]); let mut max_step = f64::MAX; - for i in 0..x.len() { - let d = self.direction[i]; + for i in 0..n { + let d = direction[i]; let room = if d > 0.0 { - (self.upper[i] - x[i]) / d + (upper[i] - x[i]) / d } else if d < 0.0 { - (self.lower[i] - x[i]) / d + (lower[i] - x[i]) / d } else { continue; }; @@ -789,10 +797,13 @@ impl Lbfgsb { // ‖P(x − g) − x‖∞: the largest move of a projected steepest descent step of length 1 fn projected_gradient(x: &[f64], g: &[f64], lower: &[f64], upper: &[f64]) -> f64 { + let n = x.len(); + let (g, lower, upper) = (&g[..n], &lower[..n], &upper[..n]); let mut largest = 0.0f64; - for i in 0..x.len() { + for i in 0..n { let moved = (x[i] - g[i]).clamp(lower[i], upper[i]) - x[i]; if moved.is_nan() { + std::hint::cold_path(); return f64::NAN; } largest = largest.max(moved.abs()); diff --git a/src/algorithm/lbfgsb/model.rs b/src/algorithm/lbfgsb/model.rs index 51ccc1cd..843e8c7a 100644 --- a/src/algorithm/lbfgsb/model.rs +++ b/src/algorithm/lbfgsb/model.rs @@ -128,8 +128,10 @@ impl Memory { epsilon: f64, ) -> bool { // sᵀy and yᵀy first, from the vectors: a skipped pair overwrites nothing + let n = self.n; + let (x_new, x_old, g_new, g_old) = (&x_new[..n], &x_old[..n], &g_new[..n], &g_old[..n]); let (mut sty, mut yty) = (0.0, 0.0); - for i in 0..self.n { + for i in 0..n { let (s, y) = (x_new[i] - x_old[i], g_new[i] - g_old[i]); sty += s * y; yty += y * y; @@ -423,7 +425,7 @@ impl Workspace { /// `upper`], by Algorithm CP of Byrd et al. (section 4), with the middle matrix factored. /// Afterwards `self.c` = Wᵀ(xc − x) (eq. 4.13) and `self.active` marks the genes held at a /// bound: those whose breakpoint the path passed or was at (tᵢ = 0), and the `fixed` ones. - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] pub(super) fn cauchy_point( &mut self, memory: &Memory, @@ -435,6 +437,7 @@ impl Workspace { xc: &mut [f64], ) { let n = x.len(); + let (g, lower, upper, fixed) = (&g[..n], &lower[..n], &upper[..n], &fixed[..n]); let len = memory.len(); let theta = memory.theta(); let d = resized(&mut self.d, n); @@ -542,6 +545,7 @@ impl Workspace { } let dt_min = if dt_min > 0.0 { dt_min } else { 0.0 }; let t = t_old + dt_min; + let xc = &mut xc[..n]; for i in 0..n { if d[i] != 0.0 { xc[i] = (x[i] + t * d[i]).clamp(lower[i], upper[i]); @@ -557,7 +561,7 @@ impl Workspace { /// section 5.1), projected into the box (Morales and Nocedal, 2011), into `xbar`. If the /// projection isn't a descent direction from `x`, the step towards x̂ truncated at the box /// instead (eq. 5.8, 5.9). - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] pub(super) fn subspace_step( &mut self, memory: &Memory, @@ -618,6 +622,7 @@ impl Workspace { if clipped { // the projection, if it's a descent direction (the authors' implementation reverts // when (x̄ − x)ᵀg > 0) + let (xbar, g) = (&mut xbar[..n], &g[..n]); let mut slope = 0.0; for i in 0..n { slope += (xbar[i] - x[i]) * g[i]; diff --git a/src/algorithm/line_search.rs b/src/algorithm/line_search.rs index a521eccf..03ff0913 100644 --- a/src/algorithm/line_search.rs +++ b/src/algorithm/line_search.rs @@ -143,7 +143,7 @@ impl Settings { /// For nonlinear conjugate gradients: as [`QUASI_NEWTON`](Settings::QUASI_NEWTON), with /// η = 0.1 (Nocedal and Wright, 2006, section 3.1). // for nonlinear conjugate gradients (batch D1 of docs/optimization-plan.md) - #[allow(dead_code)] + #[cfg_attr(not(test), expect(dead_code))] pub(crate) const CONJUGATE_GRADIENT: Settings = Settings { gtol: 0.1, ..Settings::QUASI_NEWTON @@ -341,7 +341,7 @@ impl MoreThuente { } /// The step to evaluate next, or `None` once the search has stopped. - #[cfg_attr(not(test), allow(dead_code))] + #[cfg_attr(not(test), expect(dead_code))] pub(crate) fn next(&self) -> Option { match self.stopped { None => Some(self.trial), @@ -350,7 +350,7 @@ impl MoreThuente { } /// The number of steps told so far. - #[cfg_attr(not(test), allow(dead_code))] + #[cfg_attr(not(test), expect(dead_code))] pub(crate) fn trials(&self) -> usize { self.trials } diff --git a/src/algorithm/nelder_mead.rs b/src/algorithm/nelder_mead.rs index b2ff6abc..1108f96c 100644 --- a/src/algorithm/nelder_mead.rs +++ b/src/algorithm/nelder_mead.rs @@ -513,6 +513,7 @@ fn mirror(x: f64, low: f64, high: f64) -> f64 { x }; if mirrored.is_nan() { + std::hint::cold_path(); low } else { mirrored.clamp(low, high) diff --git a/src/algorithm/open_es.rs b/src/algorithm/open_es.rs index 2e3b1614..909db931 100644 --- a/src/algorithm/open_es.rs +++ b/src/algorithm/open_es.rs @@ -646,7 +646,7 @@ impl OpenEsBuilder { setting: "population_size", })?; let invalid = |setting, reason: String| Err(Error::InvalidSetting { setting, reason }); - if size < 2 || size % 2 != 0 { + if size < 2 || !size.is_multiple_of(2) { return invalid( "population_size", format!("must be even and at least 2 (mirrored pairs), got {size}"), diff --git a/src/algorithm/steady.rs b/src/algorithm/steady.rs index 1596563e..39e8e00f 100644 --- a/src/algorithm/steady.rs +++ b/src/algorithm/steady.rs @@ -340,7 +340,7 @@ impl Hasher for Prehashed { const ATTEMPTS: usize = 100; impl SteadyGa { - #[allow(clippy::too_many_arguments)] + #[expect(clippy::too_many_arguments)] pub(crate) fn new( representation: R, select: S, diff --git a/src/bin/genoxide/builtin.rs b/src/bin/genoxide/builtin.rs index 29e02ce6..520f4dfa 100644 --- a/src/bin/genoxide/builtin.rs +++ b/src/bin/genoxide/builtin.rs @@ -48,10 +48,9 @@ pub const FUNCTIONS: &[Function] = &[ description: "real: Rosenbrock's function (minimize, 0 at all ones)", score: |x| { vec![ - x.windows(2) - .map(|pair| { - 100.0 * math::powi(pair[1] - pair[0] * pair[0], 2) - + math::powi(1.0 - pair[0], 2) + x.array_windows() + .map(|&[xi, next]| { + 100.0 * math::powi(next - xi * xi, 2) + math::powi(1.0 - xi, 2) }) .sum(), ] diff --git a/src/checkpoint.rs b/src/checkpoint.rs index cba38c4c..99968e35 100644 --- a/src/checkpoint.rs +++ b/src/checkpoint.rs @@ -118,20 +118,20 @@ pub fn load(mut reader: impl Read) -> Result { .read_to_end(&mut bytes) .map_err(|error| checkpoint_error(format!("can't read: {error}")))?; let mut rest = bytes.as_slice(); - if take(&mut rest, MAGIC.len()) != Some(MAGIC) { + if rest.split_off(..MAGIC.len()) != Some(MAGIC) { return Err(checkpoint_error("not a genoxide checkpoint".to_string())); } // the checksum first, over everything between the magic bytes and it - let summed = rest.len().checked_sub(8).ok_or_else(truncated)?; - let (content, sum) = rest.split_at(summed); - if sum != checksum(content).to_le_bytes() { + let (content, sum) = rest.split_last_chunk().ok_or_else(truncated)?; + if *sum != checksum(content).to_le_bytes() { return Err(checkpoint_error( "corrupted or truncated: the checksum doesn't match".to_string(), )); } let mut rest = content; - let version = take(&mut rest, 1) - .and_then(|len| take(&mut rest, usize::from(len[0]))) + let version = rest + .split_off_first() + .and_then(|&len| rest.split_off(..usize::from(len))) .ok_or_else(truncated)?; if version != VERSION.as_bytes() { return Err(checkpoint_error(format!( @@ -140,9 +140,13 @@ pub fn load(mut reader: impl Read) -> Result { String::from_utf8_lossy(version) ))); } - let kind_len = take(&mut rest, 2).ok_or_else(truncated)?; - let kind_len = u16::from_le_bytes([kind_len[0], kind_len[1]]); - let kind = take(&mut rest, usize::from(kind_len)).ok_or_else(truncated)?; + let kind_len = rest + .split_off(..2) + .and_then(<[u8]>::as_array) + .ok_or_else(truncated)?; + let kind = rest + .split_off(..usize::from(u16::from_le_bytes(*kind_len))) + .ok_or_else(truncated)?; if kind != type_name::().as_bytes() { return Err(checkpoint_error(format!( "holds a {}, not a {}", @@ -150,9 +154,11 @@ pub fn load(mut reader: impl Read) -> Result { type_name::() ))); } - let len = take(&mut rest, 8).ok_or_else(truncated)?; - let len = u64::from_le_bytes(len.try_into().map_err(|_| truncated())?); - if usize::try_from(len) != Ok(rest.len()) { + let len = rest + .split_off(..8) + .and_then(<[u8]>::as_array) + .ok_or_else(truncated)?; + if usize::try_from(u64::from_le_bytes(*len)) != Ok(rest.len()) { return Err(truncated()); } let payload = rest; @@ -221,16 +227,6 @@ fn truncated() -> Error { checkpoint_error("truncated".to_string()) } -// the first `len` bytes of `bytes`, which keeps the rest; `None` if there are fewer -fn take<'a>(bytes: &mut &'a [u8], len: usize) -> Option<&'a [u8]> { - if bytes.len() < len { - return None; - } - let (first, rest) = bytes.split_at(len); - *bytes = rest; - Some(first) -} - // 64-bit FNV-1a, to detect accidental corruption fn checksum(bytes: &[u8]) -> u64 { bytes.iter().fold(0xcbf2_9ce4_8422_2325, |hash, &byte| { diff --git a/src/engine/extras.rs b/src/engine/extras.rs index bdfae2f4..02246a1b 100644 --- a/src/engine/extras.rs +++ b/src/engine/extras.rs @@ -296,7 +296,7 @@ impl<'a> Extras<'a> { } // the three buffers at once: the gradient, the inequalities and the Jacobian - #[allow(clippy::type_complexity)] + #[expect(clippy::type_complexity)] pub(crate) fn buffers( &mut self, ) -> (Option<&mut [f64]>, Option<&mut [f64]>, Option<&mut [f64]>) { diff --git a/src/engine/trace.rs b/src/engine/trace.rs index b2a04986..efae9411 100644 --- a/src/engine/trace.rs +++ b/src/engine/trace.rs @@ -9,7 +9,7 @@ pub(crate) struct RunSpan { } // enters the span of a run of `A`, at the info level -#[cfg_attr(not(feature = "tracing"), allow(clippy::extra_unused_type_parameters))] +#[cfg_attr(not(feature = "tracing"), expect(clippy::extra_unused_type_parameters))] pub(crate) fn run() -> RunSpan { RunSpan { #[cfg(feature = "tracing")] @@ -20,7 +20,7 @@ pub(crate) fn run() -> RunSpan { } // a generation, at the debug level; `front` is the size of a multi-objective front -#[cfg_attr(not(feature = "tracing"), allow(unused_variables))] +#[cfg_attr(not(feature = "tracing"), expect(unused_variables))] pub(crate) fn generation(progress: &Progress, front: Option) { #[cfg(feature = "tracing")] tracing::debug!( @@ -36,7 +36,7 @@ pub(crate) fn generation(progress: &Progress, front: Option) { } // the end of a run, at the info level -#[cfg_attr(not(feature = "tracing"), allow(unused_variables))] +#[cfg_attr(not(feature = "tracing"), expect(unused_variables))] pub(crate) fn finished(progress: &Progress, reason: StopReason, front: Option) { #[cfg(feature = "tracing")] tracing::info!( diff --git a/src/fitness.rs b/src/fitness.rs index 38854411..a69ebbe5 100644 --- a/src/fitness.rs +++ b/src/fitness.rs @@ -148,6 +148,7 @@ impl Fitness { /// negative violation. pub fn try_constrained(score: f64, violation: f64) -> Result { if violation.is_nan() { + std::hint::cold_path(); return Err(Error::NanFitness); } if violation < 0.0 { diff --git a/src/genome/integer.rs b/src/genome/integer.rs index df42c90f..3d6cb00c 100644 --- a/src/genome/integer.rs +++ b/src/genome/integer.rs @@ -260,7 +260,7 @@ mod tests { #[test] fn validation() { assert!(Integer::new([]).is_err()); - #[allow(clippy::reversed_empty_ranges)] + #[expect(clippy::reversed_empty_ranges)] let empty = 1..=0; assert!(Integer::new([0..=1, empty]).is_err()); assert!(Integer::uniform(0, 0..=1).is_err()); diff --git a/src/gp/boolean.rs b/src/gp/boolean.rs index 92c4d9a6..a6906844 100644 --- a/src/gp/boolean.rs +++ b/src/gp/boolean.rs @@ -358,7 +358,9 @@ impl EvenParity { set.terminal(format!("d{bit}"), Logic::Input(bit as u16), boolean); } let primitives = set.build(boolean)?; - let table = Table::new(primitives, inputs, |case| case.count_ones() % 2 == 0); + let table = Table::new(primitives, inputs, |case| { + case.count_ones().is_multiple_of(2) + }); Ok(Self { table }) } diff --git a/src/gp/regression.rs b/src/gp/regression.rs index f94616e2..834ebf5a 100644 --- a/src/gp/regression.rs +++ b/src/gp/regression.rs @@ -367,8 +367,8 @@ pub fn primitives>( return Err(invalid("at least one variable".to_string())); } names.sort_unstable(); - if let Some(pair) = names.windows(2).find(|pair| pair[0] == pair[1]) { - return Err(invalid(format!("the name {:?} is used twice", pair[0]))); + if let Some([name, _]) = names.array_windows().find(|[a, b]| a == b) { + return Err(invalid(format!("the name {name:?} is used twice"))); } if let Some(constants) = constants { set.constants(real, constants); diff --git a/src/gp/representation.rs b/src/gp/representation.rs index f677dc5c..1b76145b 100644 --- a/src/gp/representation.rs +++ b/src/gp/representation.rs @@ -441,7 +441,7 @@ fn type_name(set: &PrimitiveSet

, ty: Type) -> String { // by `method`; with `root_function`, the root is a function when one fits (Koza 1992). Needs // `set.min_size(ty, depth) <= budget`. Without recursion: a stack of the argument slots still to // fill, and the fewest nodes they need, so every choice leaves room to complete the tree. -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(crate) fn generate( set: &PrimitiveSet

, ty: Type, diff --git a/src/linalg/blas.rs b/src/linalg/blas.rs index 5e74731f..ed3221d6 100644 --- a/src/linalg/blas.rs +++ b/src/linalg/blas.rs @@ -6,8 +6,8 @@ //! `0 + a₀b₀ + a₁b₁ + …`, so `gemm` of one column is `gemv`, and a row of `gemv` is `dot`, to the //! bit. -// L-BFGS-B (batch A2) and the Gaussian processes (batch B) are the first users -#![allow(dead_code)] +// `axpy`, `gemv_t` and `gemm` have no user outside the tests yet +#![cfg_attr(not(test), expect(dead_code))] use super::{CHUNK_ROWS, MR, NR, for_each_chunk, tile_add}; @@ -49,6 +49,8 @@ pub(crate) fn gemv(m: usize, n: usize, alpha: f64, a: &[f64], x: &[f64], beta: f } return; } + // resliced to n: no bounds checks in the loop over the columns + let x = &x[..n]; // four rows at a time: four independent sums, each in its own order let rows = a.chunks_exact(4 * n); let rest = rows.remainder(); @@ -108,7 +110,7 @@ pub(crate) fn gemv_t( /// Blocked and packed, with a register tile of the output; on rayon (the `parallel` feature) by /// independent rows of `C` for large products, with the same bits. // BLAS's order of the arguments -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(crate) fn gemm( m: usize, k: usize, @@ -124,7 +126,7 @@ pub(crate) fn gemm( } // `gemm`, on rayon or not -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] pub(super) fn gemm_with( m: usize, k: usize, @@ -173,9 +175,9 @@ pub(super) fn gemm_with( let mut panel = vec![0.0; height.div_ceil(MR) * depth * MR]; for r in 0..height { let row = &a[(i0 + r) * k + p0..(i0 + r) * k + p1]; - let tile = &mut panel[r / MR * depth * MR..]; - for (kk, &x) in row.iter().enumerate() { - tile[kk * MR + r % MR] = alpha * x; + let tile = panel[r / MR * depth * MR..].as_chunks_mut::().0; + for (column, &x) in tile.iter_mut().zip(row) { + column[r % MR] = alpha * x; } } // each column tile reused by all the row tiles while it's in the cache diff --git a/src/linalg/cholesky.rs b/src/linalg/cholesky.rs index 64bce667..6b343c7b 100644 --- a/src/linalg/cholesky.rs +++ b/src/linalg/cholesky.rs @@ -6,9 +6,6 @@ //! order. The blocked factorization keeps that order whatever its block size and thread count, so //! it gives the bits of the textbook loops. -// L-BFGS-B (batch A2) and the Gaussian processes (batch B) are the first users -#![allow(dead_code)] - use super::{CHUNK_ROWS, MR, NR, for_each_chunk, tile_sub}; use std::fmt; @@ -171,9 +168,9 @@ pub(super) fn factor( // L[i][k0..k1] for the chunk's rows, by tiles of MR rows, zero padded let mut panel = vec![0.0; height.div_ceil(MR) * size * MR]; for (r, row) in chunk.chunks_exact(n).enumerate() { - let tile = &mut panel[r / MR * size * MR..]; - for (kk, &x) in row[k0..k1].iter().enumerate() { - tile[kk * MR + r % MR] = x; + let tile = panel[r / MR * size * MR..].as_chunks_mut::().0; + for (column, &x) in tile.iter_mut().zip(&row[k0..k1]) { + column[r % MR] = x; } } // the column tiles up to the diagonal of the last row, each reused by all the row @@ -220,6 +217,7 @@ fn factor_diagonal( for j in k0..k1 { let d = w[j * n + j]; if !(d > 0.0 && d < f64::INFINITY) { + std::hint::cold_path(); return Err(NotPositiveDefinite { column: j }); } let ljj = d.sqrt(); diff --git a/src/linalg/eigen.rs b/src/linalg/eigen.rs index 932abb04..b426278e 100644 --- a/src/linalg/eigen.rs +++ b/src/linalg/eigen.rs @@ -10,7 +10,7 @@ /// The eigenvalues and eigenvectors (the columns of a row-major matrix) of the symmetric /// row-major `n × n` matrix. // the trust-region subproblem (batch D1) is its first user outside the tests -#[allow(dead_code)] +#[cfg_attr(not(test), expect(dead_code))] pub(crate) fn eigen(matrix: &[f64], n: usize) -> (Vec, Vec) { let (values, transposed) = eigen_transposed(matrix, n); let mut vectors = Vec::new(); diff --git a/src/linalg/tests.rs b/src/linalg/tests.rs index 7f647dca..da114b93 100644 --- a/src/linalg/tests.rs +++ b/src/linalg/tests.rs @@ -94,7 +94,7 @@ fn naive_cholesky(a: &[f64], n: usize) -> Option> { } // the textbook triple loop, in gemm's order -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn naive_gemm( m: usize, k: usize, diff --git a/src/linalg/triangular.rs b/src/linalg/triangular.rs index d27ceb53..674d2c26 100644 --- a/src/linalg/triangular.rs +++ b/src/linalg/triangular.rs @@ -4,9 +4,6 @@ //! //! Each column of a matrix right-hand side gets the bits of the single-vector solve. -// L-BFGS-B (batch A2) and the Gaussian processes (batch B) are the first users -#![allow(dead_code)] - /// Solves `L x = b` in place: `xᵢ = (bᵢ − lᵢ₀x₀ − lᵢ₁x₁ − … − lᵢ,ᵢ₋₁xᵢ₋₁) / lᵢᵢ`, the products /// subtracted in ascending order. pub(crate) fn solve_lower(l: &[f64], n: usize, b: &mut [f64]) { diff --git a/src/math.rs b/src/math.rs index dc7988d7..ecde5ac9 100644 --- a/src/math.rs +++ b/src/math.rs @@ -53,6 +53,7 @@ pub fn ln(x: f64) -> f64 { // one comparison for the common case, positive, normal and finite x (whose bits are from // those of f64::MIN_POSITIVE up to those of infinity), and fdlibm's special cases otherwise if x.to_bits().wrapping_sub(MIN_POSITIVE_BITS) >= INFINITY_BITS - MIN_POSITIVE_BITS { + std::hint::cold_path(); if x.is_nan() || x < 0.0 { return f64::NAN; } @@ -132,6 +133,7 @@ pub fn exp(x: f64) -> f64 { let high = high & 0x7fff_ffff; if high >= 0x4086_2e42 { // |x| >= 709.78... + std::hint::cold_path(); if x.is_nan() { return x; } @@ -336,7 +338,11 @@ pub fn erfcx(x: f64) -> f64 { ]; let mut sum = 0.0; for (n, factorial) in DOUBLE_FACTORIALS.iter().enumerate().rev() { - let term = if n % 2 == 0 { *factorial } else { -factorial }; + let term = if n.is_multiple_of(2) { + *factorial + } else { + -factorial + }; sum = sum * t + term; } inverse * std::f64::consts::FRAC_2_SQRT_PI * 0.5 * sum diff --git a/src/multi/indicator.rs b/src/multi/indicator.rs index 861f4a55..8625e06b 100644 --- a/src/multi/indicator.rs +++ b/src/multi/indicator.rs @@ -654,7 +654,7 @@ mod tests { let box_volume: f64 = (0..M) .map(|j| (reference[j] - corner[j]).max(0.0)) .product(); - let sign = if subset.count_ones() % 2 == 1 { + let sign = if !subset.count_ones().is_multiple_of(2) { 1.0 } else { -1.0 diff --git a/src/multi/moead.rs b/src/multi/moead.rs index 05e95efe..458901a0 100644 --- a/src/multi/moead.rs +++ b/src/multi/moead.rs @@ -615,7 +615,7 @@ where let candidates = if anywhere { size } else { neighbors.len() }; let mut replaced = 0; // by index: the whole population's order is drawn at each index as it's visited - #[allow(clippy::needless_range_loop)] + #[expect(clippy::needless_range_loop)] for visited in 0..candidates { if replaced == self.max_replacements { break; diff --git a/src/multi/pareto.rs b/src/multi/pareto.rs index b719d8b9..efa9ebef 100644 --- a/src/multi/pareto.rs +++ b/src/multi/pareto.rs @@ -352,8 +352,7 @@ pub fn crowding_distance(scores: &[Scores], front: &[usize]) } distances[first] = f64::INFINITY; distances[last] = f64::INFINITY; - for window in order.windows(3) { - let (previous, middle, next) = (window[0], window[1], window[2]); + for &[previous, middle, next] in order.array_windows() { distances[middle] += (value(next) - value(previous)) / range; } } diff --git a/src/multi/problems/classic.rs b/src/multi/problems/classic.rs index 66b8aaaf..678f265f 100644 --- a/src/multi/problems/classic.rs +++ b/src/multi/problems/classic.rs @@ -384,8 +384,8 @@ impl MultiFitnessFunction for Kursawe { /// The objective values of `x`. fn evaluate(&self, x: &Reals) -> [f64; 2] { let f1 = x - .windows(2) - .map(|pair| -10.0 * math::exp(-0.2 * (pair[0] * pair[0] + pair[1] * pair[1]).sqrt())) + .array_windows() + .map(|&[xi, next]| -10.0 * math::exp(-0.2 * (xi * xi + next * next).sqrt())) .sum(); let f2 = x .iter() diff --git a/src/multi/problems/engineering.rs b/src/multi/problems/engineering.rs index a4ebdf3f..1e69fbd5 100644 --- a/src/multi/problems/engineering.rs +++ b/src/multi/problems/engineering.rs @@ -484,7 +484,7 @@ impl DiscBrake { } // 3.14 is the paper's constant, which is close to π - #[allow(clippy::approx_constant)] + #[expect(clippy::approx_constant)] fn values(&self, x: &Reals) -> [f64; 5] { let [r, outer, force, s] = self.design(x); let squares = outer * outer - r * r; @@ -613,7 +613,7 @@ impl SpeedReducer { } // 0.7854 is the paper's constant, which is close to π/4 - #[allow(clippy::approx_constant)] + #[expect(clippy::approx_constant)] fn objectives(&self, x: &Reals) -> [f64; 2] { let design = self.design(x); let [x1, x2, x3, x4, x5, x6, x7] = design; @@ -1700,7 +1700,7 @@ mod tests { } #[test] - #[allow(clippy::approx_constant)] + #[expect(clippy::approx_constant)] fn disc_brake() { // the lightest end: r = 55, R = 75, F = 3000, s = 2; R² − r² = 2600 and R³ − r³ = 255,500 let (f, violation) = DiscBrake.evaluate(&at(&[55.0, 75.0, 3000.0, 2.0])); @@ -1768,7 +1768,7 @@ mod tests { } #[test] - #[allow(clippy::approx_constant)] + #[expect(clippy::approx_constant)] fn speed_reducer() { // the design rounds x₃ as the single-objective problem does let x = at(&[3.5, 0.7, 17.4, 7.3, 7.8, 3.35, 5.29]); diff --git a/src/multi/problems/wfg.rs b/src/multi/problems/wfg.rs index b8c85977..774b7a59 100644 --- a/src/multi/problems/wfg.rs +++ b/src/multi/problems/wfg.rs @@ -718,12 +718,12 @@ fn bisect(f: impl Fn(f64) -> f64, mut lo: f64, mut hi: f64) -> f64 { fn wfg2_intervals() -> Vec<(f64, f64)> { // the local minima, where x cos²(5πx) peaks: cos(5πx) = 10πx sin(5πx), once in each // (j/5, j/5 + 1/10); then x₁ = 1, where the shape is 0 - let mut ends: Vec = (0..5) + let mut ends: Vec = (0..5_u32) .map(|j| { - let sign = if j % 2 == 0 { 1.0 } else { -1.0 }; + let sign = if j.is_multiple_of(2) { 1.0 } else { -1.0 }; let slope = |x: f64| sign * (10.0 * PI * x * math::sin(5.0 * PI * x) - math::cos(5.0 * PI * x)); - let start = j as f64 / 5.0; + let start = f64::from(j) / 5.0; bisect(slope, start, start + 0.1) }) .collect(); diff --git a/src/multi/spea2.rs b/src/multi/spea2.rs index f6d98179..57db1006 100644 --- a/src/multi/spea2.rs +++ b/src/multi/spea2.rs @@ -230,7 +230,7 @@ fn strength_fitness( } } } - let k = ((n as f64).sqrt() as usize).clamp(1, n.saturating_sub(1).max(1)); + let k = n.isqrt().clamp(1, n.saturating_sub(1).max(1)); fitness.clear(); fitness.extend((0..n).map(|i| { // the k-th smallest distance: the value a sort would put there, as the order is total diff --git a/src/nn.rs b/src/nn.rs index 44ba07b3..894b206f 100644 --- a/src/nn.rs +++ b/src/nn.rs @@ -274,10 +274,12 @@ impl Mlp { } fn count_parameters(&self, bias: bool) -> Option { - self.layers.windows(2).try_fold(0_usize, |sum, pair| { - let per_unit = pair[0].checked_add(usize::from(bias))?; - sum.checked_add(pair[1].checked_mul(per_unit)?) - }) + self.layers + .array_windows() + .try_fold(0_usize, |sum, &[inputs, units]| { + let per_unit = inputs.checked_add(usize::from(bias))?; + sum.checked_add(units.checked_mul(per_unit)?) + }) } /// The representation of this network's weights, [`parameters`](Mlp::parameters) genes, each @@ -577,7 +579,7 @@ impl ElmanNetwork<'_> { #[cfg(test)] // the weights written out, -1 included -#[allow(clippy::neg_multiply)] +#[expect(clippy::neg_multiply)] mod tests { use super::*; use crate::StreamRng; diff --git a/src/population.rs b/src/population.rs index 4ac4b7df..b54a8b9f 100644 --- a/src/population.rs +++ b/src/population.rs @@ -70,7 +70,7 @@ impl Population { } else { // moved one at a time: `append` copies the memory at once, but glibc copies a large // population with `rep movsb`, which Callgrind counts per byte - #[allow(clippy::extend_with_drain)] + #[expect(clippy::extend_with_drain)] self.individuals.extend(others.drain(..)); } } diff --git a/src/problems/cec2006.rs b/src/problems/cec2006.rs index 45abfb47..668513dd 100644 --- a/src/problems/cec2006.rs +++ b/src/problems/cec2006.rs @@ -41,7 +41,7 @@ //! | [`G24`] | 2 | 2 inequalities | −5.50801327159536 | // the report's solutions keep every digit it prints -#![allow(clippy::excessive_precision)] +#![expect(clippy::excessive_precision)] use super::{Constraints, Optimum, Problem}; use crate::engine::{Extras, FitnessFunction, Provided}; @@ -1576,7 +1576,7 @@ impl Problem for G17 { } // 0.5236 is the report's bound, which is close to π/6 - #[allow(clippy::approx_constant)] + #[expect(clippy::approx_constant)] fn representation(&self) -> Real { bounds(&[ (0.0, 400.0), diff --git a/src/problems/classic.rs b/src/problems/classic.rs index 89c77207..cc96b506 100644 --- a/src/problems/classic.rs +++ b/src/problems/classic.rs @@ -595,11 +595,8 @@ impl FitnessFunction for Rosenbrock { gradient!(gradients::rosenbrock); fn evaluate(&self, x: &Reals) -> f64 { - x.windows(2) - .map(|pair| { - let (xi, next) = (pair[0], pair[1]); - 100.0 * math::powi(next - xi * xi, 2) + math::powi(xi - 1.0, 2) - }) + x.array_windows() + .map(|&[xi, next]| 100.0 * math::powi(next - xi * xi, 2) + math::powi(xi - 1.0, 2)) .sum() } } @@ -1081,9 +1078,9 @@ impl FitnessFunction for DixonPrice { return 0.0; }; let chain: f64 = x - .windows(2) + .array_windows() .enumerate() - .map(|(k, pair)| (k + 2) as f64 * math::powi(2.0 * pair[1] * pair[1] - pair[0], 2)) + .map(|(k, &[previous, xi])| (k + 2) as f64 * math::powi(2.0 * xi * xi - previous, 2)) .sum(); math::powi(first - 1.0, 2) + chain } @@ -1130,7 +1127,7 @@ impl FitnessFunction for Trid { fn evaluate(&self, x: &Reals) -> f64 { let squares: f64 = x.iter().map(|xi| math::powi(xi - 1.0, 2)).sum(); - let products: f64 = x.windows(2).map(|pair| pair[0] * pair[1]).sum(); + let products: f64 = x.array_windows().map(|&[xi, next]| xi * next).sum(); squares - products } } @@ -2833,8 +2830,8 @@ impl FitnessFunction for Penalized1 { return 0.0; }; let middle: f64 = y - .windows(2) - .map(|pair| math::powi(pair[0] - 1.0, 2) * (1.0 + 10.0 * sin_squared(PI * pair[1]))) + .array_windows() + .map(|&[yi, next]| math::powi(yi - 1.0, 2) * (1.0 + 10.0 * sin_squared(PI * next))) .sum(); let levy = 10.0 * sin_squared(PI * first) + middle + math::powi(last - 1.0, 2); let penalties: f64 = x.iter().map(|&xi| penalty(xi, 10.0, 100.0, 4)).sum(); @@ -2884,8 +2881,8 @@ impl FitnessFunction for Penalized2 { return 0.0; }; let middle: f64 = x - .windows(2) - .map(|pair| math::powi(pair[0] - 1.0, 2) * (1.0 + sin_squared(3.0 * PI * pair[1]))) + .array_windows() + .map(|&[xi, next]| math::powi(xi - 1.0, 2) * (1.0 + sin_squared(3.0 * PI * next))) .sum(); let end = math::powi(last - 1.0, 2) * (1.0 + sin_squared(2.0 * PI * last)); let penalties: f64 = x.iter().map(|&xi| penalty(xi, 5.0, 100.0, 4)).sum(); @@ -3129,7 +3126,7 @@ impl FitnessFunction for BucheRastrigin { let oscillated = oscillation(xi); let mut scale = math::powf(10.0, 0.5 * i as f64 / (n.max(2) - 1) as f64); // i from 0 here: the odd genes from 1 are the even ones from 0 - if oscillated > 0.0 && i % 2 == 0 { + if oscillated > 0.0 && i.is_multiple_of(2) { scale *= 10.0; } let z = scale * oscillated; @@ -3458,9 +3455,9 @@ impl FitnessFunction for SchafferF7 { fn evaluate(&self, x: &Reals) -> f64 { let pairs = x.len().saturating_sub(1).max(1) as f64; let sum: f64 = x - .windows(2) - .map(|pair| { - let s = (pair[0] * pair[0] + pair[1] * pair[1]).sqrt(); + .array_windows() + .map(|&[xi, next]| { + let s = (xi * xi + next * next).sqrt(); s.sqrt() * (1.0 + sin_squared(50.0 * math::powf(s, 0.2))) }) .sum(); diff --git a/src/problems/control.rs b/src/problems/control.rs index 96b6f260..b91d3d88 100644 --- a/src/problems/control.rs +++ b/src/problems/control.rs @@ -766,7 +766,7 @@ impl DoublePole { } #[cfg(test)] -#[allow(clippy::needless_range_loop)] +#[expect(clippy::needless_range_loop)] mod tests { use super::*; use crate::StreamRng; diff --git a/src/problems/engineering.rs b/src/problems/engineering.rs index 119b412f..3f16b56a 100644 --- a/src/problems/engineering.rs +++ b/src/problems/engineering.rs @@ -548,7 +548,7 @@ impl SpeedReducer { } // 0.7854 is the paper's constant, which is close to π/4 - #[allow(clippy::approx_constant)] + #[expect(clippy::approx_constant)] fn value(&self, x: &Reals) -> f64 { let [x1, x2, x3, x4, x5, x6, x7] = self.design(x); 0.7854 * x1 * x2 * x2 * (3.3333 * x3 * x3 + 14.9334 * x3 - 43.0934) diff --git a/src/problems/gradients.rs b/src/problems/gradients.rs index fa44cfc0..d6440269 100644 --- a/src/problems/gradients.rs +++ b/src/problems/gradients.rs @@ -50,8 +50,7 @@ pub(super) fn rastrigin(x: &[f64], gradient: &mut [f64]) { // Σ 100 (xᵢ₊₁ − xᵢ²)² + (xᵢ − 1)²: each term adds −400 xᵢ (xᵢ₊₁ − xᵢ²) + 2 (xᵢ − 1) to gene i // and 200 (xᵢ₊₁ − xᵢ²) to gene i + 1 pub(super) fn rosenbrock(x: &[f64], gradient: &mut [f64]) { - for (i, pair) in x.windows(2).enumerate() { - let (xi, next) = (pair[0], pair[1]); + for (i, &[xi, next]) in x.array_windows().enumerate() { let valley = next - xi * xi; gradient[i] += -400.0 * xi * valley + 2.0 * (xi - 1.0); gradient[i + 1] += 200.0 * valley; @@ -168,11 +167,11 @@ pub(super) fn dixon_price(x: &[f64], gradient: &mut [f64]) { return; }; gradient[0] = 2.0 * (first - 1.0); - for (k, pair) in x.windows(2).enumerate() { + for (k, &[previous, xi]) in x.array_windows().enumerate() { let weight = (k + 2) as f64; - let u = 2.0 * pair[1] * pair[1] - pair[0]; + let u = 2.0 * xi * xi - previous; gradient[k] += -2.0 * weight * u; - gradient[k + 1] += 8.0 * weight * u * pair[1]; + gradient[k + 1] += 8.0 * weight * u * xi; } } @@ -543,7 +542,7 @@ pub(super) fn buche_rastrigin(x: &[f64], gradient: &mut [f64]) { } let oscillated = oscillation(xi); let mut scale = math::powf(10.0, 0.5 * i as f64 / (n.max(2) - 1) as f64); - if oscillated > 0.0 && i % 2 == 0 { + if oscillated > 0.0 && i.is_multiple_of(2) { scale *= 10.0; } let z = scale * oscillated; @@ -627,8 +626,8 @@ pub(super) fn hg_bat(x: &[f64], gradient: &mut [f64]) { pub(super) fn schaffer_f7(x: &[f64], gradient: &mut [f64]) { let pairs = x.len().saturating_sub(1).max(1) as f64; let mut sum = 0.0; - for (i, pair) in x.windows(2).enumerate() { - let s = (pair[0] * pair[0] + pair[1] * pair[1]).sqrt(); + for (i, &[xi, next]) in x.array_windows().enumerate() { + let s = (xi * xi + next * next).sqrt(); let root = s.sqrt(); let fifth = math::powf(s, 0.2); let (sin, cos) = math::sin_cos(50.0 * fifth); @@ -636,8 +635,8 @@ pub(super) fn schaffer_f7(x: &[f64], gradient: &mut [f64]) { if s > 0.0 { // 10 sin(2θ) s^(−3/10) = 20 sin θ cos θ s^(1/5) / √s let slope = (1.0 + sin * sin) / (2.0 * root) + 20.0 * sin * cos * fifth / root; - gradient[i] += slope * pair[0] / s; - gradient[i + 1] += slope * pair[1] / s; + gradient[i] += slope * xi / s; + gradient[i + 1] += slope * next / s; } } let outer = 2.0 * sum / (pairs * pairs); diff --git a/src/rng.rs b/src/rng.rs index 0001ccbe..00f75950 100644 --- a/src/rng.rs +++ b/src/rng.rs @@ -92,14 +92,15 @@ impl StreamRng { #[inline] pub(crate) fn below_u64(&mut self, n: u64) -> u64 { debug_assert!(n > 0, "below(0)"); - let mut product = u128::from(self.next_u64()) * u128::from(n); - if (product as u64) < n { + // the 128-bit product's low and high halves + let (mut low, mut high) = self.next_u64().carrying_mul(n, 0); + if low < n { let threshold = n.wrapping_neg() % n; - while (product as u64) < threshold { - product = u128::from(self.next_u64()) * u128::from(n); + while low < threshold { + (low, high) = self.next_u64().carrying_mul(n, 0); } } - (product >> 64) as u64 + high } /// A uniformly random `f64` in `[0, 1)`, with 53 random bits. diff --git a/tests/algorithms.rs b/tests/algorithms.rs index 4d0c9d09..b20e1c4b 100644 --- a/tests/algorithms.rs +++ b/tests/algorithms.rs @@ -98,8 +98,8 @@ fn adaptive_differential_evolution_solves_rastrigin_without_tuning() { } fn rosenbrock(x: &Reals) -> f64 { - x.windows(2) - .map(|w| 100.0 * (w[1] - w[0] * w[0]).powi(2) + (1.0 - w[0]).powi(2)) + x.array_windows() + .map(|&[xi, next]| 100.0 * (next - xi * xi).powi(2) + (1.0 - xi).powi(2)) .sum() } @@ -229,9 +229,9 @@ fn evolution_strategy_runs_in_parallel_with_the_same_results() { // change for the same major version, on any platform fn portable_run>(algorithm: A) -> Vec { let rosenbrock = |x: &Reals| { - x.windows(2) - .map(|w| { - let (a, b) = (w[1] - w[0] * w[0], 1.0 - w[0]); + x.array_windows() + .map(|&[xi, next]| { + let (a, b) = (next - xi * xi, 1.0 - xi); 100.0 * a * a + b * b }) .sum::() @@ -283,9 +283,9 @@ fn portable_continuation_run(algorithm: FirstOrder) -> Vec { let shared = Arc::clone(&weight); let rosenbrock = move |x: &Reals| { let w = f64::from_bits(shared.load(Ordering::Relaxed)); - x.windows(2) - .map(|pair| { - let (a, b) = (pair[1] - pair[0] * pair[0], 1.0 - pair[0]); + x.array_windows() + .map(|&[xi, next]| { + let (a, b) = (next - xi * xi, 1.0 - xi); w * a * a + b * b }) .sum::() @@ -317,9 +317,9 @@ where { let objectives = |x: &Reals| { let rosenbrock = x - .windows(2) - .map(|w| { - let (a, b) = (w[1] - w[0] * w[0], 1.0 - w[0]); + .array_windows() + .map(|&[xi, next]| { + let (a, b) = (next - xi * xi, 1.0 - xi); 100.0 * a * a + b * b }) .sum::(); diff --git a/tests/continuation.rs b/tests/continuation.rs index c6388cee..278d5f70 100644 --- a/tests/continuation.rs +++ b/tests/continuation.rs @@ -14,7 +14,7 @@ const CENTER: [f64; 3] = [0.3, -1.2, 2.0]; const EPSILON: [f64; 4] = [1.0, 0.1, 0.01, 0.001]; // Σ √((xᵢ − cᵢ)² + ε²), a smoothed Σ |xᵢ − cᵢ|, with its gradient, and the cell that holds ε -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] fn smoothed() -> ( Arc, Differentiable f64 + Sync>, diff --git a/tests/gaussian_process.rs b/tests/gaussian_process.rs index 6fe52a6f..a66e64b5 100644 --- a/tests/gaussian_process.rs +++ b/tests/gaussian_process.rs @@ -24,7 +24,7 @@ struct TwoPoints { log_likelihood: f64, } -#[allow(clippy::too_many_arguments)] +#[expect(clippy::too_many_arguments)] fn two_points( kernel: Kernel, x: [f64; 2], diff --git a/tests/gp.rs b/tests/gp.rs index 1588d5b9..377ece8d 100644 --- a/tests/gp.rs +++ b/tests/gp.rs @@ -287,7 +287,7 @@ fn invalid_representations_are_errors() { setting_error(Gp::builder(set()).max_size(1 << 25).build(), "max_size"); setting_error(Gp::builder(set()).max_depth(1 << 25).build(), "max_depth"); setting_error(Gp::builder(set()).max_depth(5).build(), "init"); - #[allow(clippy::reversed_empty_ranges)] + #[expect(clippy::reversed_empty_ranges)] let empty = Init::Grow { depths: 3..=2 }; setting_error(Gp::builder(set()).init(empty).build(), "init"); let gp = Gp::builder(set()).build().unwrap(); diff --git a/tests/nelder_mead.rs b/tests/nelder_mead.rs index 2779222c..b2aafa47 100644 --- a/tests/nelder_mead.rs +++ b/tests/nelder_mead.rs @@ -6,9 +6,9 @@ use genoxide::prelude::*; use genoxide::problems::{Himmelblau, Problem}; fn rosenbrock(x: &Reals) -> f64 { - x.windows(2) - .map(|w| { - let (a, b) = (w[1] - w[0] * w[0], 1.0 - w[0]); + x.array_windows() + .map(|&[xi, next]| { + let (a, b) = (next - xi * xi, 1.0 - xi); 100.0 * a * a + b * b }) .sum()