| 1 //! Plotting helper utilities |
1 //! Plotting helper utilities |
| 2 |
2 |
| |
3 use crate::measures::*; |
| |
4 use alg_tools::lingrid::LinGrid; |
| |
5 use alg_tools::loc::Loc; |
| |
6 use alg_tools::mapping::RealMapping; |
| |
7 use alg_tools::tabledump::write_csv; |
| |
8 use alg_tools::types::*; |
| 3 use numeric_literals::replace_float_literals; |
9 use numeric_literals::replace_float_literals; |
| 4 use serde::Serialize; |
10 use serde::Serialize; |
| 5 use alg_tools::types::*; |
|
| 6 use alg_tools::lingrid::LinGrid; |
|
| 7 use alg_tools::mapping::RealMapping; |
|
| 8 use alg_tools::loc::Loc; |
|
| 9 use alg_tools::tabledump::write_csv; |
|
| 10 use crate::measures::*; |
|
| 11 |
11 |
| 12 /// Helper trait for implementing dimension-dependent plotting routines. |
12 /// Helper trait for implementing dimension-dependent plotting routines. |
| 13 pub trait Plotting<const N : usize> { |
13 pub trait Plotting<const N: usize> { |
| 14 /// Plot several mappings and a discrete measure into a file. |
14 /// Plot several mappings and a discrete measure into a file. |
| 15 fn plot_into_file_spikes< |
15 fn plot_into_file_spikes<F: Float, T1: RealMapping<N, F>, T2: RealMapping<N, F>>( |
| 16 F : Float, |
16 g: Option<&T1>, |
| 17 T1 : RealMapping<F, N>, |
17 ω: Option<&T2>, |
| 18 T2 : RealMapping<F, N> |
18 grid: LinGrid<N, F>, |
| 19 > ( |
19 μ: &RNDM<N, F>, |
| 20 g : Option<&T1>, |
20 filename: String, |
| 21 ω : Option<&T2>, |
|
| 22 grid : LinGrid<F, N>, |
|
| 23 μ : &RNDM<F, N>, |
|
| 24 filename : String, |
|
| 25 ); |
21 ); |
| 26 |
22 |
| 27 /// Plot a mapping into a file, sampling values on a given grid. |
23 /// Plot a mapping into a file, sampling values on a given grid. |
| 28 fn plot_into_file< |
24 fn plot_into_file<F: Float, T1: RealMapping<N, F>>( |
| 29 F : Float, |
25 g: &T1, |
| 30 T1 : RealMapping<F, N>, |
26 grid: LinGrid<N, F>, |
| 31 > ( |
27 filename: String, |
| 32 g : &T1, |
|
| 33 grid : LinGrid<F, N>, |
|
| 34 filename : String, |
|
| 35 ); |
28 ); |
| 36 } |
29 } |
| 37 |
30 |
| 38 /// Helper type for looking up a [`Plotting`] based on dimension. |
31 /// Helper type for looking up a [`Plotting`] based on dimension. |
| 39 pub struct PlotLookup; |
32 pub struct PlotLookup; |
| 40 |
33 |
| 41 #[derive(Serialize)] |
34 #[derive(Serialize)] |
| 42 struct CSVHelper1<F : Float> { |
35 struct CSVHelper1<F: Float> { |
| 43 x : F, |
36 x: F, |
| 44 f : F, |
37 f: F, |
| 45 } |
38 } |
| 46 |
39 |
| 47 #[derive(Serialize)] |
40 #[derive(Serialize)] |
| 48 struct CSVHelper1_2<F : Float>{ |
41 struct CSVHelper1_2<F: Float> { |
| 49 x : F, |
42 x: F, |
| 50 g : Option<F>, |
43 g: Option<F>, |
| 51 omega : Option<F> |
44 omega: Option<F>, |
| 52 } |
45 } |
| 53 |
46 |
| 54 #[derive(Serialize)] |
47 #[derive(Serialize)] |
| 55 struct CSVSpike1<F : Float> { |
48 struct CSVSpike1<F: Float> { |
| 56 x : F, |
49 x: F, |
| 57 alpha : F, |
50 alpha: F, |
| 58 } |
51 } |
| 59 |
52 |
| 60 impl Plotting<1> for PlotLookup { |
53 impl Plotting<1> for PlotLookup { |
| 61 fn plot_into_file_spikes< |
54 fn plot_into_file_spikes<F: Float, T1: RealMapping<1, F>, T2: RealMapping<1, F>>( |
| 62 F : Float, |
55 g0: Option<&T1>, |
| 63 T1 : RealMapping<F, 1>, |
56 ω0: Option<&T2>, |
| 64 T2 : RealMapping<F, 1> |
57 grid: LinGrid<1, F>, |
| 65 > ( |
58 μ: &DiscreteMeasure<Loc<1, F>, F>, |
| 66 g0 : Option<&T1>, |
59 filename: String, |
| 67 ω0 : Option<&T2>, |
60 ) { |
| 68 grid : LinGrid<F, 1>, |
61 let data = grid |
| 69 μ : &DiscreteMeasure<Loc<F, 1>, F>, |
62 .into_iter() |
| 70 filename : String, |
63 .map(|p @ Loc([x]): Loc<1, F>| CSVHelper1_2 { |
| 71 ) { |
64 x, |
| 72 let data = grid.into_iter().map(|p@Loc([x]) : Loc<F, 1>| CSVHelper1_2 { |
65 g: g0.map(|g| g.apply(&p)), |
| 73 x, |
66 omega: ω0.map(|ω| ω.apply(&p)), |
| 74 g : g0.map(|g| g.apply(&p)), |
67 }); |
| 75 omega : ω0.map(|ω| ω.apply(&p)) |
|
| 76 }); |
|
| 77 let csv_f = format!("{}_functions.csv", filename); |
68 let csv_f = format!("{}_functions.csv", filename); |
| 78 write_csv(data, csv_f).expect("CSV save error"); |
69 write_csv(data, csv_f).expect("CSV save error"); |
| 79 |
70 |
| 80 let spikes = μ.iter_spikes().map(|δ| { |
71 let spikes = μ.iter_spikes().map(|δ| { |
| 81 let Loc([x]) = δ.x; |
72 let Loc([x]) = δ.x; |
| 82 CSVSpike1 { x, alpha : δ.α } |
73 CSVSpike1 { x, alpha: δ.α } |
| 83 }); |
74 }); |
| 84 let csv_f = format!("{}_spikes.csv", filename); |
75 let csv_f = format!("{}_spikes.csv", filename); |
| 85 write_csv(spikes, csv_f).expect("CSV save error"); |
76 write_csv(spikes, csv_f).expect("CSV save error"); |
| 86 } |
77 } |
| 87 |
78 |
| 88 fn plot_into_file< |
79 fn plot_into_file<F: Float, T1: RealMapping<1, F>>( |
| 89 F : Float, |
80 g: &T1, |
| 90 T1 : RealMapping<F, 1>, |
81 grid: LinGrid<1, F>, |
| 91 > ( |
82 filename: String, |
| 92 g : &T1, |
83 ) { |
| 93 grid : LinGrid<F, 1>, |
84 let data = grid |
| 94 filename : String, |
85 .into_iter() |
| 95 ) { |
86 .map(|p @ Loc([x]): Loc<1, F>| CSVHelper1 { x, f: g.apply(&p) }); |
| 96 let data = grid.into_iter().map(|p@Loc([x]) : Loc<F, 1>| CSVHelper1 { |
|
| 97 x, |
|
| 98 f : g.apply(&p), |
|
| 99 }); |
|
| 100 let csv_f = format!("{}.txt", filename); |
87 let csv_f = format!("{}.txt", filename); |
| 101 write_csv(data, csv_f).expect("CSV save error"); |
88 write_csv(data, csv_f).expect("CSV save error"); |
| 102 } |
89 } |
| 103 |
90 } |
| 104 } |
91 |
| 105 |
92 #[derive(Serialize)] |
| 106 #[derive(Serialize)] |
93 struct CSVHelper2<F: Float> { |
| 107 struct CSVHelper2<F : Float> { |
94 x: F, |
| 108 x : F, |
95 y: F, |
| 109 y : F, |
96 f: F, |
| 110 f : F, |
97 } |
| 111 } |
98 |
| 112 |
99 #[derive(Serialize)] |
| 113 #[derive(Serialize)] |
100 struct CSVHelper2_2<F: Float> { |
| 114 struct CSVHelper2_2<F : Float>{ |
101 x: F, |
| 115 x : F, |
102 y: F, |
| 116 y : F, |
103 g: Option<F>, |
| 117 g : Option<F>, |
104 omega: Option<F>, |
| 118 omega : Option<F> |
105 } |
| 119 } |
106 |
| 120 |
107 #[derive(Serialize)] |
| 121 #[derive(Serialize)] |
108 struct CSVSpike2<F: Float> { |
| 122 struct CSVSpike2<F : Float> { |
109 x: F, |
| 123 x : F, |
110 y: F, |
| 124 y : F, |
111 alpha: F, |
| 125 alpha : F, |
112 } |
| 126 } |
|
| 127 |
|
| 128 |
113 |
| 129 impl Plotting<2> for PlotLookup { |
114 impl Plotting<2> for PlotLookup { |
| 130 #[replace_float_literals(F::cast_from(literal))] |
115 #[replace_float_literals(F::cast_from(literal))] |
| 131 fn plot_into_file_spikes< |
116 fn plot_into_file_spikes<F: Float, T1: RealMapping<2, F>, T2: RealMapping<2, F>>( |
| 132 F : Float, |
117 g0: Option<&T1>, |
| 133 T1 : RealMapping<F, 2>, |
118 ω0: Option<&T2>, |
| 134 T2 : RealMapping<F, 2> |
119 grid: LinGrid<2, F>, |
| 135 > ( |
120 μ: &DiscreteMeasure<Loc<2, F>, F>, |
| 136 g0 : Option<&T1>, |
121 filename: String, |
| 137 ω0 : Option<&T2>, |
122 ) { |
| 138 grid : LinGrid<F, 2>, |
123 let data = grid |
| 139 μ : &DiscreteMeasure<Loc<F, 2>, F>, |
124 .into_iter() |
| 140 filename : String, |
125 .map(|p @ Loc([x, y]): Loc<2, F>| CSVHelper2_2 { |
| 141 ) { |
126 x, |
| 142 let data = grid.into_iter().map(|p@Loc([x, y]) : Loc<F, 2>| CSVHelper2_2 { |
127 y, |
| 143 x, |
128 g: g0.map(|g| g.apply(&p)), |
| 144 y, |
129 omega: ω0.map(|ω| ω.apply(&p)), |
| 145 g : g0.map(|g| g.apply(&p)), |
130 }); |
| 146 omega : ω0.map(|ω| ω.apply(&p)) |
|
| 147 }); |
|
| 148 let csv_f = format!("{}_functions.csv", filename); |
131 let csv_f = format!("{}_functions.csv", filename); |
| 149 write_csv(data, csv_f).expect("CSV save error"); |
132 write_csv(data, csv_f).expect("CSV save error"); |
| 150 |
133 |
| 151 let spikes = μ.iter_spikes().map(|δ| { |
134 let spikes = μ.iter_spikes().map(|δ| { |
| 152 let Loc([x, y]) = δ.x; |
135 let Loc([x, y]) = δ.x; |
| 153 CSVSpike2 { x, y, alpha : δ.α } |
136 CSVSpike2 { x, y, alpha: δ.α } |
| 154 }); |
137 }); |
| 155 let csv_f = format!("{}_spikes.csv", filename); |
138 let csv_f = format!("{}_spikes.csv", filename); |
| 156 write_csv(spikes, csv_f).expect("CSV save error"); |
139 write_csv(spikes, csv_f).expect("CSV save error"); |
| 157 } |
140 } |
| 158 |
141 |
| 159 fn plot_into_file< |
142 fn plot_into_file<F: Float, T1: RealMapping<2, F>>( |
| 160 F : Float, |
143 g: &T1, |
| 161 T1 : RealMapping<F, 2>, |
144 grid: LinGrid<2, F>, |
| 162 > ( |
145 filename: String, |
| 163 g : &T1, |
146 ) { |
| 164 grid : LinGrid<F, 2>, |
147 let data = grid |
| 165 filename : String, |
148 .into_iter() |
| 166 ) { |
149 .map(|p @ Loc([x, y]): Loc<2, F>| CSVHelper2 { |
| 167 let data = grid.into_iter().map(|p@Loc([x, y]) : Loc<F, 2>| CSVHelper2 { |
150 x, |
| 168 x, |
151 y, |
| 169 y, |
152 f: g.apply(&p), |
| 170 f : g.apply(&p), |
153 }); |
| 171 }); |
|
| 172 let csv_f = format!("{}.txt", filename); |
154 let csv_f = format!("{}.txt", filename); |
| 173 write_csv(data, csv_f).expect("CSV save error"); |
155 write_csv(data, csv_f).expect("CSV save error"); |
| 174 } |
156 } |
| 175 |
157 } |
| 176 } |
158 |
| 177 |
159 /// Trait for plotters |
| 178 /// A helper structure for plotting a sequence of images. |
160 pub trait Plotter<T1, T2, M> { |
| 179 #[derive(Clone,Debug)] |
161 /// Plot the functions `g` and `ω` as well as the spikes of `μ`. |
| 180 pub struct SeqPlotter<F : Float, const N : usize> { |
162 fn plot_spikes(&mut self, iter: usize, g: Option<&T1>, ω: Option<&T2>, μ: &M); |
| |
163 } |
| |
164 |
| |
165 /// A plotter that does nothing. |
| |
166 pub struct NoPlotter; |
| |
167 |
| |
168 impl<T1, T2, M> Plotter<T1, T2, M> for NoPlotter { |
| |
169 fn plot_spikes(&mut self, _iter: usize, _g: Option<&T1>, _ω: Option<&T2>, _μ: &M) {} |
| |
170 } |
| |
171 |
| |
172 /// A basic plotter. |
| |
173 /// |
| |
174 /// This calls [`PlotLookup::plot_into_file_spikes`] with a sequentially numbered file name. |
| |
175 #[derive(Clone, Debug)] |
| |
176 pub struct SeqPlotter<const N: usize, F: Float = f64> { |
| 181 /// File name prefix |
177 /// File name prefix |
| 182 prefix : String, |
178 prefix: String, |
| 183 /// Maximum number of plots to perform |
179 /// Maximum number of plots to perform |
| 184 max_plots : usize, |
180 max_plots: usize, |
| 185 /// Sampling grid |
181 /// Sampling grid |
| 186 grid : LinGrid<F, N>, |
182 grid: LinGrid<N, F>, |
| 187 /// Current plot count |
183 /// Current plot count |
| 188 plot_count : usize, |
184 plot_count: usize, |
| 189 } |
185 } |
| 190 |
186 |
| 191 impl<F : Float, const N : usize> SeqPlotter<F, N> |
187 impl<F: Float, const N: usize> SeqPlotter<N, F> |
| 192 where PlotLookup : Plotting<N> { |
188 where |
| |
189 PlotLookup: Plotting<N>, |
| |
190 { |
| 193 /// Creates a new sequence plotter instance |
191 /// Creates a new sequence plotter instance |
| 194 pub fn new(prefix : String, max_plots : usize, grid : LinGrid<F, N>) -> Self { |
192 pub fn new(prefix: String, max_plots: usize, grid: LinGrid<N, F>) -> Self { |
| 195 SeqPlotter { prefix, max_plots, grid, plot_count : 0 } |
193 SeqPlotter { |
| 196 } |
194 prefix, |
| 197 |
195 max_plots, |
| 198 /// This calls [`PlotLookup::plot_into_file_spikes`] with a sequentially numbered file name. |
196 grid, |
| 199 pub fn plot_spikes<T1, T2>( |
197 plot_count: 0, |
| 200 &mut self, |
198 } |
| 201 iter : usize, |
199 } |
| 202 g : Option<&T1>, |
200 } |
| 203 ω : Option<&T2>, |
201 |
| 204 μ : &RNDM<F, N>, |
202 impl<F, T1, T2, const N: usize> Plotter<T1, T2, RNDM<N, F>> for SeqPlotter<N, F> |
| 205 ) where T1 : RealMapping<F, N>, |
203 where |
| 206 T2 : RealMapping<F, N> |
204 F: Float, |
| 207 { |
205 T1: RealMapping<N, F>, |
| |
206 T2: RealMapping<N, F>, |
| |
207 PlotLookup: Plotting<N>, |
| |
208 { |
| |
209 fn plot_spikes(&mut self, iter: usize, g: Option<&T1>, ω: Option<&T2>, μ: &RNDM<N, F>) { |
| 208 if self.plot_count == 0 && self.max_plots > 0 { |
210 if self.plot_count == 0 && self.max_plots > 0 { |
| 209 std::fs::create_dir_all(&self.prefix).expect("Unable to create plot directory"); |
211 std::fs::create_dir_all(&self.prefix).expect("Unable to create plot directory"); |
| 210 } |
212 } |
| 211 if self.plot_count < self.max_plots { |
213 if self.plot_count < self.max_plots { |
| 212 PlotLookup::plot_into_file_spikes( |
214 PlotLookup::plot_into_file_spikes( |
| 213 g, |
215 g, |
| 214 ω, |
216 ω, |
| 215 self.grid, |
217 self.grid, |
| 216 μ, |
218 μ, |
| 217 format!("{}out{:03}", self.prefix, iter) |
219 format!("{}out{:03}", self.prefix, iter), |
| 218 ); |
220 ); |
| 219 self.plot_count += 1; |
221 self.plot_count += 1; |
| 220 } |
222 } |
| 221 } |
223 } |
| 222 } |
224 } |