src/plot.rs

branch
dev
changeset 61
4f468d35fa29
parent 35
b087e3eab191
equal deleted inserted replaced
60:9738b51d90d7 61:4f468d35fa29
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 }

mercurial