diff -r 4f468d35fa29 -r 7a8a55fd41c0 src/types.rs --- a/src/types.rs Thu Feb 26 11:38:43 2026 -0500 +++ b/src/types.rs Thu Feb 26 11:36:22 2026 -0500 @@ -21,6 +21,32 @@ impl ClapFloat for f32 {} impl ClapFloat for f64 {} +/// Structure for storing transport statistics +#[derive(Debug, Clone, Serialize)] +pub struct TransportInfo { + /// Tuple of (untransported mass, source mass) + pub untransported_fraction: (F, F), + /// Tuple of (|destination mass - transported_mass|, transported mass) + pub transport_error: (F, F), + /// Number of readjustment iterations for transport + pub readjustment_iters: usize, + /// ($∫ c_2 dγ , ∫ dγ$) + pub dist: (F, F), +} + +#[replace_float_literals(F::cast_from(literal))] +impl TransportInfo { + /// Initialise transport statistics + pub fn new() -> Self { + TransportInfo { + untransported_fraction: (0.0, 0.0), + transport_error: (0.0, 0.0), + readjustment_iters: 0, + dist: (0.0, 0.0), + } + } +} + /// Structure for storing iteration statistics #[derive(Debug, Clone, Serialize)] pub struct IterInfo { @@ -38,10 +64,8 @@ pub pruned: usize, /// Number of inner iterations since last IterInfo statistic pub inner_iters: usize, - /// Tuple of (transported mass, source mass) - pub untransported_fraction: Option<(F, F)>, - /// Tuple of (|destination mass - untransported_mass|, transported mass) - pub transport_error: Option<(F, F)>, + /// Transport statistis + pub transport: Option>, /// Current tolerance pub ε: F, // /// Solve fin.dim problem for this measure to get the optimal `value`. @@ -61,10 +85,17 @@ inner_iters: 0, ε: F::NAN, // postprocessing : None, - untransported_fraction: None, - transport_error: None, + transport: None, } } + + /// Get mutable reference to transport statistics, creating it if it is `None`. + pub fn get_transport_mut(&mut self) -> &mut TransportInfo { + if self.transport.is_none() { + self.transport = Some(TransportInfo::new()); + } + self.transport.as_mut().unwrap() + } } #[replace_float_literals(F::cast_from(literal))] @@ -74,7 +105,7 @@ { fn logrepr(&self) -> ColoredString { format!( - "{}\t| N = {}, ε = {:.8}, 𝔼inner_it = {}, 𝔼ins/mer/pru = {}/{}/{}{}{}", + "{}\t| N = {}, ε = {:.2e}, 𝔼inner_it = {}, 𝔼ins/mer/pru = {}/{}/{}{}", self.value.logrepr(), self.n_spikes, self.ε, @@ -82,23 +113,20 @@ self.inserted as float / self.this_iters.max(1) as float, self.merged as float / self.this_iters.max(1) as float, self.pruned as float / self.this_iters.max(1) as float, - match self.untransported_fraction { + match &self.transport { None => format!(""), - Some((a, b)) => - if b > 0.0 { - format!(", untransported {:.2}%", 100.0 * a / b) - } else { - format!("") - }, - }, - match self.transport_error { - None => format!(""), - Some((a, b)) => - if b > 0.0 { - format!(", transport error {:.2}%", 100.0 * a / b) - } else { - format!("") - }, + Some(t) => { + let (a1, b1) = t.untransported_fraction; + let (a2, b2) = t.transport_error; + let (a3, b3) = t.dist; + format!( + ", γ-un/er/d/it = {:.2}%/{:.2}%/{:.2e}/{:.2}", + if b1 > 0.0 { 100.0 * a1 / b1 } else { F::NAN }, + if b2 > 0.0 { 100.0 * a2 / b2 } else { F::NAN }, + if b3 > 0.0 { a3 / b3 } else { F::NAN }, + t.readjustment_iters as float / self.this_iters.max(1) as float, + ) + } } ) .as_str() @@ -120,10 +148,7 @@ #[replace_float_literals(F::cast_from(literal))] impl Default for RefinementSettings { fn default() -> Self { - RefinementSettings { - tolerance_mult: 0.1, - max_steps: 50000, - } + RefinementSettings { tolerance_mult: 0.1, max_steps: 50000 } } }