--- a/src/plot.rs Sun Apr 27 15:03:51 2025 -0500 +++ b/src/plot.rs Thu Feb 26 11:38:43 2026 -0500 @@ -1,37 +1,30 @@ //! Plotting helper utilities +use crate::measures::*; +use alg_tools::lingrid::LinGrid; +use alg_tools::loc::Loc; +use alg_tools::mapping::RealMapping; +use alg_tools::tabledump::write_csv; +use alg_tools::types::*; use numeric_literals::replace_float_literals; use serde::Serialize; -use alg_tools::types::*; -use alg_tools::lingrid::LinGrid; -use alg_tools::mapping::RealMapping; -use alg_tools::loc::Loc; -use alg_tools::tabledump::write_csv; -use crate::measures::*; /// Helper trait for implementing dimension-dependent plotting routines. -pub trait Plotting<const N : usize> { +pub trait Plotting<const N: usize> { /// Plot several mappings and a discrete measure into a file. - fn plot_into_file_spikes< - F : Float, - T1 : RealMapping<F, N>, - T2 : RealMapping<F, N> - > ( - g : Option<&T1>, - ω : Option<&T2>, - grid : LinGrid<F, N>, - μ : &RNDM<F, N>, - filename : String, + fn plot_into_file_spikes<F: Float, T1: RealMapping<N, F>, T2: RealMapping<N, F>>( + g: Option<&T1>, + ω: Option<&T2>, + grid: LinGrid<N, F>, + μ: &RNDM<N, F>, + filename: String, ); /// Plot a mapping into a file, sampling values on a given grid. - fn plot_into_file< - F : Float, - T1 : RealMapping<F, N>, - > ( - g : &T1, - grid : LinGrid<F, N>, - filename : String, + fn plot_into_file<F: Float, T1: RealMapping<N, F>>( + g: &T1, + grid: LinGrid<N, F>, + filename: String, ); } @@ -39,172 +32,181 @@ pub struct PlotLookup; #[derive(Serialize)] -struct CSVHelper1<F : Float> { - x : F, - f : F, +struct CSVHelper1<F: Float> { + x: F, + f: F, } #[derive(Serialize)] -struct CSVHelper1_2<F : Float>{ - x : F, - g : Option<F>, - omega : Option<F> +struct CSVHelper1_2<F: Float> { + x: F, + g: Option<F>, + omega: Option<F>, } #[derive(Serialize)] -struct CSVSpike1<F : Float> { - x : F, - alpha : F, +struct CSVSpike1<F: Float> { + x: F, + alpha: F, } impl Plotting<1> for PlotLookup { - fn plot_into_file_spikes< - F : Float, - T1 : RealMapping<F, 1>, - T2 : RealMapping<F, 1> - > ( - g0 : Option<&T1>, - ω0 : Option<&T2>, - grid : LinGrid<F, 1>, - μ : &DiscreteMeasure<Loc<F, 1>, F>, - filename : String, + fn plot_into_file_spikes<F: Float, T1: RealMapping<1, F>, T2: RealMapping<1, F>>( + g0: Option<&T1>, + ω0: Option<&T2>, + grid: LinGrid<1, F>, + μ: &DiscreteMeasure<Loc<1, F>, F>, + filename: String, ) { - let data = grid.into_iter().map(|p@Loc([x]) : Loc<F, 1>| CSVHelper1_2 { - x, - g : g0.map(|g| g.apply(&p)), - omega : ω0.map(|ω| ω.apply(&p)) - }); + let data = grid + .into_iter() + .map(|p @ Loc([x]): Loc<1, F>| CSVHelper1_2 { + x, + g: g0.map(|g| g.apply(&p)), + omega: ω0.map(|ω| ω.apply(&p)), + }); let csv_f = format!("{}_functions.csv", filename); write_csv(data, csv_f).expect("CSV save error"); let spikes = μ.iter_spikes().map(|δ| { let Loc([x]) = δ.x; - CSVSpike1 { x, alpha : δ.α } + CSVSpike1 { x, alpha: δ.α } }); let csv_f = format!("{}_spikes.csv", filename); write_csv(spikes, csv_f).expect("CSV save error"); } - fn plot_into_file< - F : Float, - T1 : RealMapping<F, 1>, - > ( - g : &T1, - grid : LinGrid<F, 1>, - filename : String, + fn plot_into_file<F: Float, T1: RealMapping<1, F>>( + g: &T1, + grid: LinGrid<1, F>, + filename: String, ) { - let data = grid.into_iter().map(|p@Loc([x]) : Loc<F, 1>| CSVHelper1 { - x, - f : g.apply(&p), - }); + let data = grid + .into_iter() + .map(|p @ Loc([x]): Loc<1, F>| CSVHelper1 { x, f: g.apply(&p) }); let csv_f = format!("{}.txt", filename); write_csv(data, csv_f).expect("CSV save error"); } - } #[derive(Serialize)] -struct CSVHelper2<F : Float> { - x : F, - y : F, - f : F, +struct CSVHelper2<F: Float> { + x: F, + y: F, + f: F, } #[derive(Serialize)] -struct CSVHelper2_2<F : Float>{ - x : F, - y : F, - g : Option<F>, - omega : Option<F> +struct CSVHelper2_2<F: Float> { + x: F, + y: F, + g: Option<F>, + omega: Option<F>, } #[derive(Serialize)] -struct CSVSpike2<F : Float> { - x : F, - y : F, - alpha : F, +struct CSVSpike2<F: Float> { + x: F, + y: F, + alpha: F, } - impl Plotting<2> for PlotLookup { #[replace_float_literals(F::cast_from(literal))] - fn plot_into_file_spikes< - F : Float, - T1 : RealMapping<F, 2>, - T2 : RealMapping<F, 2> - > ( - g0 : Option<&T1>, - ω0 : Option<&T2>, - grid : LinGrid<F, 2>, - μ : &DiscreteMeasure<Loc<F, 2>, F>, - filename : String, + fn plot_into_file_spikes<F: Float, T1: RealMapping<2, F>, T2: RealMapping<2, F>>( + g0: Option<&T1>, + ω0: Option<&T2>, + grid: LinGrid<2, F>, + μ: &DiscreteMeasure<Loc<2, F>, F>, + filename: String, ) { - let data = grid.into_iter().map(|p@Loc([x, y]) : Loc<F, 2>| CSVHelper2_2 { - x, - y, - g : g0.map(|g| g.apply(&p)), - omega : ω0.map(|ω| ω.apply(&p)) - }); + let data = grid + .into_iter() + .map(|p @ Loc([x, y]): Loc<2, F>| CSVHelper2_2 { + x, + y, + g: g0.map(|g| g.apply(&p)), + omega: ω0.map(|ω| ω.apply(&p)), + }); let csv_f = format!("{}_functions.csv", filename); write_csv(data, csv_f).expect("CSV save error"); let spikes = μ.iter_spikes().map(|δ| { let Loc([x, y]) = δ.x; - CSVSpike2 { x, y, alpha : δ.α } + CSVSpike2 { x, y, alpha: δ.α } }); let csv_f = format!("{}_spikes.csv", filename); write_csv(spikes, csv_f).expect("CSV save error"); } - fn plot_into_file< - F : Float, - T1 : RealMapping<F, 2>, - > ( - g : &T1, - grid : LinGrid<F, 2>, - filename : String, + fn plot_into_file<F: Float, T1: RealMapping<2, F>>( + g: &T1, + grid: LinGrid<2, F>, + filename: String, ) { - let data = grid.into_iter().map(|p@Loc([x, y]) : Loc<F, 2>| CSVHelper2 { - x, - y, - f : g.apply(&p), - }); + let data = grid + .into_iter() + .map(|p @ Loc([x, y]): Loc<2, F>| CSVHelper2 { + x, + y, + f: g.apply(&p), + }); let csv_f = format!("{}.txt", filename); write_csv(data, csv_f).expect("CSV save error"); } - } -/// A helper structure for plotting a sequence of images. -#[derive(Clone,Debug)] -pub struct SeqPlotter<F : Float, const N : usize> { - /// File name prefix - prefix : String, - /// Maximum number of plots to perform - max_plots : usize, - /// Sampling grid - grid : LinGrid<F, N>, - /// Current plot count - plot_count : usize, +/// Trait for plotters +pub trait Plotter<T1, T2, M> { + /// Plot the functions `g` and `ω` as well as the spikes of `μ`. + fn plot_spikes(&mut self, iter: usize, g: Option<&T1>, ω: Option<&T2>, μ: &M); +} + +/// A plotter that does nothing. +pub struct NoPlotter; + +impl<T1, T2, M> Plotter<T1, T2, M> for NoPlotter { + fn plot_spikes(&mut self, _iter: usize, _g: Option<&T1>, _ω: Option<&T2>, _μ: &M) {} } -impl<F : Float, const N : usize> SeqPlotter<F, N> -where PlotLookup : Plotting<N> { - /// Creates a new sequence plotter instance - pub fn new(prefix : String, max_plots : usize, grid : LinGrid<F, N>) -> Self { - SeqPlotter { prefix, max_plots, grid, plot_count : 0 } - } +/// A basic plotter. +/// +/// This calls [`PlotLookup::plot_into_file_spikes`] with a sequentially numbered file name. +#[derive(Clone, Debug)] +pub struct SeqPlotter<const N: usize, F: Float = f64> { + /// File name prefix + prefix: String, + /// Maximum number of plots to perform + max_plots: usize, + /// Sampling grid + grid: LinGrid<N, F>, + /// Current plot count + plot_count: usize, +} - /// This calls [`PlotLookup::plot_into_file_spikes`] with a sequentially numbered file name. - pub fn plot_spikes<T1, T2>( - &mut self, - iter : usize, - g : Option<&T1>, - ω : Option<&T2>, - μ : &RNDM<F, N>, - ) where T1 : RealMapping<F, N>, - T2 : RealMapping<F, N> - { +impl<F: Float, const N: usize> SeqPlotter<N, F> +where + PlotLookup: Plotting<N>, +{ + /// Creates a new sequence plotter instance + pub fn new(prefix: String, max_plots: usize, grid: LinGrid<N, F>) -> Self { + SeqPlotter { + prefix, + max_plots, + grid, + plot_count: 0, + } + } +} + +impl<F, T1, T2, const N: usize> Plotter<T1, T2, RNDM<N, F>> for SeqPlotter<N, F> +where + F: Float, + T1: RealMapping<N, F>, + T2: RealMapping<N, F>, + PlotLookup: Plotting<N>, +{ + fn plot_spikes(&mut self, iter: usize, g: Option<&T1>, ω: Option<&T2>, μ: &RNDM<N, F>) { if self.plot_count == 0 && self.max_plots > 0 { std::fs::create_dir_all(&self.prefix).expect("Unable to create plot directory"); } @@ -214,7 +216,7 @@ ω, self.grid, μ, - format!("{}out{:03}", self.prefix, iter) + format!("{}out{:03}", self.prefix, iter), ); self.plot_count += 1; }