Map diagnostics chart series to log scale by hand

This works around problems with the way ECharts does scaling and data
zoom for logarithmic axes.
This commit is contained in:
Aaron Fenyes 2025-06-25 15:44:42 -07:00
parent 2688b76678
commit e877985202

View file

@ -50,8 +50,11 @@ fn RealizationStatus() -> View {
} }
} }
fn into_time_point((step, value): (usize, f64)) -> Vec<Option<f64>> { fn into_log10_time_point((step, value): (usize, f64)) -> Vec<Option<f64>> {
vec![Some(step as f64), Some(value)] vec![
Some(step as f64),
if value == 0.0 { None } else { Some(value.abs().log10()) }
]
} }
// the loss history from the last realization // the loss history from the last realization
@ -69,15 +72,15 @@ fn LossHistory() -> View {
.iter() .iter()
.enumerate() .enumerate()
.map(|(step, &loss)| (step, loss)) .map(|(step, &loss)| (step, loss))
.map(into_time_point) .map(into_log10_time_point)
.collect() .collect()
); );
// initialize the chart axes and series // initialize the chart axes
let step_axis = Axis::new() let step_axis = Axis::new()
.type_(AxisType::Category) .type_(AxisType::Category)
.boundary_gap(false); .boundary_gap(false);
let scaled_loss_axis = Axis::new().type_(AxisType::Log); let scaled_loss_axis = Axis::new();
// load the chart data. when there's no history, we load the data // load the chart data. when there's no history, we load the data
// point (0, None) to clear the chart. it would feel more natural to // point (0, None) to clear the chart. it would feel more natural to
@ -116,9 +119,8 @@ fn SpectrumHistory() -> View {
on_mount(move || { on_mount(move || {
create_effect(move || { create_effect(move || {
// get the spectrum of the Hessian at each step, split into its // get the spectrum of the Hessian at each step, split into its
// positive and negative parts. throw away eigenvalues that are // positive and negative parts. exact zero eigenvalues will be
// close to zero // filtered out by `into_log10_time_point` later
const ZERO_THRESHOLD: f64 = 1e-6;
let ( let (
hess_eigvals_pos, hess_eigvals_pos,
hess_eigvals_neg hess_eigvals_neg
@ -127,22 +129,19 @@ fn SpectrumHistory() -> View {
.iter() .iter()
.enumerate() .enumerate()
.map( .map(
|(step, eigvals)| eigvals |(step, eigvals)| eigvals.iter().map(
.iter() move |&val| (step, val)
.filter(|&&val| val.abs() > ZERO_THRESHOLD) )
.map(
move |&val| (step, val)
)
) )
.flatten() .flatten()
.partition(|&(_, val)| val > 0.0) .partition(|&(_, val)| val > 0.0)
); );
// initialize the chart axes and series // initialize the chart axes
let step_axis = Axis::new() let step_axis = Axis::new()
.type_(AxisType::Category) .type_(AxisType::Category)
.boundary_gap(false); .boundary_gap(false);
let eigval_axis = Axis::new().type_(AxisType::Log); let eigval_axis = Axis::new();
// load the chart data. when there's no history, we load the data // load the chart data. when there's no history, we load the data
// point (0, None) to clear the chart. it would feel more natural to // point (0, None) to clear the chart. it would feel more natural to
@ -154,7 +153,7 @@ fn SpectrumHistory() -> View {
if hess_eigvals_pos.len() > 0 { if hess_eigvals_pos.len() > 0 {
hess_eigvals_pos hess_eigvals_pos
.into_iter() .into_iter()
.map(into_time_point) .map(into_log10_time_point)
.collect() .collect()
} else { } else {
vec![vec![Some(0.0), None::<f64>]] vec![vec![Some(0.0), None::<f64>]]
@ -167,8 +166,7 @@ fn SpectrumHistory() -> View {
if hess_eigvals_neg.len() > 0 { if hess_eigvals_neg.len() > 0 {
hess_eigvals_neg hess_eigvals_neg
.into_iter() .into_iter()
.map(|(step, val)| (step, -val)) .map(into_log10_time_point)
.map(into_time_point)
.collect() .collect()
} else { } else {
vec![vec![Some(0.0), None::<f64>]] vec![vec![Some(0.0), None::<f64>]]