5 the [`SpikeMerging::merge_spikes`] method. The trait is implemented on |
5 the [`SpikeMerging::merge_spikes`] method. The trait is implemented on |
6 [`DiscreteMeasure<Loc<F, N>, F>`]s in dimensions `N=1` and `N=2`. |
6 [`DiscreteMeasure<Loc<F, N>, F>`]s in dimensions `N=1` and `N=2`. |
7 */ |
7 */ |
8 |
8 |
9 use numeric_literals::replace_float_literals; |
9 use numeric_literals::replace_float_literals; |
|
10 use serde::{Deserialize, Serialize}; |
10 use std::cmp::Ordering; |
11 use std::cmp::Ordering; |
11 use serde::{Serialize, Deserialize}; |
|
12 //use clap::builder::{PossibleValuesParser, PossibleValue}; |
12 //use clap::builder::{PossibleValuesParser, PossibleValue}; |
13 use alg_tools::nanleast::NaNLeast; |
13 use alg_tools::nanleast::NaNLeast; |
14 |
14 |
15 use crate::types::*; |
|
16 use super::delta::*; |
15 use super::delta::*; |
17 use super::discrete::*; |
16 use super::discrete::*; |
|
17 use crate::types::*; |
18 |
18 |
19 /// Spike merging heuristic selection |
19 /// Spike merging heuristic selection |
20 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
20 #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] |
21 #[allow(dead_code)] |
21 #[allow(dead_code)] |
22 pub struct SpikeMergingMethod<F> { |
22 pub struct SpikeMergingMethod<F> { |
23 // Merging radius |
23 // Merging radius |
24 pub(crate) radius : F, |
24 pub(crate) radius: F, |
25 // Enabled |
25 // Enabled |
26 pub(crate) enabled : bool, |
26 pub(crate) enabled: bool, |
27 // Interpolate merged points |
27 // Interpolate merged points |
28 pub(crate) interp : bool, |
28 pub(crate) interp: bool, |
29 } |
29 } |
30 |
|
31 |
30 |
32 #[replace_float_literals(F::cast_from(literal))] |
31 #[replace_float_literals(F::cast_from(literal))] |
33 impl<F : Float> Default for SpikeMergingMethod<F> { |
32 impl<F: Float> Default for SpikeMergingMethod<F> { |
34 fn default() -> Self { |
33 fn default() -> Self { |
35 SpikeMergingMethod{ |
34 SpikeMergingMethod { |
36 radius : 0.01, |
35 radius: 0.01, |
37 enabled : false, |
36 enabled: false, |
38 interp : true, |
37 interp: true, |
39 } |
38 } |
40 } |
39 } |
41 } |
40 } |
42 |
41 |
43 /// Trait for dimension-dependent implementation of heuristic peak merging strategies. |
42 /// Trait for dimension-dependent implementation of heuristic peak merging strategies. |
162 |
169 |
163 /// Sorts a vector of indices into `slice` by `compare`. |
170 /// Sorts a vector of indices into `slice` by `compare`. |
164 /// |
171 /// |
165 /// The closure `compare` operators on references to elements of `slice`. |
172 /// The closure `compare` operators on references to elements of `slice`. |
166 /// Returns the sorted vector of indices into `slice`. |
173 /// Returns the sorted vector of indices into `slice`. |
167 pub fn sort_indices_by<V, F>(slice : &[V], mut compare : F) -> Vec<usize> |
174 pub fn sort_indices_by<V, F>(slice: &[V], mut compare: F) -> Vec<usize> |
168 where F : FnMut(&V, &V) -> Ordering |
175 where |
|
176 F: FnMut(&V, &V) -> Ordering, |
169 { |
177 { |
170 let mut indices = Vec::from_iter(0..slice.len()); |
178 let mut indices = Vec::from_iter(0..slice.len()); |
171 indices.sort_by(|&i, &j| compare(&slice[i], &slice[j])); |
179 indices.sort_by(|&i, &j| compare(&slice[i], &slice[j])); |
172 indices |
180 indices |
173 } |
181 } |
174 |
182 |
175 #[replace_float_literals(F::cast_from(literal))] |
183 #[replace_float_literals(F::cast_from(literal))] |
176 impl<F : Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 1>, F> { |
184 impl<F: Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 1>, F> { |
177 |
185 fn do_merge_spikes_radius<G>(&mut self, ρ: F, interp: bool, mut accept: G) -> usize |
178 fn do_merge_spikes_radius<G>( |
186 where |
179 &mut self, |
187 G: FnMut(&'_ Self) -> bool, |
180 ρ : F, |
188 { |
181 interp : bool, |
|
182 mut accept : G |
|
183 ) -> usize |
|
184 where G : FnMut(&'_ Self) -> bool { |
|
185 // Sort by coordinate into an indexing array. |
189 // Sort by coordinate into an indexing array. |
186 let mut indices = sort_indices_by(&self.spikes, |&δ1, &δ2| { |
190 let mut indices = sort_indices_by(&self.spikes, |&δ1, &δ2| { |
187 let &Loc([x1]) = &δ1.x; |
191 let &Loc([x1]) = &δ1.x; |
188 let &Loc([x2]) = &δ2.x; |
192 let &Loc([x2]) = &δ2.x; |
189 // nan-ignoring ordering of floats |
193 // nan-ignoring ordering of floats |
193 // Initialise result |
197 // Initialise result |
194 let mut count = 0; |
198 let mut count = 0; |
195 |
199 |
196 // Scan consecutive pairs and merge if close enough and accepted by `accept`. |
200 // Scan consecutive pairs and merge if close enough and accepted by `accept`. |
197 if indices.len() == 0 { |
201 if indices.len() == 0 { |
198 return count |
202 return count; |
199 } |
203 } |
200 for k in 0..(indices.len()-1) { |
204 for k in 0..(indices.len() - 1) { |
201 let i = indices[k]; |
205 let i = indices[k]; |
202 let j = indices[k+1]; |
206 let j = indices[k + 1]; |
203 let &DeltaMeasure{ x : Loc([xi]), α : αi } = &self.spikes[i]; |
207 let &DeltaMeasure { |
204 let &DeltaMeasure{ x : Loc([xj]), α : αj } = &self.spikes[j]; |
208 x: Loc([xi]), |
|
209 α: αi, |
|
210 } = &self.spikes[i]; |
|
211 let &DeltaMeasure { |
|
212 x: Loc([xj]), |
|
213 α: αj, |
|
214 } = &self.spikes[j]; |
205 debug_assert!(xi <= xj); |
215 debug_assert!(xi <= xj); |
206 // If close enough, attempt merging |
216 // If close enough, attempt merging |
207 if αi != 0.0 && αj != 0.0 && xj <= xi + ρ { |
217 if αi != 0.0 && αj != 0.0 && xj <= xi + ρ { |
208 if let Some(l) = self.attempt_merge(i, j, interp, &mut accept) { |
218 if let Some(l) = self.attempt_merge(i, j, interp, &mut accept) { |
209 // For this to work (the debug_assert! to not trigger above), the new |
219 // For this to work (the debug_assert! to not trigger above), the new |
210 // coordinate produced by attempt_merge has to be at most xj. |
220 // coordinate produced by attempt_merge has to be at most xj. |
211 indices[k+1] = l; |
221 indices[k + 1] = l; |
212 count += 1 |
222 count += 1 |
213 } |
223 } |
214 } |
224 } |
215 } |
225 } |
216 |
226 |
217 count |
227 count |
218 } |
228 } |
219 } |
229 } |
220 |
230 |
221 /// Orders `δ1` and `δ1` according to the first coordinate. |
231 /// Orders `δ1` and `δ1` according to the first coordinate. |
222 fn compare_first_coordinate<F : Float>( |
232 fn compare_first_coordinate<F: Float>( |
223 δ1 : &DeltaMeasure<Loc<F, 2>, F>, |
233 δ1: &DeltaMeasure<Loc<F, 2>, F>, |
224 δ2 : &DeltaMeasure<Loc<F, 2>, F> |
234 δ2: &DeltaMeasure<Loc<F, 2>, F>, |
225 ) -> Ordering { |
235 ) -> Ordering { |
226 let &Loc([x11, ..]) = &δ1.x; |
236 let &Loc([x11, ..]) = &δ1.x; |
227 let &Loc([x21, ..]) = &δ2.x; |
237 let &Loc([x21, ..]) = &δ2.x; |
228 // nan-ignoring ordering of floats |
238 // nan-ignoring ordering of floats |
229 NaNLeast(x11).cmp(&NaNLeast(x21)) |
239 NaNLeast(x11).cmp(&NaNLeast(x21)) |
230 } |
240 } |
231 |
241 |
232 #[replace_float_literals(F::cast_from(literal))] |
242 #[replace_float_literals(F::cast_from(literal))] |
233 impl<F : Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 2>, F> { |
243 impl<F: Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 2>, F> { |
234 |
244 fn do_merge_spikes_radius<G>(&mut self, ρ: F, interp: bool, mut accept: G) -> usize |
235 fn do_merge_spikes_radius<G>(&mut self, ρ : F, interp : bool, mut accept : G) -> usize |
245 where |
236 where G : FnMut(&'_ Self) -> bool { |
246 G: FnMut(&'_ Self) -> bool, |
|
247 { |
237 // Sort by first coordinate into an indexing array. |
248 // Sort by first coordinate into an indexing array. |
238 let mut indices = sort_indices_by(&self.spikes, compare_first_coordinate); |
249 let mut indices = sort_indices_by(&self.spikes, compare_first_coordinate); |
239 |
250 |
240 // Initialise result |
251 // Initialise result |
241 let mut count = 0; |
252 let mut count = 0; |
242 let mut start_scan_2nd = 0; |
253 let mut start_scan_2nd = 0; |
243 |
254 |
244 // Scan in order |
255 // Scan in order |
245 if indices.len() == 0 { |
256 if indices.len() == 0 { |
246 return count |
257 return count; |
247 } |
258 } |
248 for k in 0..indices.len()-1 { |
259 for k in 0..indices.len() - 1 { |
249 let i = indices[k]; |
260 let i = indices[k]; |
250 let &DeltaMeasure{ x : Loc([xi1, xi2]), α : αi } = &self[i]; |
261 let &DeltaMeasure { |
|
262 x: Loc([xi1, xi2]), |
|
263 α: αi, |
|
264 } = &self[i]; |
251 |
265 |
252 if αi == 0.0 { |
266 if αi == 0.0 { |
253 // Nothin to be done if the weight is already zero |
267 // Nothin to be done if the weight is already zero |
254 continue |
268 continue; |
255 } |
269 } |
256 |
270 |
257 let mut closest = None; |
271 let mut closest = None; |
258 |
272 |
259 // Scan for second spike. We start from `start_scan_2nd + 1` with `start_scan_2nd` |
273 // Scan for second spike. We start from `start_scan_2nd + 1` with `start_scan_2nd` |
260 // the smallest invalid merging index on the previous loop iteration, because a |
274 // the smallest invalid merging index on the previous loop iteration, because a |
261 // the _closest_ mergeable spike might have index less than `k` in `indices`, and a |
275 // the _closest_ mergeable spike might have index less than `k` in `indices`, and a |
262 // merge with it might have not been attempted with this spike if a different closer |
276 // merge with it might have not been attempted with this spike if a different closer |
263 // spike was discovered based on the second coordinate. |
277 // spike was discovered based on the second coordinate. |
264 'scan_2nd: for l in (start_scan_2nd+1)..indices.len() { |
278 'scan_2nd: for l in (start_scan_2nd + 1)..indices.len() { |
265 if l == k { |
279 if l == k { |
266 // Do not attempt to merge a spike with itself |
280 // Do not attempt to merge a spike with itself |
267 continue |
281 continue; |
268 } |
282 } |
269 let j = indices[l]; |
283 let j = indices[l]; |
270 let &DeltaMeasure{ x : Loc([xj1, xj2]), α : αj } = &self[j]; |
284 let &DeltaMeasure { |
|
285 x: Loc([xj1, xj2]), |
|
286 α: αj, |
|
287 } = &self[j]; |
271 |
288 |
272 if xj1 < xi1 - ρ { |
289 if xj1 < xi1 - ρ { |
273 // Spike `j = indices[l]` has too low first coordinate. Update starting index |
290 // Spike `j = indices[l]` has too low first coordinate. Update starting index |
274 // for next iteration, and continue scanning. |
291 // for next iteration, and continue scanning. |
275 start_scan_2nd = l; |
292 start_scan_2nd = l; |
276 continue 'scan_2nd |
293 continue 'scan_2nd; |
277 } else if xj1 > xi1 + ρ { |
294 } else if xj1 > xi1 + ρ { |
278 // Break out: spike `j = indices[l]` has already too high first coordinate, no |
295 // Break out: spike `j = indices[l]` has already too high first coordinate, no |
279 // more close enough spikes can be found due to the sorting of `indices`. |
296 // more close enough spikes can be found due to the sorting of `indices`. |
280 break 'scan_2nd |
297 break 'scan_2nd; |
281 } |
298 } |
282 |
299 |
283 // If also second coordinate is close enough, attempt merging if closer than |
300 // If also second coordinate is close enough, attempt merging if closer than |
284 // previously discovered mergeable spikes. |
301 // previously discovered mergeable spikes. |
285 let d2 = (xi2-xj2).abs(); |
302 let d2 = (xi2 - xj2).abs(); |
286 if αj != 0.0 && d2 <= ρ { |
303 if αj != 0.0 && d2 <= ρ { |
287 let r1 = xi1-xj1; |
304 let r1 = xi1 - xj1; |
288 let d = (d2*d2 + r1*r1).sqrt(); |
305 let d = (d2 * d2 + r1 * r1).sqrt(); |
289 match closest { |
306 match closest { |
290 None => closest = Some((l, j, d)), |
307 None => closest = Some((l, j, d)), |
291 Some((_, _, r)) if r > d => closest = Some((l, j, d)), |
308 Some((_, _, r)) if r > d => closest = Some((l, j, d)), |
292 _ => {}, |
309 _ => {} |
293 } |
310 } |
294 } |
311 } |
295 } |
312 } |
296 |
313 |
297 // Attempt merging closest close-enough spike |
314 // Attempt merging closest close-enough spike |
298 if let Some((l, j, _)) = closest { |
315 if let Some((l, j, _)) = closest { |
299 if let Some(n) = self.attempt_merge(i, j, interp, &mut accept) { |
316 if let Some(n) = self.attempt_merge(i, j, interp, &mut accept) { |
300 // If merge was succesfull, make new spike candidate for merging. |
317 // If merge was succesfull, make new spike candidate for merging. |
301 indices[l] = n; |
318 indices[l] = n; |
302 count += 1; |
319 count += 1; |
303 let compare = |i, j| compare_first_coordinate(&self.spikes[i], |
320 let compare = |i, j| compare_first_coordinate(&self.spikes[i], &self.spikes[j]); |
304 &self.spikes[j]); |
|
305 // Re-sort relevant range of indices |
321 // Re-sort relevant range of indices |
306 if l < k { |
322 if l < k { |
307 indices[l..k].sort_by(|&i, &j| compare(i, j)); |
323 indices[l..k].sort_by(|&i, &j| compare(i, j)); |
308 } else { |
324 } else { |
309 indices[k+1..=l].sort_by(|&i, &j| compare(i, j)); |
325 indices[k + 1..=l].sort_by(|&i, &j| compare(i, j)); |
310 } |
326 } |
311 } |
327 } |
312 } |
328 } |
313 } |
329 } |
314 |
330 |
315 count |
331 count |
316 } |
332 } |
317 } |
333 } |
318 |
|