//! This module contains a log of all actions that happens during the run of the algorithms.
//! It is meant to be used by constructing an `Entry`, and operate on it during the course of the algorithm.
use std::collections::HashMap;
use crate::serde::Serialize;
use crate::persistence::{Simplex, Persistence};
/// Stats for a single `simplex_reduce` call.
#[derive(Debug, Copy, Clone)]
pub struct Add {
/// The dimension of the simplices added together
pub dim: isize,
/// The size of the boundary of the simplex that is being reduced.
pub this_size: usize,
/// The size of the boundary of the other simplex.
pub other_size: usize,
/// This is true when this add was an exhaustive add, namely that low(this) \notin other.
pub exhaustive: bool,
/// The ID of the reduced simplex
this_j: usize,
/// The ID of the other simplex
other_j: usize,
}
/// Stats for reducting one simplex.
#[derive(Debug)]
pub struct SimplexReduction {
/// All adds before the simplex was reduced
pub adds: Vec<Add>,
/// This simplex's lowest boundary element after reduced, if any.
pub place_index: Option<usize>,
/// Dimension of the simplex
pub dim: isize,
}
/// Stats for a whole run of the reduction algorithm.
pub struct Reduction<'a> {
pub reductions: Vec<SimplexReduction>,
pub persistence: &'a Persistence,
}
/// This is the full data that we output.
#[derive(Debug, Serialize)]
pub struct Aggregate {
/// Simplex IDs of all additions.
adds: Vec<(usize, usize)>,
/// The number of adds required to reduce a simplex, by dimension.
adds_required_by_dim: HashMap<isize, f64>,
/// The average number of additions required to reduce a simplex, by dimension.
avg_adds_required: HashMap<isize, f64>,
/// The size of a simplex when we add them to reduce the first.
add_size_by_dim: HashMap<isize, Vec<(usize, usize)>>,
/// Average of the sizes.
avg_add_size_by_dim: HashMap<isize, (f64, f64)>,
/// Histogram of the sizes
histogram_add_size_by_dim: HashMap<isize, HashMap<usize, (usize, usize)>>,
/// The sum of sizes of simplices when we reduce
add_cost_by_dim: HashMap<isize, Vec<usize>>,
avg_add_cost_by_dim: HashMap<isize, f64>,
histogram_add_cost_by_dim: HashMap<isize, HashMap<usize, usize>>,
num_simplices: HashMap<isize, usize>,
}
impl<'a> Reduction<'a> {
pub fn new(persistence: &'a Persistence) -> Self {
Self {
reductions: Vec::new(),
persistence
}
}
pub fn add(&mut self, s: SimplexReduction) {
self.reductions.push(s);
}
pub fn aggregate(&self) -> Aggregate {
let mut simplices2dim = HashMap::new();
for s in &self.persistence.simplices {
simplices2dim.insert(s.j, s.dim);
}
let mut num_simplices = HashMap::new();
for (&j, &dim) in simplices2dim.iter() {
*num_simplices.entry(dim).or_insert(0) += 1;
}
let mut adds = Vec::new();
for r in self.reductions.iter() {
for a in r.adds.iter() {
adds.push((a.this_j, a.other_j));
}
}
let mut adds_required_by_dim = HashMap::new();
let mut count = HashMap::new();
for r in self.reductions.iter() {
*adds_required_by_dim.entry(r.dim).or_insert(0.0) += r.adds.len() as f64;
*count.entry(r.dim).or_insert(0) += 1;
}
let mut avg_adds_required = HashMap::new();
for (&k, &c) in count.iter() {
avg_adds_required.insert(k, adds_required_by_dim.get(&k).unwrap() / c as f64);
}
let mut add_size_by_dim = HashMap::<isize, Vec<(usize, usize)>>::new();
for r in self.reductions.iter() {
if r.adds.len() == 0 {
continue;
}
let v = add_size_by_dim.entry(r.dim).or_insert(Vec::new());
for a in r.adds.iter() {
v.push((a.this_size, a.other_size));
}
}
let mut avg_add_size_by_dim = HashMap::new();
for (&dim, adds) in add_size_by_dim.iter() {
if adds.len() == 0 {
continue;
}
let avg_f = adds.iter().map(|(f, _s)| *f).sum::<usize>() as f64 / adds.len() as f64;
let avg_s = adds.iter().map(|(_f, s)| *s).sum::<usize>() as f64 / adds.len() as f64;
avg_add_size_by_dim.insert(dim, (avg_f, avg_s));
}
let mut histogram_add_size_by_dim = HashMap::new();
for r in self.reductions.iter() {
if r.adds.len() == 0 {
continue;
}
let histogram: &mut HashMap<usize, (usize, usize)> = histogram_add_size_by_dim
.entry(r.dim)
.or_insert(HashMap::new());
for add in r.adds.iter() {
histogram.entry(add.this_size).or_insert((0, 0)).0 += 1;
histogram.entry(add.other_size).or_insert((0, 0)).1 += 1;
}
}
let mut add_cost_by_dim = HashMap::<isize, Vec<usize>>::new();
for r in self.reductions.iter() {
if r.adds.len() == 0 {
continue;
}
let v = add_cost_by_dim.entry(r.dim).or_insert(Vec::new());
for a in r.adds.iter() {
v.push(a.this_size + a.other_size);
}
}
let mut avg_add_cost_by_dim = HashMap::new();
for (&dim, adds) in add_cost_by_dim.iter() {
if adds.len() == 0 {
continue;
}
let avg = adds.iter().map(|f| *f).sum::<usize>() as f64 / adds.len() as f64;
avg_add_cost_by_dim.insert(dim, avg);
}
let mut histogram_add_cost_by_dim = HashMap::new();
for r in self.reductions.iter() {
if r.adds.len() == 0 {
continue;
}
let histogram: &mut HashMap<usize, usize> = histogram_add_cost_by_dim
.entry(r.dim)
.or_insert(HashMap::new());
for add in r.adds.iter() {
*histogram.entry(add.this_size + add.other_size).or_insert(0) += 1;
}
}
Aggregate {
adds,
adds_required_by_dim,
avg_adds_required,
add_size_by_dim,
avg_add_size_by_dim,
histogram_add_size_by_dim,
add_cost_by_dim,
avg_add_cost_by_dim,
histogram_add_cost_by_dim,
num_simplices,
}
}
}
impl Add {
pub fn regular(this: &Simplex, other: &Simplex) -> Self {
Self {
dim: this.dim,
this_size: this.faces.len(),
other_size: other.faces.len(),
exhaustive: false,
this_j: this.j,
other_j: other.j,
}
}
pub fn exhaustive(this: &Simplex, other: &Simplex) -> Self {
Self {
dim: this.dim,
this_size: this.faces.len(),
other_size: other.faces.len(),
exhaustive: true,
this_j: this.j,
other_j: other.j,
}
}
}
impl SimplexReduction {
pub fn new(s: &Simplex) -> Self {
Self {
dim: s.dim,
adds: Vec::new(),
place_index: None,
}
}
pub fn add(self: &mut Self, add: Add) {
self.adds.push(add)
}
}