flatk/chunked/
sparse.rs

1//! This module defines functions specific to `Chunked<Sparse<_>>` types
2use super::*;
3use crate::select::{AsIndexSlice, AsIndexSliceMut};
4use crate::Sparse;
5
6impl<'a, S, T, I, O> Chunked<Sparse<S, T, I>, Offsets<O>>
7where
8    I: Dummy + AsIndexSliceMut,
9    T: Dummy + Set + View<'a>,
10    <T as View<'a>>::Type: Set + Clone + Dummy,
11    S: Dummy + Set + ViewMut<'a>,
12    <S as ViewMut<'a>>::Type: Set + SplitAt + Dummy + PermuteInPlace,
13    O: Clone + AsRef<[usize]>,
14{
15    /// Sort each sparse chunk by the source index.
16    ///
17    /// # Example
18    ///
19    /// ```
20    /// use flatk::*;
21    /// let sparse = Sparse::from_dim(vec![0,2,1,2,0], 4, vec![1,2,3,4,5]);
22    /// let mut chunked = Chunked::from_sizes(vec![3,2], sparse);
23    /// chunked.sort_chunks_by_index();
24    /// assert_eq!(chunked.storage(), &[1,3,2,5,4]);
25    /// assert_eq!(chunked.data().indices(), &[0,1,2,0,2]);
26    /// ```
27    pub fn sort_chunks_by_index(&'a mut self) {
28        let mut indices = vec![0; self.data.selection.indices.as_mut().len()];
29        let mut seen = vec![false; indices.len()];
30        let mut chunked_workspace = Chunked {
31            chunks: self.chunks.clone(),
32            data: (indices.as_mut_slice(), seen.as_mut_slice()),
33        };
34
35        for ((permutation, seen), mut chunk) in chunked_workspace.iter_mut().zip(self.iter_mut()) {
36            // Initialize permutation
37            (0..permutation.len())
38                .zip(permutation.iter_mut())
39                .for_each(|(i, out)| *out = i);
40
41            // Sort the permutation according to selection indices.
42            chunk.selection.indices.sort_indices(permutation);
43
44            // Apply the result of the sort (i.e. the permutation) to the whole chunk.
45            chunk.permute_in_place(permutation, seen);
46        }
47    }
48}
49
50impl<'a, S, T, I, O> Chunked<Sparse<S, T, I>, Offsets<O>>
51where
52    S: Storage + IntoOwned + Set + View<'a>,
53    S::Storage: Set,
54    <S as View<'a>>::Type: SplitAt + Dummy + Set,
55    T: View<'a> + Set + Clone,
56    <T as View<'a>>::Type: Set + Dummy + Clone,
57    I: AsIndexSlice,
58    O: Set + AsRef<[usize]>,
59{
60    /// Combine elements in each sorted chunk with the same index using the given function.
61    ///
62    /// Assuming that the chunks are sorted by index, this function will combine adjacent
63    /// elements with the same index into one element.
64    ///
65    /// # Example
66    ///
67    /// ```
68    /// use flatk::*;
69    /// let sparse = Sparse::from_dim(vec![0,2,1,1,2,0,2], 4, vec![1,2,3,4,5,6,7]);
70    /// let mut chunked = Chunked::from_sizes(vec![4,3], sparse);
71    /// chunked.sort_chunks_by_index();
72    /// let compressed = chunked.compressed(|a, b| *a += *b);
73    /// assert_eq!(compressed.view().offsets().into_inner(), &[0,3,5]);
74    /// assert_eq!(compressed.view().storage(), &[1,7,2,6,12]);
75    /// assert_eq!(compressed.view().data().indices(), &[0,1,2,0,2]);
76    /// ```
77    pub fn compressed<E>(
78        &'a self,
79        mut combine: impl FnMut(&mut E::Owned, E),
80    ) -> Chunked<Sparse<S::Owned, T>, Offsets>
81    where
82        <S as View<'a>>::Type: IntoIterator<Item = E>,
83        E: IntoOwned,
84        S::Owned: Set<Elem = E::Owned> + Default + Reserve + Push<E::Owned>,
85    {
86        self.pruned(&mut combine, |_, _, _| true, |_, _| {})
87    }
88}
89
90impl<'a, S, T, I, O> Chunked<Sparse<S, T, I>, Offsets<O>>
91where
92    S: Storage + IntoOwned + Set + View<'a>,
93    S::Storage: Set,
94    <S as View<'a>>::Type: SplitAt + Dummy + Set,
95    T: View<'a> + Set + Clone,
96    <T as View<'a>>::Type: Set + Dummy + Clone,
97    I: AsIndexSlice,
98    O: Set + AsRef<[usize]>,
99{
100    /// Prune elements according to a given predicate and combine them in each
101    /// sorted chunk with the same index.
102    ///
103    /// Assuming that the chunks are sorted by index, this function will combine adjacent
104    /// elements with the same index into one element.
105    ///
106    /// This is a more general version of `compressed` that allows you to prune unwanted elements.
107    /// In addition the `combine` and `keep` functions get an additional target
108    /// index where each element will be written to.
109    ///
110    /// # Examples
111    ///
112    /// A simple example.
113    ///
114    /// ```
115    /// use flatk::*;
116    /// let sparse = Sparse::from_dim(vec![0,2,1,1,2,0,2], 4, vec![1.0, 2.0, 0.1, 0.01, 5.0, 0.001, 7.0]);
117    /// let mut chunked = Chunked::from_sizes(vec![4,3], sparse);
118    /// chunked.sort_chunks_by_index();
119    /// let pruned = chunked.pruned(|a, b| *a += *b, |_, _, &val| val > 0.01, |_,_| {});
120    /// assert_eq!(pruned.view().offsets().into_inner(), &[0,3,4]);
121    /// assert_eq!(pruned.view().storage(), &[1.0, 0.11, 2.0, 12.0]); // 0.001 is pruned.
122    /// assert_eq!(pruned.view().data().indices(), &[0,1,2,2]);
123    /// ```
124    ///
125    /// The following example extends on the previous example but shows how one
126    /// may construct a mapping from original elements to the pruned output.
127    ///
128    /// ```
129    /// use flatk::*;
130    /// let indices = vec![0, 2, 1, 1, 2, 0, 2];
131    /// let num_indices = indices.len();
132    /// let sparse = Sparse::from_dim(indices, 4, vec![1.0, 2.0, 0.1, 0.01, 5.0, 0.001, 7.0]);
133    /// let mut chunked = Chunked::from_sizes(vec![4,3], sparse);
134    /// chunked.sort_chunks_by_index();
135    /// let mut mapping = vec![None; num_indices];
136    /// let pruned = chunked.pruned(|a, b| {
137    ///     *a += *b
138    /// }, |_, _, &val| {
139    ///     val > 0.01
140    /// }, |src, dst| mapping[src] = Some(dst));
141    ///
142    /// // As before, the resulting structure is pruned.
143    /// assert_eq!(pruned.view().offsets().into_inner(), &[0,3,4]);
144    /// assert_eq!(pruned.view().storage(), &[1.0, 0.11, 2.0, 12.0]); // 0.001 is pruned.
145    /// assert_eq!(pruned.view().data().indices(), &[0,1,2,2]);
146    /// assert_eq!(mapping, vec![Some(0), Some(1), Some(1), Some(2), None, Some(3), Some(3)]);
147    /// ```
148    pub fn pruned<E>(
149        &'a self,
150        mut combine: impl FnMut(&mut E::Owned, E),
151        mut keep: impl FnMut(usize, usize, &E::Owned) -> bool,
152        mut map: impl FnMut(usize, usize),
153    ) -> Chunked<Sparse<S::Owned, T>, Offsets>
154    where
155        <S as View<'a>>::Type: IntoIterator<Item = E>,
156        E: IntoOwned,
157        S::Owned: Set<Elem = E::Owned> + Default + Reserve + Push<E::Owned>,
158    {
159        // Initialize and allocate all output types.
160        let mut data: <S as IntoOwned>::Owned = Default::default();
161        data.reserve_with_storage(self.data.len(), self.storage().len());
162        let mut indices = Vec::new();
163        indices.reserve(self.data.selection.len());
164        let mut sparse = Sparse::new(
165            Select::new(indices, self.data.selection.target.clone()),
166            data,
167        );
168
169        let mut offsets = Vec::with_capacity(self.chunks.num_offsets());
170        offsets.push(0);
171
172        for (i, sparse_chunk) in self.iter().enumerate() {
173            sparse.extend_pruned(
174                sparse_chunk,
175                |_pos, a, b| combine(a, b),
176                |j, e| keep(i, j, e),
177                |src, dst| map(src + self.offset(i), dst),
178            );
179            offsets.push(sparse.len());
180        }
181
182        // Assemble the output type from constructed parts.
183        Chunked::from_offsets(offsets, sparse)
184    }
185}