17 use super::discrete::*; |
17 use super::discrete::*; |
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 enum SpikeMergingMethod<F> { |
22 pub struct SpikeMergingMethod<F> { |
23 /// Try to merge spikes within a given radius of each other, averaging the location |
23 // Merging radius |
24 HeuristicRadius(F), |
24 pub(crate) radius : F, |
25 /// Try to merge spikes within a given radius of each other, attempting original locations |
25 // Enabled |
26 HeuristicRadiusNoInterp(F), |
26 pub(crate) enabled : bool, |
27 /// No merging |
27 // Interpolate merged points |
28 None, |
28 pub(crate) interp : bool, |
29 } |
29 } |
30 |
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"), |
|
45 Self::HeuristicRadius(r) => write!(f, "i:{}", r), |
|
46 Self::HeuristicRadiusNoInterp(r) => write!(f, "n:{}", r), |
|
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 { |
|
58 let mut subs = s.split(':'); |
|
59 match subs.next() { |
|
60 None => Ok(Self::HeuristicRadius(F::from_str(s)?)), |
|
61 Some(t) if t == "n" => match subs.next() { |
|
62 None => Err(core::num::dec2flt::pfe_invalid()), |
|
63 Some(v) => Ok(Self::HeuristicRadiusNoInterp(F::from_str(v)?)) |
|
64 }, |
|
65 Some(t) if t == "i" => match subs.next() { |
|
66 None => Err(core::num::dec2flt::pfe_invalid()), |
|
67 Some(v) => Ok(Self::HeuristicRadius(F::from_str(v)?)) |
|
68 }, |
|
69 Some(v) => Ok(Self::HeuristicRadius(F::from_str(v)?)) |
|
70 } |
|
71 } |
|
72 } |
|
73 } |
|
74 |
31 |
75 #[replace_float_literals(F::cast_from(literal))] |
32 #[replace_float_literals(F::cast_from(literal))] |
76 impl<F : Float> Default for SpikeMergingMethod<F> { |
33 impl<F : Float> Default for SpikeMergingMethod<F> { |
77 fn default() -> Self { |
34 fn default() -> Self { |
78 SpikeMergingMethod::HeuristicRadius(0.02) |
35 SpikeMergingMethod{ |
|
36 radius : 0.01, |
|
37 enabled : false, |
|
38 interp : true, |
|
39 } |
79 } |
40 } |
80 } |
41 } |
81 |
42 |
82 /// Trait for dimension-dependent implementation of heuristic peak merging strategies. |
43 /// Trait for dimension-dependent implementation of heuristic peak merging strategies. |
83 pub trait SpikeMerging<F> { |
44 pub trait SpikeMerging<F> { |
88 /// new candidate measure (it will generally be internally mutated `self`, although this is |
49 /// 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 |
50 /// 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 |
51 /// an arbitrary value. This method will return that value for the *last* accepted merge, or |
91 /// [`None`] if no merge was accepted. |
52 /// [`None`] if no merge was accepted. |
92 /// |
53 /// |
93 /// This method is stable with respect to spike locations: on merge, the weight of existing |
54 /// This method is stable with respect to spike locations: on merge, the weights of existing |
94 /// spikes is set to zero, and a new one inserted at the end of the spike vector. |
55 /// removed spikes is set to zero, new ones inserted at the end of the spike vector. |
|
56 /// They merge may also be performed by increasing the weights of the existing spikes, |
|
57 /// without inserting new spikes. |
95 fn merge_spikes<G>(&mut self, method : SpikeMergingMethod<F>, accept : G) -> usize |
58 fn merge_spikes<G>(&mut self, method : SpikeMergingMethod<F>, accept : G) -> usize |
96 where G : FnMut(&'_ Self) -> bool { |
59 where G : FnMut(&'_ Self) -> bool { |
97 match method { |
60 if method.enabled { |
98 SpikeMergingMethod::HeuristicRadius(ρ) => |
61 self.do_merge_spikes_radius(method.radius, method.interp, accept) |
99 self.do_merge_spikes_radius(ρ, true, accept), |
62 } else { |
100 SpikeMergingMethod::HeuristicRadiusNoInterp(ρ) => |
63 0 |
101 self.do_merge_spikes_radius(ρ, false, accept), |
|
102 SpikeMergingMethod::None => 0, |
|
103 } |
64 } |
104 } |
65 } |
105 |
66 |
106 /// Attempt to merge spikes based on a value and a fitness function. |
67 /// Attempt to merge spikes based on a value and a fitness function. |
107 /// |
68 /// |