Tue, 31 Dec 2024 09:25:45 -0500
New version of sliding.
0 | 1 | /*! |
2 | Spike merging heuristics for [`DiscreteMeasure`]s. | |
3 | ||
4 | This module primarily provides the [`SpikeMerging`] trait, and within it, | |
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`. | |
7 | */ | |
8 | ||
9 | use numeric_literals::replace_float_literals; | |
10 | use std::cmp::Ordering; | |
11 | use serde::{Serialize, Deserialize}; | |
12 | //use clap::builder::{PossibleValuesParser, PossibleValue}; | |
13 | use alg_tools::nanleast::NaNLeast; | |
14 | ||
15 | use crate::types::*; | |
16 | use super::delta::*; | |
17 | use super::discrete::*; | |
18 | ||
19 | /// Spike merging heuristic selection | |
20 | #[derive(Clone, Copy, Eq, PartialEq, Serialize, Deserialize, Debug)] | |
21 | #[allow(dead_code)] | |
22 | pub enum SpikeMergingMethod<F> { | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
23 | /// Try to merge spikes within a given radius of each other, averaging the location |
0 | 24 | HeuristicRadius(F), |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
25 | /// Try to merge spikes within a given radius of each other, attempting original locations |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
26 | HeuristicRadiusNoInterp(F), |
0 | 27 | /// No merging |
28 | None, | |
29 | } | |
30 | ||
31 | // impl<F : Float> SpikeMergingMethod<F> { | |
32 | // /// This is for [`clap`] to display command line help. | |
33 | // pub fn value_parser() -> PossibleValuesParser { | |
34 | // PossibleValuesParser::new([ | |
35 | // PossibleValue::new("none").help("No merging"), | |
36 | // PossibleValue::new("<radius>").help("Heuristic merging within indicated radius") | |
37 | // ]) | |
38 | // } | |
39 | // } | |
40 | ||
41 | impl<F : ClapFloat> std::fmt::Display for SpikeMergingMethod<F> { | |
42 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { | |
43 | match self { | |
44 | Self::None => write!(f, "none"), | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
45 | Self::HeuristicRadius(r) => write!(f, "i:{}", r), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
46 | Self::HeuristicRadiusNoInterp(r) => write!(f, "n:{}", r), |
0 | 47 | } |
48 | } | |
49 | } | |
50 | ||
51 | impl<F : ClapFloat> std::str::FromStr for SpikeMergingMethod<F> { | |
52 | type Err = F::Err; | |
53 | ||
54 | fn from_str(s: &str) -> Result<Self, Self::Err> { | |
55 | if s == "none" { | |
56 | Ok(Self::None) | |
57 | } else { | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
58 | let mut subs = s.split(':'); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
59 | match subs.next() { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
60 | None => Ok(Self::HeuristicRadius(F::from_str(s)?)), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
61 | Some(t) if t == "n" => match subs.next() { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
62 | None => Err(core::num::dec2flt::pfe_invalid()), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
63 | Some(v) => Ok(Self::HeuristicRadiusNoInterp(F::from_str(v)?)) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
64 | }, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
65 | Some(t) if t == "i" => match subs.next() { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
66 | None => Err(core::num::dec2flt::pfe_invalid()), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
67 | Some(v) => Ok(Self::HeuristicRadius(F::from_str(v)?)) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
68 | }, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
69 | Some(v) => Ok(Self::HeuristicRadius(F::from_str(v)?)) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
70 | } |
0 | 71 | } |
72 | } | |
73 | } | |
74 | ||
75 | #[replace_float_literals(F::cast_from(literal))] | |
76 | impl<F : Float> Default for SpikeMergingMethod<F> { | |
77 | fn default() -> Self { | |
78 | SpikeMergingMethod::HeuristicRadius(0.02) | |
79 | } | |
80 | } | |
81 | ||
82 | /// Trait for dimension-dependent implementation of heuristic peak merging strategies. | |
83 | pub trait SpikeMerging<F> { | |
84 | /// Attempt spike merging according to [`SpikeMerging`] method. | |
85 | /// | |
86 | /// Returns the last [`Some`] returned by the merging candidate acceptance decision closure | |
87 | /// `accept` if any merging is performed. The closure should accept as its only parameter a | |
88 | /// new candidate measure (it will generally be internally mutated `self`, although this is | |
89 | /// not guaranteed), and return [`None`] if the merge is accepted, and otherwise a [`Some`] of | |
90 | /// an arbitrary value. This method will return that value for the *last* accepted merge, or | |
91 | /// [`None`] if no merge was accepted. | |
92 | /// | |
93 | /// This method is stable with respect to spike locations: on merge, the weight of existing | |
94 | /// spikes is set to zero, and a new one inserted at the end of the spike vector. | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
95 | fn merge_spikes<G>(&mut self, method : SpikeMergingMethod<F>, accept : G) -> usize |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
96 | where G : FnMut(&'_ Self) -> bool { |
0 | 97 | match method { |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
98 | SpikeMergingMethod::HeuristicRadius(ρ) => |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
99 | self.do_merge_spikes_radius(ρ, true, accept), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
100 | SpikeMergingMethod::HeuristicRadiusNoInterp(ρ) => |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
101 | self.do_merge_spikes_radius(ρ, false, accept), |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
102 | SpikeMergingMethod::None => 0, |
0 | 103 | } |
104 | } | |
105 | ||
106 | /// Attempt to merge spikes based on a value and a fitness function. | |
107 | /// | |
108 | /// Calls [`SpikeMerging::merge_spikes`] with `accept` constructed from the composition of | |
109 | /// `value` and `fitness`, compared to initial fitness. Returns the last return value of `value` | |
110 | // for a merge accepted by `fitness`. If no merge was accepted, `value` applied to the initial | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
111 | /// `self` is returned. also the number of merges is returned; |
0 | 112 | fn merge_spikes_fitness<G, H, V, O>( |
113 | &mut self, | |
114 | method : SpikeMergingMethod<F>, | |
115 | value : G, | |
116 | fitness : H | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
117 | ) -> (V, usize) |
0 | 118 | where G : Fn(&'_ Self) -> V, |
119 | H : Fn(&'_ V) -> O, | |
120 | O : PartialOrd { | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
121 | let mut res = value(self); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
122 | let initial_fitness = fitness(&res); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
123 | let count = self.merge_spikes(method, |μ| { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
124 | res = value(μ); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
125 | fitness(&res) <= initial_fitness |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
126 | }); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
127 | (res, count) |
0 | 128 | } |
129 | ||
130 | /// Attempt to merge spikes that are within radius $ρ$ of each other (unspecified norm). | |
131 | /// | |
132 | /// This method implements [`SpikeMerging::merge_spikes`] for | |
133 | /// [`SpikeMergingMethod::HeuristicRadius`]. The closure `accept` and the return value are | |
134 | /// as for that method. | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
135 | fn do_merge_spikes_radius<G>(&mut self, ρ : F, interp : bool, accept : G) -> usize |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
136 | where G : FnMut(&'_ Self) -> bool; |
0 | 137 | } |
138 | ||
139 | #[replace_float_literals(F::cast_from(literal))] | |
140 | impl<F : Float, const N : usize> DiscreteMeasure<Loc<F, N>, F> { | |
141 | /// Attempts to merge spikes with indices `i` and `j`. | |
142 | /// | |
143 | /// This assumes that the weights of the two spikes have already been checked not to be zero. | |
144 | /// | |
145 | /// The parameter `res` points to the current “result” for [`SpikeMerging::merge_spikes`]. | |
146 | /// If the merge is accepted by `accept` returning a [`Some`], `res` will be replaced by its | |
147 | /// return value. | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
148 | /// |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
149 | /// Returns the index of `self.spikes` storing the new spike. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
150 | fn attempt_merge<G>( |
0 | 151 | &mut self, |
152 | i : usize, | |
153 | j : usize, | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
154 | interp : bool, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
155 | accept : &mut G |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
156 | ) -> Option<usize> |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
157 | where G : FnMut(&'_ Self) -> bool { |
0 | 158 | let &DeltaMeasure{ x : xi, α : αi } = &self.spikes[i]; |
159 | let &DeltaMeasure{ x : xj, α : αj } = &self.spikes[j]; | |
160 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
161 | if interp { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
162 | // Merge inplace |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
163 | self.spikes[i].α = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
164 | self.spikes[j].α = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
165 | let αia = αi.abs(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
166 | let αja = αj.abs(); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
167 | self.spikes.push(DeltaMeasure{ α : αi + αj, x : (xi * αia + xj * αja) / (αia + αja) }); |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
168 | if accept(self) { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
169 | Some(self.spikes.len()-1) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
170 | } else { |
0 | 171 | // Merge not accepted, restore modification |
172 | self.spikes[i].α = αi; | |
173 | self.spikes[j].α = αj; | |
174 | self.spikes.pop(); | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
175 | None |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
176 | } |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
177 | } else { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
178 | // Attempt merge inplace, first combination |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
179 | self.spikes[i].α = αi + αj; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
180 | self.spikes[j].α = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
181 | if accept(self) { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
182 | // Merge accepted |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
183 | Some(i) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
184 | } else { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
185 | // Attempt merge inplace, second combination |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
186 | self.spikes[i].α = 0.0; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
187 | self.spikes[j].α = αi + αj; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
188 | if accept(self) { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
189 | // Merge accepted |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
190 | Some(j) |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
191 | } else { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
192 | // Merge not accepted, restore modification |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
193 | self.spikes[i].α = αi; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
194 | self.spikes[j].α = αj; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
195 | None |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
196 | } |
0 | 197 | } |
198 | } | |
199 | } | |
200 | } | |
201 | ||
202 | /// Sorts a vector of indices into `slice` by `compare`. | |
203 | /// | |
204 | /// The closure `compare` operators on references to elements of `slice`. | |
205 | /// Returns the sorted vector of indices into `slice`. | |
206 | pub fn sort_indices_by<V, F>(slice : &[V], mut compare : F) -> Vec<usize> | |
207 | where F : FnMut(&V, &V) -> Ordering | |
208 | { | |
209 | let mut indices = Vec::from_iter(0..slice.len()); | |
210 | indices.sort_by(|&i, &j| compare(&slice[i], &slice[j])); | |
211 | indices | |
212 | } | |
213 | ||
214 | #[replace_float_literals(F::cast_from(literal))] | |
215 | impl<F : Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 1>, F> { | |
216 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
217 | fn do_merge_spikes_radius<G>( |
0 | 218 | &mut self, |
219 | ρ : F, | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
220 | interp : bool, |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
221 | mut accept : G |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
222 | ) -> usize |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
223 | where G : FnMut(&'_ Self) -> bool { |
0 | 224 | // Sort by coordinate into an indexing array. |
225 | let mut indices = sort_indices_by(&self.spikes, |&δ1, &δ2| { | |
226 | let &Loc([x1]) = &δ1.x; | |
227 | let &Loc([x2]) = &δ2.x; | |
228 | // nan-ignoring ordering of floats | |
229 | NaNLeast(x1).cmp(&NaNLeast(x2)) | |
230 | }); | |
231 | ||
232 | // Initialise result | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
233 | let mut count = 0; |
0 | 234 | |
235 | // Scan consecutive pairs and merge if close enough and accepted by `accept`. | |
236 | if indices.len() == 0 { | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
237 | return count |
0 | 238 | } |
239 | for k in 0..(indices.len()-1) { | |
240 | let i = indices[k]; | |
241 | let j = indices[k+1]; | |
242 | let &DeltaMeasure{ x : Loc([xi]), α : αi } = &self.spikes[i]; | |
243 | let &DeltaMeasure{ x : Loc([xj]), α : αj } = &self.spikes[j]; | |
244 | debug_assert!(xi <= xj); | |
245 | // If close enough, attempt merging | |
246 | if αi != 0.0 && αj != 0.0 && xj <= xi + ρ { | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
247 | if let Some(l) = self.attempt_merge(i, j, interp, &mut accept) { |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
248 | // For this to work (the debug_assert! to not trigger above), the new |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
249 | // coordinate produced by attempt_merge has to be at most xj. |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
250 | indices[k+1] = l; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
251 | count += 1 |
0 | 252 | } |
253 | } | |
254 | } | |
255 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
256 | count |
0 | 257 | } |
258 | } | |
259 | ||
260 | /// Orders `δ1` and `δ1` according to the first coordinate. | |
261 | fn compare_first_coordinate<F : Float>( | |
262 | δ1 : &DeltaMeasure<Loc<F, 2>, F>, | |
263 | δ2 : &DeltaMeasure<Loc<F, 2>, F> | |
264 | ) -> Ordering { | |
265 | let &Loc([x11, ..]) = &δ1.x; | |
266 | let &Loc([x21, ..]) = &δ2.x; | |
267 | // nan-ignoring ordering of floats | |
268 | NaNLeast(x11).cmp(&NaNLeast(x21)) | |
269 | } | |
270 | ||
271 | #[replace_float_literals(F::cast_from(literal))] | |
272 | impl<F : Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 2>, F> { | |
273 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
274 | fn do_merge_spikes_radius<G>(&mut self, ρ : F, interp : bool, mut accept : G) -> usize |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
275 | where G : FnMut(&'_ Self) -> bool { |
0 | 276 | // Sort by first coordinate into an indexing array. |
277 | let mut indices = sort_indices_by(&self.spikes, compare_first_coordinate); | |
278 | ||
279 | // Initialise result | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
280 | let mut count = 0; |
0 | 281 | let mut start_scan_2nd = 0; |
282 | ||
283 | // Scan in order | |
284 | if indices.len() == 0 { | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
285 | return count |
0 | 286 | } |
287 | for k in 0..indices.len()-1 { | |
288 | let i = indices[k]; | |
289 | let &DeltaMeasure{ x : Loc([xi1, xi2]), α : αi } = &self[i]; | |
290 | ||
291 | if αi == 0.0 { | |
292 | // Nothin to be done if the weight is already zero | |
293 | continue | |
294 | } | |
295 | ||
296 | let mut closest = None; | |
297 | ||
298 | // Scan for second spike. We start from `start_scan_2nd + 1` with `start_scan_2nd` | |
299 | // the smallest invalid merging index on the previous loop iteration, because a | |
300 | // the _closest_ mergeable spike might have index less than `k` in `indices`, and a | |
301 | // merge with it might have not been attempted with this spike if a different closer | |
302 | // spike was discovered based on the second coordinate. | |
303 | 'scan_2nd: for l in (start_scan_2nd+1)..indices.len() { | |
304 | if l == k { | |
305 | // Do not attempt to merge a spike with itself | |
306 | continue | |
307 | } | |
308 | let j = indices[l]; | |
309 | let &DeltaMeasure{ x : Loc([xj1, xj2]), α : αj } = &self[j]; | |
310 | ||
311 | if xj1 < xi1 - ρ { | |
312 | // Spike `j = indices[l]` has too low first coordinate. Update starting index | |
313 | // for next iteration, and continue scanning. | |
314 | start_scan_2nd = l; | |
315 | continue 'scan_2nd | |
316 | } else if xj1 > xi1 + ρ { | |
317 | // Break out: spike `j = indices[l]` has already too high first coordinate, no | |
318 | // more close enough spikes can be found due to the sorting of `indices`. | |
319 | break 'scan_2nd | |
320 | } | |
321 | ||
322 | // If also second coordinate is close enough, attempt merging if closer than | |
323 | // previously discovered mergeable spikes. | |
324 | let d2 = (xi2-xj2).abs(); | |
325 | if αj != 0.0 && d2 <= ρ { | |
326 | let r1 = xi1-xj1; | |
327 | let d = (d2*d2 + r1*r1).sqrt(); | |
328 | match closest { | |
329 | None => closest = Some((l, j, d)), | |
330 | Some((_, _, r)) if r > d => closest = Some((l, j, d)), | |
331 | _ => {}, | |
332 | } | |
333 | } | |
334 | } | |
335 | ||
336 | // Attempt merging closest close-enough spike | |
337 | if let Some((l, j, _)) = closest { | |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
338 | if let Some(n) = self.attempt_merge(i, j, interp, &mut accept) { |
0 | 339 | // If merge was succesfull, make new spike candidate for merging. |
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
340 | indices[l] = n; |
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
341 | count += 1; |
0 | 342 | let compare = |i, j| compare_first_coordinate(&self.spikes[i], |
343 | &self.spikes[j]); | |
344 | // Re-sort relevant range of indices | |
345 | if l < k { | |
346 | indices[l..k].sort_by(|&i, &j| compare(i, j)); | |
347 | } else { | |
348 | indices[k+1..=l].sort_by(|&i, &j| compare(i, j)); | |
349 | } | |
350 | } | |
351 | } | |
352 | } | |
353 | ||
34
efa60bc4f743
Radon FB + sliding improvements
Tuomo Valkonen <tuomov@iki.fi>
parents:
0
diff
changeset
|
354 | count |
0 | 355 | } |
356 | } | |
357 |