diff --git a/Cargo.toml b/Cargo.toml index 4fd5c8f5d..f82b3c168 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ name = "lenskit" version = "2026.3.0-beta1" edition = "2024" license = "MIT" -rust-version = "1.87" +rust-version = "1.89" [lib] name = "lenskit_accel" diff --git a/hk.pkl b/hk.pkl index 76b7e014d..376edd912 100644 --- a/hk.pkl +++ b/hk.pkl @@ -11,6 +11,7 @@ local linters = new Mapping { ["trailing-whitespace"] = Builtins.trailing_whitespace // ["check-merge-conflict"] = Builtins.check_merge_conflict ["check-toml"] = Builtins.taplo + ["clippy"] = Builtins.cargo_clippy ["ruff"] = Builtins.ruff } diff --git a/mise/mise.lock b/mise/mise.lock index 3e82f2965..297865c27 100644 --- a/mise/mise.lock +++ b/mise/mise.lock @@ -40,38 +40,38 @@ url = "https://github.com/dprint/dprint/releases/download/0.55.2/dprint-x86_64-p url_api = "https://api.github.com/repos/dprint/dprint/releases/assets/477041355" [[tools.hk]] -version = "1.51.0" +version = "1.53.0" backend = "aqua:jdx/hk" [tools.hk."platforms.linux-arm64"] -checksum = "sha256:6dc84f63ba365544d36a3436360b69c04fcad50dc29758c826de5a7e363c9809" -url = "https://github.com/jdx/hk/releases/download/v1.51.0/hk-aarch64-unknown-linux-gnu.tar.gz" -url_api = "https://api.github.com/repos/jdx/hk/releases/assets/476588078" +checksum = "sha256:3cf58d268a9114f0923a06c38cf0995b9da43bf2279b09aac3c3c615a615b3e4" +url = "https://github.com/jdx/hk/releases/download/v1.53.0/hk-aarch64-unknown-linux-gnu.tar.gz" +url_api = "https://api.github.com/repos/jdx/hk/releases/assets/487429257" [tools.hk."platforms.linux-arm64-musl"] -checksum = "sha256:e367ad005cd42172431e3df9f14774401b1620f82f09e508c9cf6ee164deeda6" -url = "https://github.com/jdx/hk/releases/download/v1.51.0/hk-aarch64-unknown-linux-musl.tar.gz" -url_api = "https://api.github.com/repos/jdx/hk/releases/assets/476588076" +checksum = "sha256:36fdea5faa4994f4c46e18cb5a52f07a7db4228f1ea618cf371bc297cbc48070" +url = "https://github.com/jdx/hk/releases/download/v1.53.0/hk-aarch64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/jdx/hk/releases/assets/487429258" [tools.hk."platforms.linux-x64"] -checksum = "sha256:6ef59a3a06c10141181853bfcbe82bdb939c5a65efe05c762f98a06896b4cc25" -url = "https://github.com/jdx/hk/releases/download/v1.51.0/hk-x86_64-unknown-linux-gnu.tar.gz" -url_api = "https://api.github.com/repos/jdx/hk/releases/assets/476588091" +checksum = "sha256:960c14b3bcd61e36dcb42c304e3cc23b22ef2e6f28ac6559e3856d27acf6b54a" +url = "https://github.com/jdx/hk/releases/download/v1.53.0/hk-x86_64-unknown-linux-gnu.tar.gz" +url_api = "https://api.github.com/repos/jdx/hk/releases/assets/487429267" [tools.hk."platforms.linux-x64-musl"] -checksum = "sha256:cced510878bc6b78107b438bbbeff1b4b451623ff965ba81d72345d229c1318f" -url = "https://github.com/jdx/hk/releases/download/v1.51.0/hk-x86_64-unknown-linux-musl.tar.gz" -url_api = "https://api.github.com/repos/jdx/hk/releases/assets/476588095" +checksum = "sha256:c2075be6f6d4b3606450bdcb58ce5d17d9668804e8ee4f9d93dda596cae2de55" +url = "https://github.com/jdx/hk/releases/download/v1.53.0/hk-x86_64-unknown-linux-musl.tar.gz" +url_api = "https://api.github.com/repos/jdx/hk/releases/assets/487429272" [tools.hk."platforms.macos-arm64"] -checksum = "sha256:1f19137c63bc3be45c58badd4aa0e14b2718669df93c8f4c1d05b74e5ab6acef" -url = "https://github.com/jdx/hk/releases/download/v1.51.0/hk-aarch64-apple-darwin.tar.gz" -url_api = "https://api.github.com/repos/jdx/hk/releases/assets/476588079" +checksum = "sha256:d5c1f9a4c3598cc72c9f15908af6e036b138b9dd97c8891e4d08f699c98a5069" +url = "https://github.com/jdx/hk/releases/download/v1.53.0/hk-aarch64-apple-darwin.tar.gz" +url_api = "https://api.github.com/repos/jdx/hk/releases/assets/487429255" [tools.hk."platforms.windows-x64"] -checksum = "sha256:0a583f8980c19bb4b95e64c3785bc824177ff51600e994309851c79e2f23b2ac" -url = "https://github.com/jdx/hk/releases/download/v1.51.0/hk-x86_64-pc-windows-msvc.zip" -url_api = "https://api.github.com/repos/jdx/hk/releases/assets/476588075" +checksum = "sha256:a2e5c6131d01cfd6d62204872798f97fc933e7f142d718b3e191434e2d31ef1c" +url = "https://github.com/jdx/hk/releases/download/v1.53.0/hk-x86_64-pc-windows-msvc.zip" +url_api = "https://api.github.com/repos/jdx/hk/releases/assets/487429261" [[tools.perl]] version = "5.44.0.0" diff --git a/rust-toolchain.toml b/rust-toolchain.toml index fec53dc0f..b85f01441 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,3 +1,3 @@ [toolchain] channel = "stable" -components = ["rustfmt", "cargo", "rustc", "llvm-tools"] +components = ["rustfmt", "cargo", "rustc", "llvm-tools", "clippy"] diff --git a/src/accel/als/explicit.rs b/src/accel/als/explicit.rs index 2aed79d06..d79598c73 100644 --- a/src/accel/als/explicit.rs +++ b/src/accel/als/explicit.rs @@ -12,7 +12,6 @@ use ndarray::{Array1, ArrayBase, ArrayView2, Axis, ViewRepr}; use numpy::{Ix1, PyArray2, PyArrayMethods}; use pyo3::{IntoPyObjectExt, exceptions::PyRuntimeError, prelude::*}; use rayon::prelude::*; -use thiserror::Error; use rayon_cancel::CancelAdapter; @@ -89,13 +88,13 @@ fn train_row_solve( let cols = matrix.row_cols(row_num); let vals = matrix.row_vals(row_num); - if cols.len() == 0 { + if cols.is_empty() { row_data.fill(0.0); return Ok(0.0); } let cols: Vec<_> = cols.iter().map(|c| *c as usize).collect(); - let vals: Array1<_> = vals.iter().map(|f| *f).collect(); + let vals: Array1<_> = vals.iter().copied().collect(); let nd = row_data.len(); diff --git a/src/accel/als/implicit.rs b/src/accel/als/implicit.rs index 988f0028c..d8b044f17 100644 --- a/src/accel/als/implicit.rs +++ b/src/accel/als/implicit.rs @@ -95,13 +95,13 @@ fn train_row_solve( let cols = matrix.row_cols(row_num); let vals = matrix.row_vals(row_num); - if cols.len() == 0 { + if cols.is_empty() { row_data.fill(0.0); return Ok(0.0); } let cols: Vec<_> = cols.iter().map(|c| *c as usize).collect(); - let mut vals: Array1<_> = vals.iter().map(|f| *f).collect(); + let mut vals: Array1<_> = vals.iter().copied().collect(); let nd = row_data.len(); diff --git a/src/accel/als/solve.rs b/src/accel/als/solve.rs index 85ffd490f..9581d1cb4 100644 --- a/src/accel/als/solve.rs +++ b/src/accel/als/solve.rs @@ -38,6 +38,7 @@ pub enum SolveError { /// Wrapper for LAPACK solver functions. #[derive(Clone, Copy)] +#[allow(clippy::upper_case_acronyms)] pub struct POSV { lapack_fn: LapackSPOSV, } @@ -105,8 +106,8 @@ impl POSV { } } -impl Into for SolveError { - fn into(self) -> PyErr { - PyRuntimeError::new_err(format!("LAPACK error: {}", self)) +impl From for PyErr { + fn from(val: SolveError) -> Self { + PyRuntimeError::new_err(format!("LAPACK error: {}", val)) } } diff --git a/src/accel/arrow/lists.rs b/src/accel/arrow/lists.rs index 345b733d7..0488fe8a4 100644 --- a/src/accel/arrow/lists.rs +++ b/src/accel/arrow/lists.rs @@ -47,6 +47,6 @@ impl ExtractListArray for LargeListArray { impl ExtractListArray for ListArray { fn extract_list_array(array: &dyn Array) -> Option { - array.as_any().downcast_ref::().map(Clone::clone) + array.as_any().downcast_ref::().cloned() } } diff --git a/src/accel/arrow/mod.rs b/src/accel/arrow/mod.rs index 634246f9e..e05885bbd 100644 --- a/src/accel/arrow/mod.rs +++ b/src/accel/arrow/mod.rs @@ -45,9 +45,9 @@ pub fn checked_array_ref<'array, T: Array + 'static>( ) } -pub fn checked_array<'array, E: ArrowPrimitiveType + 'static>( +pub fn checked_array( name: &str, - array: &'array dyn Array, + array: &dyn Array, ) -> PyResult> { if array.data_type().equals_datatype(&E::DATA_TYPE) { Ok(downcast_array(array)) diff --git a/src/accel/arrow/types/index_list.rs b/src/accel/arrow/types/index_list.rs index 17270157f..087af0c7f 100644 --- a/src/accel/arrow/types/index_list.rs +++ b/src/accel/arrow/types/index_list.rs @@ -49,7 +49,7 @@ impl TryFrom<&DataType> for SparseIndexListType { type Error = ArrowError; fn try_from(value: &DataType) -> Result { - Self::try_new(&value, ()) + Self::try_new(value, ()) } } diff --git a/src/accel/arrow/types/row.rs b/src/accel/arrow/types/row.rs index 1dfa68e46..2f8872495 100644 --- a/src/accel/arrow/types/row.rs +++ b/src/accel/arrow/types/row.rs @@ -52,7 +52,7 @@ impl TryFrom<&DataType> for SparseRowType { type Error = ArrowError; fn try_from(value: &DataType) -> Result { - Self::try_new(&value, ()) + Self::try_new(value, ()) } } @@ -126,7 +126,7 @@ impl ExtensionType for SparseRowType { ))); } - let idx_f = fields.get(0).unwrap(); + let idx_f = fields.first().unwrap(); let idx_name = idx_f.name(); let idx_t: SparseIndexType = idx_f.try_extension_type()?; diff --git a/src/accel/data/cooc.rs b/src/accel/data/cooc.rs index 56e416588..0e0d1cc14 100644 --- a/src/accel/data/cooc.rs +++ b/src/accel/data/cooc.rs @@ -201,10 +201,9 @@ fn count_cooc_parallel( let n = items.len(); for i in 0..n { - let ri = items[i as usize]; - for j in i..n { - let ci = items[j as usize]; - counts.crecord(ri, ci); + let ri = items[i]; + for ci in &items[i..n] { + counts.crecord(ri, *ci); } } cancel.advance(items.len()); @@ -239,12 +238,9 @@ fn compute_group_pointers(n_groups: usize, gvals: &[i32]) -> PyResult fn count_items(counts: &mut PC, items: &[i32]) { let n = items.len(); for i in 0..n { - let ri = items[i as usize]; - for j in (i + 1)..n { - if i != j { - let ci = items[j as usize]; - counts.record(ri, ci); - } + let ri = items[i]; + for ci in &items[(i + 1)..n] { + counts.record(ri, *ci); } } } diff --git a/src/accel/data/coordinates.rs b/src/accel/data/coordinates.rs index 7edd5357d..4413e9b47 100644 --- a/src/accel/data/coordinates.rs +++ b/src/accel/data/coordinates.rs @@ -231,7 +231,7 @@ impl ChunkIndex { fn hash_entry(chunks: &[Vec], ix: &ChunkIndex) -> u64 { let chunk = &chunks[ix.chunk_index()]; - hash_chunk_entry(&chunk, ix.item) + hash_chunk_entry(chunk, ix.item) } fn hash_chunk_entry(chunk: &[Int32Array], ri: u32) -> u64 { diff --git a/src/accel/data/index.rs b/src/accel/data/index.rs index bd276074d..1f3295958 100644 --- a/src/accel/data/index.rs +++ b/src/accel/data/index.rs @@ -62,7 +62,7 @@ impl IDIndex { fn from_data(data: PyArrowType) -> PyResult { let ids = make_array(data.0); let index: Box = match ids.data_type() { - DataType::Null if ids.len() == 0 => return Ok(Self::empty()), + DataType::Null if ids.is_empty() => return Ok(Self::empty()), DataType::Int16 => prim_tbl::(&ids)?, DataType::UInt16 => prim_tbl::(&ids)?, DataType::Int32 => prim_tbl::(&ids)?, diff --git a/src/accel/data/mod.rs b/src/accel/data/mod.rs index baaf1a86e..d9f1adc16 100644 --- a/src/accel/data/mod.rs +++ b/src/accel/data/mod.rs @@ -74,5 +74,5 @@ fn hash_array(arr: PyArrowType) -> PyResult { start += bsize; } - Ok(hex::encode(&hash.finalize())) + Ok(hex::encode(hash.finalize())) } diff --git a/src/accel/data/pairs/dense.rs b/src/accel/data/pairs/dense.rs index bd7dc5a94..212b595ed 100644 --- a/src/accel/data/pairs/dense.rs +++ b/src/accel/data/pairs/dense.rs @@ -22,6 +22,7 @@ pub struct DensePairCounter { } impl DensePairCounter { + #[allow(clippy::missing_transmute_annotations)] pub fn with_diagonal(n: usize, diagonal: bool) -> Self { DensePairCounter { n_items: n, @@ -67,10 +68,9 @@ impl PairCounter for DensePairCounter { let data: Vec = unsafe { transmute(data) }; let arr = Array1::from_vec(data); - let mat = arr - .into_shape_with_order((self.n_items, self.n_items)) - .expect("array reshape failed"); - mat + + arr.into_shape_with_order((self.n_items, self.n_items)) + .expect("array reshape failed") } } diff --git a/src/accel/data/pairs/symmetric.rs b/src/accel/data/pairs/symmetric.rs index 61ec16578..08aec52c5 100644 --- a/src/accel/data/pairs/symmetric.rs +++ b/src/accel/data/pairs/symmetric.rs @@ -26,6 +26,7 @@ pub struct SymmetricPairCounter { } impl SymmetricPairCounter { + #[allow(clippy::missing_transmute_annotations)] pub fn with_diagonal(n: usize, diagonal: bool) -> Self { let cap = arith_tot(n); SymmetricPairCounter { diff --git a/src/accel/data/sampling.rs b/src/accel/data/sampling.rs index c67694b98..20fa57d9f 100644 --- a/src/accel/data/sampling.rs +++ b/src/accel/data/sampling.rs @@ -16,6 +16,7 @@ use crate::data::CoordinateTable; /// Sample negative columns for given rows from a coordinate table. #[pyfunction] #[pyo3(signature=(coords, rows, n_cols, *, n=1, max_attempts=10, pop_weighted=false, seed))] +#[allow(clippy::too_many_arguments)] pub fn sample_negatives<'py>( py: Python<'py>, coords: &CoordinateTable, diff --git a/src/accel/data/scatter.rs b/src/accel/data/scatter.rs index 4f998d8e8..6d8d61657 100644 --- a/src/accel/data/scatter.rs +++ b/src/accel/data/scatter.rs @@ -86,8 +86,8 @@ fn scatter_empty_stype( } fn scatter_impl( - dst: &mut Vec, - dst_valid: &mut Vec, + dst: &mut [T::Native], + dst_valid: &mut [bool], idx: &PrimitiveArray, src: &PrimitiveArray, ) where diff --git a/src/accel/data/sorting.rs b/src/accel/data/sorting.rs index 26690a581..f707a79d2 100644 --- a/src/accel/data/sorting.rs +++ b/src/accel/data/sorting.rs @@ -51,11 +51,11 @@ pub(super) fn is_sorted_coo<'py>( let v1 = col1.value(i); let v2 = col2.value(i); let k = (v1, v2); - if let Some(lk) = last { - if k <= lk { - // found a key out-of-order, we're done - return Ok(false); - } + if let Some(lk) = last + && k <= lk + { + // found a key out-of-order, we're done + return Ok(false); } last = Some(k); } @@ -90,10 +90,10 @@ where let mut indices = Vec::with_capacity(scores.len()); for (i, v) in scores.iter().enumerate() { - if let Some(v) = v { - if !v.is_nan() { - indices.push(i as i32); - } + if let Some(v) = v + && !v.is_nan() + { + indices.push(i as i32); } } diff --git a/src/accel/data/transpose.rs b/src/accel/data/transpose.rs index 4a5a98d2f..5c2037abd 100644 --- a/src/accel/data/transpose.rs +++ b/src/accel/data/transpose.rs @@ -17,6 +17,7 @@ use crate::sparse::{CSR, CSRStructure, IxVar, csr_structure}; /// Transpose the structure of a CSR matrix. #[pyfunction] +#[allow(clippy::type_complexity)] pub fn transpose_csr( arr: PyArrowType, permute: bool, @@ -55,8 +56,7 @@ where let nnz = csr.nnz(); let mut row_ptrs = Vec::with_capacity(csr.n_cols + 1); row_ptrs.resize(csr.n_cols + 1, It::Native::from(0)); - let mut col_inds = Vec::with_capacity(nnz); - col_inds.resize(nnz, 0); + let mut col_inds = vec![0; nnz]; let mut permutation = if permute { let mut p = Vec::with_capacity(nnz); p.resize(nnz, It::Native::from(0)); diff --git a/src/accel/indirect/hashing/table.rs b/src/accel/indirect/hashing/table.rs index 8c11dcb48..17699c011 100644 --- a/src/accel/indirect/hashing/table.rs +++ b/src/accel/indirect/hashing/table.rs @@ -101,7 +101,7 @@ impl PositionLookup for IndirectHashTable { let res = self .table .find(hash, |jr| search.compare_with_entry(0, *jr)); - Ok(res.map(|ir| *ir)) + Ok(res.copied()) } fn lookup_array<'py>(&self, py: Python<'py>, val: Bound<'py, PyAny>) -> PyResult { diff --git a/src/accel/indirect/heap.rs b/src/accel/indirect/heap.rs index 240bb50d7..02fc10d6b 100644 --- a/src/accel/indirect/heap.rs +++ b/src/accel/indirect/heap.rs @@ -16,7 +16,7 @@ pub struct IndirectMinHeap V> { lookup: F, } -impl<'f, K: Copy, V: PartialOrd + Copy, F: Fn(K) -> V> IndirectMinHeap { +impl V> IndirectMinHeap { /// Create a new min heap with the specified size and value lookup function. pub fn create(size: usize, lookup: F) -> Self { IndirectMinHeap { @@ -45,12 +45,9 @@ impl<'f, K: Copy, V: PartialOrd + Copy, F: Fn(K) -> V> IndirectMinHeap } else { // heap is full, new value belongs — replace + adjust let kv = (self.lookup)(key); - match kv.partial_cmp(&self.value_for_position(0)) { - Some(Ordering::Greater) => { - self.keys[0] = key; - self.downheap(0, self.size); - } - _ => (), + if let Some(Ordering::Greater) = kv.partial_cmp(&self.value_for_position(0)) { + self.keys[0] = key; + self.downheap(0, self.size); } } } @@ -74,21 +71,15 @@ impl<'f, K: Copy, V: PartialOrd + Copy, F: Fn(K) -> V> IndirectMinHeap if left < lim { let lv = self.value_for_position(left); - match lv.partial_cmp(&mv) { - Some(Ordering::Less) => { - min = left; - mv = lv; - } - _ => (), + if let Some(Ordering::Less) = lv.partial_cmp(&mv) { + min = left; + mv = lv; } } if right < lim { let rv = self.value_for_position(right); - match rv.partial_cmp(&mv) { - Some(Ordering::Less) => { - min = right; - } - _ => (), + if let Some(Ordering::Less) = rv.partial_cmp(&mv) { + min = right; } } @@ -103,12 +94,9 @@ impl<'f, K: Copy, V: PartialOrd + Copy, F: Fn(K) -> V> IndirectMinHeap let parent = (pos - 1) / 2; let pv = self.value_for_position(parent); let mv = self.value_for_position(pos); - match pv.partial_cmp(&mv) { - Some(Ordering::Greater) => { - self.keys.swap(pos, parent); - self.upheap(parent); - } - _ => (), + if let Some(Ordering::Greater) = pv.partial_cmp(&mv) { + self.keys.swap(pos, parent); + self.upheap(parent); } } } diff --git a/src/accel/knn/accum.rs b/src/accel/knn/accum.rs index 9df40fb6e..e6ba3eab4 100644 --- a/src/accel/knn/accum.rs +++ b/src/accel/knn/accum.rs @@ -30,10 +30,8 @@ impl ScoreAccumulator { pub fn new_array(n: usize, active: &Int32Array) -> Vec> { // create accumulators for all items, and enable the targets let mut heaps: Vec> = vec![ScoreAccumulator::disabled(); n]; - for i in active.iter() { - if let Some(i) = i { - heaps[i as usize].enable() - } + for i in active.iter().flatten() { + heaps[i as usize].enable() } heaps } @@ -47,17 +45,13 @@ impl ScoreAccumulator { /// Enable a score accumulator. pub fn enable(&mut self) { - match self { - Self::Disabled => *self = Self::Empty, - _ => (), + if let Self::Disabled = self { + *self = Self::Empty } } pub fn enabled(&self) -> bool { - match self { - ScoreAccumulator::Disabled => false, - _ => true, - } + !matches!(self, ScoreAccumulator::Disabled) } pub fn len(&self) -> usize { @@ -170,6 +164,7 @@ impl PartialEq for AccEntry { impl Eq for AccEntry {} +#[allow(clippy::non_canonical_partial_ord_impl)] impl PartialOrd for AccEntry { fn partial_cmp(&self, other: &Self) -> Option { // reverse the ordering to make a min-heap diff --git a/src/accel/knn/item_score.rs b/src/accel/knn/item_score.rs index aecad2067..252734d74 100644 --- a/src/accel/knn/item_score.rs +++ b/src/accel/knn/item_score.rs @@ -51,7 +51,6 @@ pub fn score_explicit<'py>( let rv = *rv; let (sp, ep) = sims.extent(ri); for i in sp..ep { - let i = i as usize; let ti = sims.col_inds.value(i); let sim = sims.values.value(i); @@ -95,7 +94,6 @@ pub fn score_implicit<'py>( let ri = *ri as usize; let (sp, ep) = sims.extent(ri); for i in sp..ep { - let i = i as usize; let ti = sims.col_inds.value(i); let sim = sims.values.value(i); @@ -104,7 +102,7 @@ pub fn score_implicit<'py>( } } - let out = collect_items_summed(&heaps, &tgt_is, min_nbrs); + let out = collect_items_summed(&heaps, tgt_is, min_nbrs); let counts = collect_items_counts(&heaps, tgt_is); assert_eq!(out.len(), tgt_is.len()); diff --git a/src/accel/knn/item_train.rs b/src/accel/knn/item_train.rs index c7be187c2..a8cca8880 100644 --- a/src/accel/knn/item_train.rs +++ b/src/accel/knn/item_train.rs @@ -30,7 +30,7 @@ struct ItemSimTask { } #[pyfunction] -pub fn compute_similarities<'py>( +pub fn compute_similarities( ui_ratings: PyArrowType, iu_ratings: PyArrowType, shape: (usize, usize), @@ -110,13 +110,12 @@ fn sim_row( // loop over the users for i in r_start..r_end { - let u = iu_mat.col_inds.value(i as usize); - let r = iu_mat.values.value(i as usize); + let u = iu_mat.col_inds.value(i); + let r = iu_mat.values.value(i); let (u_start, u_end) = ui_mat.extent(u as usize); // loop over the users' items for j in u_start..u_end { - let j = j as usize; let other = ui_mat.col_inds.value(j) as usize; if other == row { continue; @@ -138,13 +137,13 @@ fn sim_row( .collect(); // truncate if needed - if let Some(limit) = save_nbrs { - if limit > 0 { - // sort by value number - sims.sort_by_key(|(_i, s)| Reverse(NotNan::new(*s).unwrap())); - sims.truncate(limit as usize); - sims.shrink_to_fit(); - } + if let Some(limit) = save_nbrs + && limit > 0 + { + // sort by value number + sims.sort_by_key(|(_i, s)| Reverse(NotNan::new(*s).unwrap())); + sims.truncate(limit as usize); + sims.shrink_to_fit(); } // sort by column number sims.sort_by_key(|(i, _s)| *i); diff --git a/src/accel/knn/user_score.rs b/src/accel/knn/user_score.rs index a3dfaca21..58d577f8e 100644 --- a/src/accel/knn/user_score.rs +++ b/src/accel/knn/user_score.rs @@ -19,7 +19,7 @@ use crate::{ use super::accum::{ScoreAccumulator, collect_items_averaged, collect_items_summed}; #[pyfunction] -pub fn user_score_items_explicit<'py>( +pub fn user_score_items_explicit( tgt_items: PyArrowType, nbr_rows: PyArrowType, nbr_sims: PyArrowType, @@ -47,7 +47,6 @@ pub fn user_score_items_explicit<'py>( for (nbr, sim) in iter { let (sp, ep) = rmat.extent(nbr as usize); for i in sp..ep { - let i = i as usize; let item = rmat.col_inds.value(i); let rating = rmat.values.value(i); heaps[item as usize].add_value(max_nbrs, sim, rating)?; @@ -60,7 +59,7 @@ pub fn user_score_items_explicit<'py>( } #[pyfunction] -pub fn user_score_items_implicit<'py>( +pub fn user_score_items_implicit( tgt_items: PyArrowType, nbr_rows: PyArrowType, nbr_sims: PyArrowType, @@ -88,7 +87,6 @@ pub fn user_score_items_implicit<'py>( for (nbr, sim) in iter { let (sp, ep) = rmat.extent(nbr as usize); for i in sp..ep { - let i = i as usize; let item = rmat.col_inds.value(i); heaps[item as usize].add_weight(max_nbrs, sim)?; } diff --git a/src/accel/slim/mod.rs b/src/accel/slim/mod.rs index ff689e759..535c997e3 100644 --- a/src/accel/slim/mod.rs +++ b/src/accel/slim/mod.rs @@ -55,7 +55,7 @@ pub fn register_slim(parent: &Bound<'_, PyModule>) -> PyResult<()> { /// This returns the **transpose** of the weight matrix, for convenient /// implementation. #[pyfunction] -fn train_slim<'py>( +fn train_slim( ui_matrix: PyArrowType, iu_matrix: PyArrowType, l1_reg: FP, @@ -107,10 +107,9 @@ impl AccelTaskImpl for SLIMTask { task.set_cancel(IterCancel::from_adapter(&adapter)); let chunks = py.detach(move || { - let chunks = maybe_fuse(adapter) + maybe_fuse(adapter) .map(|i| self.compute_column(i)) - .drive_unindexed(collector); - chunks + .drive_unindexed(collector) }); let result: Vec<_> = py.detach(move || { chunks @@ -154,7 +153,7 @@ impl SLIMTask { let mut weights = vec![0.0; self.n_items()]; let mut resids = vec![0.0; self.n_users()]; - let active = self.prep_resid_and_active(item, &i_users, &mut resids); + let active = self.prep_resid_and_active(item, i_users, &mut resids); // iteratively apply coordinate descent until we converge let n_iters = self.run_cd(item, &mut weights, &mut resids, &active); @@ -215,8 +214,8 @@ impl SLIMTask { } } - if let Some(k) = self.options.max_nbrs { - if k < active.len() { + match self.options.max_nbrs { + Some(k) if k < active.len() => { debug!("limiting column {} to {} active neighbors", item, k); // co-rating count is the numerator of cosine, so we just need // the denominators to sort the items & pick the top K. @@ -228,6 +227,7 @@ impl SLIMTask { }); active.resize(k, 0); } + _ => (), } active diff --git a/src/accel/sparse/csr.rs b/src/accel/sparse/csr.rs index 4fff12358..1ac85fcdd 100644 --- a/src/accel/sparse/csr.rs +++ b/src/accel/sparse/csr.rs @@ -50,6 +50,7 @@ pub struct CSRMatrix = i32> { /// Get the underlying Arrow array of rows. fn array(&self) -> &GenericListArray; diff --git a/src/accel/tasks/mod.rs b/src/accel/tasks/mod.rs index ae000a538..d466e0b98 100644 --- a/src/accel/tasks/mod.rs +++ b/src/accel/tasks/mod.rs @@ -6,9 +6,9 @@ //! Support for monitored accelerator tasks. -use std::{panic::catch_unwind, sync::Mutex}; +use std::sync::Mutex; -use pyo3::{IntoPyObjectExt, exceptions::PyRuntimeError, prelude::*, types::PyNone}; +use pyo3::{IntoPyObjectExt, prelude::*, types::PyNone}; mod atomic; mod progress; @@ -67,15 +67,13 @@ impl AccelTask { py: Python<'py>, pool: Option>, ) -> PyResult> { - let pool = pool - .map(|p| { - let ip = p.get_pool(); - if ip.is_none() { - warn!("attempted to invoke accelerator task with shut-down thread pool") - } - ip - }) - .flatten(); + let pool = pool.and_then(|p| { + let ip = p.get_pool(); + if ip.is_none() { + warn!("attempted to invoke accelerator task with shut-down thread pool") + } + ip + }); if let Some(pool) = pool { py.detach(|| {