src/plot.rs

branch
dev
changeset 61
4f468d35fa29
parent 35
b087e3eab191
--- 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;
         }

mercurial