73 /// new candidate measure (it will generally be internally mutated `self`, although this is |
48 /// new candidate measure (it will generally be internally mutated `self`, although this is |
74 /// not guaranteed), and return [`None`] if the merge is accepted, and otherwise a [`Some`] of |
49 /// not guaranteed), and return [`None`] if the merge is accepted, and otherwise a [`Some`] of |
75 /// an arbitrary value. This method will return that value for the *last* accepted merge, or |
50 /// an arbitrary value. This method will return that value for the *last* accepted merge, or |
76 /// [`None`] if no merge was accepted. |
51 /// [`None`] if no merge was accepted. |
77 /// |
52 /// |
78 /// This method is stable with respect to spike locations: on merge, the weight of existing |
53 /// This method is stable with respect to spike locations: on merge, the weights of existing |
79 /// spikes is set to zero, and a new one inserted at the end of the spike vector. |
54 /// removed spikes is set to zero, new ones inserted at the end of the spike vector. |
80 fn merge_spikes<G, V>(&mut self, method : SpikeMergingMethod<F>, accept : G) -> Option<V> |
55 /// They merge may also be performed by increasing the weights of the existing spikes, |
81 where G : Fn(&'_ Self) -> Option<V> { |
56 /// without inserting new spikes. |
82 match method { |
57 fn merge_spikes<G>(&mut self, method: SpikeMergingMethod<F>, accept: G) -> usize |
83 SpikeMergingMethod::HeuristicRadius(ρ) => self.do_merge_spikes_radius(ρ, accept), |
58 where |
84 SpikeMergingMethod::None => None, |
59 G: FnMut(&'_ Self) -> bool, |
|
60 { |
|
61 if method.enabled { |
|
62 self.do_merge_spikes_radius(method.radius, method.interp, accept) |
|
63 } else { |
|
64 0 |
85 } |
65 } |
86 } |
66 } |
87 |
67 |
88 /// Attempt to merge spikes based on a value and a fitness function. |
68 /// Attempt to merge spikes based on a value and a fitness function. |
89 /// |
69 /// |
90 /// Calls [`SpikeMerging::merge_spikes`] with `accept` constructed from the composition of |
70 /// Calls [`SpikeMerging::merge_spikes`] with `accept` constructed from the composition of |
91 /// `value` and `fitness`, compared to initial fitness. Returns the last return value of `value` |
71 /// `value` and `fitness`, compared to initial fitness. Returns the last return value of `value` |
92 // for a merge accepted by `fitness`. If no merge was accepted, `value` applied to the initial |
72 // for a merge accepted by `fitness`. If no merge was accepted, `value` applied to the initial |
93 /// `self` is returned. |
73 /// `self` is returned. also the number of merges is returned; |
94 fn merge_spikes_fitness<G, H, V, O>( |
74 fn merge_spikes_fitness<G, H, V, O>( |
95 &mut self, |
75 &mut self, |
96 method : SpikeMergingMethod<F>, |
76 method: SpikeMergingMethod<F>, |
97 value : G, |
77 value: G, |
98 fitness : H |
78 fitness: H, |
99 ) -> V |
79 ) -> (V, usize) |
100 where G : Fn(&'_ Self) -> V, |
80 where |
101 H : Fn(&'_ V) -> O, |
81 G: Fn(&'_ Self) -> V, |
102 O : PartialOrd { |
82 H: Fn(&'_ V) -> O, |
103 let initial_res = value(self); |
83 O: PartialOrd, |
104 let initial_fitness = fitness(&initial_res); |
84 { |
105 self.merge_spikes(method, |μ| { |
85 let mut res = value(self); |
106 let res = value(μ); |
86 let initial_fitness = fitness(&res); |
107 (fitness(&res) <= initial_fitness).then_some(res) |
87 let count = self.merge_spikes(method, |μ| { |
108 }).unwrap_or(initial_res) |
88 res = value(μ); |
|
89 fitness(&res) <= initial_fitness |
|
90 }); |
|
91 (res, count) |
109 } |
92 } |
110 |
93 |
111 /// Attempt to merge spikes that are within radius $ρ$ of each other (unspecified norm). |
94 /// Attempt to merge spikes that are within radius $ρ$ of each other (unspecified norm). |
112 /// |
95 /// |
113 /// This method implements [`SpikeMerging::merge_spikes`] for |
96 /// This method implements [`SpikeMerging::merge_spikes`]. |
114 /// [`SpikeMergingMethod::HeuristicRadius`]. The closure `accept` and the return value are |
97 fn do_merge_spikes_radius<G>(&mut self, ρ: F, interp: bool, accept: G) -> usize |
115 /// as for that method. |
98 where |
116 fn do_merge_spikes_radius<G, V>(&mut self, ρ : F, accept : G) -> Option<V> |
99 G: FnMut(&'_ Self) -> bool; |
117 where G : Fn(&'_ Self) -> Option<V>; |
|
118 } |
100 } |
119 |
101 |
120 #[replace_float_literals(F::cast_from(literal))] |
102 #[replace_float_literals(F::cast_from(literal))] |
121 impl<F : Float, const N : usize> DiscreteMeasure<Loc<F, N>, F> { |
103 impl<F: Float, const N: usize> DiscreteMeasure<Loc<F, N>, F> { |
122 /// Attempts to merge spikes with indices `i` and `j`. |
104 /// Attempts to merge spikes with indices `i` and `j`. |
123 /// |
105 /// |
124 /// This assumes that the weights of the two spikes have already been checked not to be zero. |
106 /// This assumes that the weights of the two spikes have already been checked not to be zero. |
125 /// |
107 /// |
126 /// The parameter `res` points to the current “result” for [`SpikeMerging::merge_spikes`]. |
108 /// The parameter `res` points to the current “result” for [`SpikeMerging::merge_spikes`]. |
127 /// If the merge is accepted by `accept` returning a [`Some`], `res` will be replaced by its |
109 /// If the merge is accepted by `accept` returning a [`Some`], `res` will be replaced by its |
128 /// return value. |
110 /// return value. |
129 fn attempt_merge<G, V>( |
111 /// |
|
112 /// Returns the index of `self.spikes` storing the new spike. |
|
113 fn attempt_merge<G>( |
130 &mut self, |
114 &mut self, |
131 res : &mut Option<V>, |
115 i: usize, |
132 i : usize, |
116 j: usize, |
133 j : usize, |
117 interp: bool, |
134 accept : &G |
118 accept: &mut G, |
135 ) -> bool |
119 ) -> Option<usize> |
136 where G : Fn(&'_ Self) -> Option<V> { |
120 where |
137 let &DeltaMeasure{ x : xi, α : αi } = &self.spikes[i]; |
121 G: FnMut(&'_ Self) -> bool, |
138 let &DeltaMeasure{ x : xj, α : αj } = &self.spikes[j]; |
122 { |
139 |
123 let &DeltaMeasure { x: xi, α: αi } = &self.spikes[i]; |
140 // Merge inplace |
124 let &DeltaMeasure { x: xj, α: αj } = &self.spikes[j]; |
141 self.spikes[i].α = 0.0; |
125 |
142 self.spikes[j].α = 0.0; |
126 if interp { |
143 //self.spikes.push(DeltaMeasure{ α : αi + αj, x : (xi + xj)/2.0 }); |
127 // Merge inplace |
144 self.spikes.push(DeltaMeasure{ α : αi + αj, x : (xi * αi + xj * αj) / (αi + αj) }); |
128 self.spikes[i].α = 0.0; |
145 match accept(self) { |
129 self.spikes[j].α = 0.0; |
146 some@Some(..) => { |
130 let αia = αi.abs(); |
147 // Merge accepted, update our return value |
131 let αja = αj.abs(); |
148 *res = some; |
132 self.spikes.push(DeltaMeasure { |
149 // On next iteration process the newly merged spike. |
133 α: αi + αj, |
150 //indices[k+1] = self.spikes.len() - 1; |
134 x: (xi * αia + xj * αja) / (αia + αja), |
151 true |
135 }); |
152 }, |
136 if accept(self) { |
153 None => { |
137 Some(self.spikes.len() - 1) |
|
138 } else { |
154 // Merge not accepted, restore modification |
139 // Merge not accepted, restore modification |
155 self.spikes[i].α = αi; |
140 self.spikes[i].α = αi; |
156 self.spikes[j].α = αj; |
141 self.spikes[j].α = αj; |
157 self.spikes.pop(); |
142 self.spikes.pop(); |
158 false |
143 None |
159 } |
144 } |
160 } |
145 } else { |
161 } |
146 // Attempt merge inplace, first combination |
162 |
147 self.spikes[i].α = αi + αj; |
163 /* |
148 self.spikes[j].α = 0.0; |
164 /// Attempts to merge spikes with indices i and j, acceptance through a delta. |
149 if accept(self) { |
165 fn attempt_merge_change<G, V>( |
150 // Merge accepted |
166 &mut self, |
151 Some(i) |
167 res : &mut Option<V>, |
152 } else { |
168 i : usize, |
153 // Attempt merge inplace, second combination |
169 j : usize, |
|
170 accept_change : &G |
|
171 ) -> bool |
|
172 where G : Fn(&'_ Self) -> Option<V> { |
|
173 let &DeltaMeasure{ x : xi, α : αi } = &self.spikes[i]; |
|
174 let &DeltaMeasure{ x : xj, α : αj } = &self.spikes[j]; |
|
175 let δ = DeltaMeasure{ α : αi + αj, x : (xi + xj)/2.0 }; |
|
176 let λ = [-self.spikes[i], -self.spikes[j], δ.clone()].into(); |
|
177 |
|
178 match accept_change(&λ) { |
|
179 some@Some(..) => { |
|
180 // Merge accepted, update our return value |
|
181 *res = some; |
|
182 self.spikes[i].α = 0.0; |
154 self.spikes[i].α = 0.0; |
183 self.spikes[j].α = 0.0; |
155 self.spikes[j].α = αi + αj; |
184 self.spikes.push(δ); |
156 if accept(self) { |
185 true |
157 // Merge accepted |
186 }, |
158 Some(j) |
187 None => { |
159 } else { |
188 false |
160 // Merge not accepted, restore modification |
189 } |
161 self.spikes[i].α = αi; |
190 } |
162 self.spikes[j].α = αj; |
191 }*/ |
163 None |
192 |
164 } |
|
165 } |
|
166 } |
|
167 } |
193 } |
168 } |
194 |
169 |
195 /// Sorts a vector of indices into `slice` by `compare`. |
170 /// Sorts a vector of indices into `slice` by `compare`. |
196 /// |
171 /// |
197 /// The closure `compare` operators on references to elements of `slice`. |
172 /// The closure `compare` operators on references to elements of `slice`. |
198 /// Returns the sorted vector of indices into `slice`. |
173 /// Returns the sorted vector of indices into `slice`. |
199 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> |
200 where F : FnMut(&V, &V) -> Ordering |
175 where |
|
176 F: FnMut(&V, &V) -> Ordering, |
201 { |
177 { |
202 let mut indices = Vec::from_iter(0..slice.len()); |
178 let mut indices = Vec::from_iter(0..slice.len()); |
203 indices.sort_by(|&i, &j| compare(&slice[i], &slice[j])); |
179 indices.sort_by(|&i, &j| compare(&slice[i], &slice[j])); |
204 indices |
180 indices |
205 } |
181 } |
206 |
182 |
207 #[replace_float_literals(F::cast_from(literal))] |
183 #[replace_float_literals(F::cast_from(literal))] |
208 impl<F : Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 1>, F> { |
184 impl<F: Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 1>, F> { |
209 |
185 fn do_merge_spikes_radius<G>(&mut self, ρ: F, interp: bool, mut accept: G) -> usize |
210 fn do_merge_spikes_radius<G, V>( |
186 where |
211 &mut self, |
187 G: FnMut(&'_ Self) -> bool, |
212 ρ : F, |
188 { |
213 accept : G |
|
214 ) -> Option<V> |
|
215 where G : Fn(&'_ Self) -> Option<V> { |
|
216 // Sort by coordinate into an indexing array. |
189 // Sort by coordinate into an indexing array. |
217 let mut indices = sort_indices_by(&self.spikes, |&δ1, &δ2| { |
190 let mut indices = sort_indices_by(&self.spikes, |&δ1, &δ2| { |
218 let &Loc([x1]) = &δ1.x; |
191 let &Loc([x1]) = &δ1.x; |
219 let &Loc([x2]) = &δ2.x; |
192 let &Loc([x2]) = &δ2.x; |
220 // nan-ignoring ordering of floats |
193 // nan-ignoring ordering of floats |
221 NaNLeast(x1).cmp(&NaNLeast(x2)) |
194 NaNLeast(x1).cmp(&NaNLeast(x2)) |
222 }); |
195 }); |
223 |
196 |
224 // Initialise result |
197 // Initialise result |
225 let mut res = None; |
198 let mut count = 0; |
226 |
199 |
227 // 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`. |
228 if indices.len() == 0 { |
201 if indices.len() == 0 { |
229 return res |
202 return count; |
230 } |
203 } |
231 for k in 0..(indices.len()-1) { |
204 for k in 0..(indices.len() - 1) { |
232 let i = indices[k]; |
205 let i = indices[k]; |
233 let j = indices[k+1]; |
206 let j = indices[k + 1]; |
234 let &DeltaMeasure{ x : Loc([xi]), α : αi } = &self.spikes[i]; |
207 let &DeltaMeasure { |
235 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]; |
236 debug_assert!(xi <= xj); |
215 debug_assert!(xi <= xj); |
237 // If close enough, attempt merging |
216 // If close enough, attempt merging |
238 if αi != 0.0 && αj != 0.0 && xj <= xi + ρ { |
217 if αi != 0.0 && αj != 0.0 && xj <= xi + ρ { |
239 if self.attempt_merge(&mut res, i, j, &accept) { |
218 if let Some(l) = self.attempt_merge(i, j, interp, &mut accept) { |
240 indices[k+1] = self.spikes.len() - 1; |
219 // For this to work (the debug_assert! to not trigger above), the new |
241 } |
220 // coordinate produced by attempt_merge has to be at most xj. |
242 } |
221 indices[k + 1] = l; |
243 } |
222 count += 1 |
244 |
223 } |
245 res |
224 } |
|
225 } |
|
226 |
|
227 count |
246 } |
228 } |
247 } |
229 } |
248 |
230 |
249 /// Orders `δ1` and `δ1` according to the first coordinate. |
231 /// Orders `δ1` and `δ1` according to the first coordinate. |
250 fn compare_first_coordinate<F : Float>( |
232 fn compare_first_coordinate<F: Float>( |
251 δ1 : &DeltaMeasure<Loc<F, 2>, F>, |
233 δ1: &DeltaMeasure<Loc<F, 2>, F>, |
252 δ2 : &DeltaMeasure<Loc<F, 2>, F> |
234 δ2: &DeltaMeasure<Loc<F, 2>, F>, |
253 ) -> Ordering { |
235 ) -> Ordering { |
254 let &Loc([x11, ..]) = &δ1.x; |
236 let &Loc([x11, ..]) = &δ1.x; |
255 let &Loc([x21, ..]) = &δ2.x; |
237 let &Loc([x21, ..]) = &δ2.x; |
256 // nan-ignoring ordering of floats |
238 // nan-ignoring ordering of floats |
257 NaNLeast(x11).cmp(&NaNLeast(x21)) |
239 NaNLeast(x11).cmp(&NaNLeast(x21)) |
258 } |
240 } |
259 |
241 |
260 #[replace_float_literals(F::cast_from(literal))] |
242 #[replace_float_literals(F::cast_from(literal))] |
261 impl<F : Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 2>, F> { |
243 impl<F: Float> SpikeMerging<F> for DiscreteMeasure<Loc<F, 2>, F> { |
262 |
244 fn do_merge_spikes_radius<G>(&mut self, ρ: F, interp: bool, mut accept: G) -> usize |
263 fn do_merge_spikes_radius<G, V>(&mut self, ρ : F, accept : G) -> Option<V> |
245 where |
264 where G : Fn(&'_ Self) -> Option<V> { |
246 G: FnMut(&'_ Self) -> bool, |
|
247 { |
265 // Sort by first coordinate into an indexing array. |
248 // Sort by first coordinate into an indexing array. |
266 let mut indices = sort_indices_by(&self.spikes, compare_first_coordinate); |
249 let mut indices = sort_indices_by(&self.spikes, compare_first_coordinate); |
267 |
250 |
268 // Initialise result |
251 // Initialise result |
269 let mut res = None; |
252 let mut count = 0; |
270 let mut start_scan_2nd = 0; |
253 let mut start_scan_2nd = 0; |
271 |
254 |
272 // Scan in order |
255 // Scan in order |
273 if indices.len() == 0 { |
256 if indices.len() == 0 { |
274 return res |
257 return count; |
275 } |
258 } |
276 for k in 0..indices.len()-1 { |
259 for k in 0..indices.len() - 1 { |
277 let i = indices[k]; |
260 let i = indices[k]; |
278 let &DeltaMeasure{ x : Loc([xi1, xi2]), α : αi } = &self[i]; |
261 let &DeltaMeasure { |
|
262 x: Loc([xi1, xi2]), |
|
263 α: αi, |
|
264 } = &self[i]; |
279 |
265 |
280 if αi == 0.0 { |
266 if αi == 0.0 { |
281 // Nothin to be done if the weight is already zero |
267 // Nothin to be done if the weight is already zero |
282 continue |
268 continue; |
283 } |
269 } |
284 |
270 |
285 let mut closest = None; |
271 let mut closest = None; |
286 |
272 |
287 // 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` |
288 // 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 |
289 // 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 |
290 // 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 |
291 // spike was discovered based on the second coordinate. |
277 // spike was discovered based on the second coordinate. |
292 'scan_2nd: for l in (start_scan_2nd+1)..indices.len() { |
278 'scan_2nd: for l in (start_scan_2nd + 1)..indices.len() { |
293 if l == k { |
279 if l == k { |
294 // Do not attempt to merge a spike with itself |
280 // Do not attempt to merge a spike with itself |
295 continue |
281 continue; |
296 } |
282 } |
297 let j = indices[l]; |
283 let j = indices[l]; |
298 let &DeltaMeasure{ x : Loc([xj1, xj2]), α : αj } = &self[j]; |
284 let &DeltaMeasure { |
|
285 x: Loc([xj1, xj2]), |
|
286 α: αj, |
|
287 } = &self[j]; |
299 |
288 |
300 if xj1 < xi1 - ρ { |
289 if xj1 < xi1 - ρ { |
301 // 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 |
302 // for next iteration, and continue scanning. |
291 // for next iteration, and continue scanning. |
303 start_scan_2nd = l; |
292 start_scan_2nd = l; |
304 continue 'scan_2nd |
293 continue 'scan_2nd; |
305 } else if xj1 > xi1 + ρ { |
294 } else if xj1 > xi1 + ρ { |
306 // 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 |
307 // 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`. |
308 break 'scan_2nd |
297 break 'scan_2nd; |
309 } |
298 } |
310 |
299 |
311 // 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 |
312 // previously discovered mergeable spikes. |
301 // previously discovered mergeable spikes. |
313 let d2 = (xi2-xj2).abs(); |
302 let d2 = (xi2 - xj2).abs(); |
314 if αj != 0.0 && d2 <= ρ { |
303 if αj != 0.0 && d2 <= ρ { |
315 let r1 = xi1-xj1; |
304 let r1 = xi1 - xj1; |
316 let d = (d2*d2 + r1*r1).sqrt(); |
305 let d = (d2 * d2 + r1 * r1).sqrt(); |
317 match closest { |
306 match closest { |
318 None => closest = Some((l, j, d)), |
307 None => closest = Some((l, j, d)), |
319 Some((_, _, r)) if r > d => closest = Some((l, j, d)), |
308 Some((_, _, r)) if r > d => closest = Some((l, j, d)), |
320 _ => {}, |
309 _ => {} |
321 } |
310 } |
322 } |
311 } |
323 } |
312 } |
324 |
313 |
325 // Attempt merging closest close-enough spike |
314 // Attempt merging closest close-enough spike |
326 if let Some((l, j, _)) = closest { |
315 if let Some((l, j, _)) = closest { |
327 if self.attempt_merge(&mut res, i, j, &accept) { |
316 if let Some(n) = self.attempt_merge(i, j, interp, &mut accept) { |
328 // If merge was succesfull, make new spike candidate for merging. |
317 // If merge was succesfull, make new spike candidate for merging. |
329 indices[l] = self.spikes.len() - 1; |
318 indices[l] = n; |
330 let compare = |i, j| compare_first_coordinate(&self.spikes[i], |
319 count += 1; |
331 &self.spikes[j]); |
320 let compare = |i, j| compare_first_coordinate(&self.spikes[i], &self.spikes[j]); |
332 // Re-sort relevant range of indices |
321 // Re-sort relevant range of indices |
333 if l < k { |
322 if l < k { |
334 indices[l..k].sort_by(|&i, &j| compare(i, j)); |
323 indices[l..k].sort_by(|&i, &j| compare(i, j)); |
335 } else { |
324 } else { |
336 indices[k+1..=l].sort_by(|&i, &j| compare(i, j)); |
325 indices[k + 1..=l].sort_by(|&i, &j| compare(i, j)); |
337 } |
326 } |
338 } |
327 } |
339 } |
328 } |
340 } |
329 } |
341 |
330 |
342 res |
331 count |
343 } |
332 } |
344 } |
333 } |
345 |
|