Switch to log scale for diagnostics charts
Split the spectrum of the Hessian into its positive and negative parts and plot them on the same logarithmic axis. The data zoom controls are still linearly scaled, as discussed in ECharts issue #20927 https://github.com/apache/echarts/issues/20927
This commit is contained in:
parent
af28e885bb
commit
2688b76678
1 changed files with 70 additions and 37 deletions
|
@ -2,7 +2,7 @@ use charming::{
|
|||
Chart,
|
||||
WasmRenderer,
|
||||
component::{Axis, DataZoom, Grid},
|
||||
element::AxisType,
|
||||
element::{AxisType, Symbol},
|
||||
series::{Line, Scatter},
|
||||
};
|
||||
use sycamore::prelude::*;
|
||||
|
@ -50,6 +50,10 @@ fn RealizationStatus() -> View {
|
|||
}
|
||||
}
|
||||
|
||||
fn into_time_point((step, value): (usize, f64)) -> Vec<Option<f64>> {
|
||||
vec![Some(step as f64), Some(value)]
|
||||
}
|
||||
|
||||
// the loss history from the last realization
|
||||
#[component]
|
||||
fn LossHistory() -> View {
|
||||
|
@ -60,38 +64,35 @@ fn LossHistory() -> View {
|
|||
on_mount(move || {
|
||||
create_effect(move || {
|
||||
// get the loss history
|
||||
let scaled_loss = state.assembly.descent_history.with(
|
||||
|history| history.scaled_loss.clone()
|
||||
let scaled_loss: Vec<_> = state.assembly.descent_history.with(
|
||||
|history| history.scaled_loss
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(step, &loss)| (step, loss))
|
||||
.map(into_time_point)
|
||||
.collect()
|
||||
);
|
||||
let step_cnt = scaled_loss.len();
|
||||
|
||||
// initialize the chart axes and series
|
||||
const MIN_INTERVAL: f64 = 0.01;
|
||||
let mut step_axis = Axis::new()
|
||||
let step_axis = Axis::new()
|
||||
.type_(AxisType::Category)
|
||||
.boundary_gap(false);
|
||||
let scaled_loss_axis = Axis::new()
|
||||
.type_(AxisType::Value)
|
||||
.min(0)
|
||||
.min_interval(MIN_INTERVAL);
|
||||
let scaled_loss_axis = Axis::new().type_(AxisType::Log);
|
||||
|
||||
// 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
|
||||
// load empty data vectors, but that turns out not to clear the
|
||||
// chart: it instead leads to previous data being re-used
|
||||
let mut scaled_loss_series = Line::new();
|
||||
if step_cnt > 0 {
|
||||
step_axis = step_axis.data(
|
||||
(0..step_cnt).map(|step| step.to_string()).collect()
|
||||
);
|
||||
scaled_loss_series = scaled_loss_series.data(scaled_loss);
|
||||
} else {
|
||||
step_axis = step_axis.data(vec![0.to_string()]);
|
||||
scaled_loss_series = scaled_loss_series.data(vec![None::<f64>]);
|
||||
}
|
||||
let scaled_loss_series = Line::new().data(
|
||||
if scaled_loss.len() > 0 {
|
||||
scaled_loss
|
||||
} else {
|
||||
vec![vec![Some(0.0), None::<f64>]]
|
||||
}
|
||||
);
|
||||
let chart = Chart::new()
|
||||
.animation(false)
|
||||
.data_zoom(DataZoom::new().y_axis_index(0).right(40).start(0).end(100))
|
||||
.data_zoom(DataZoom::new().y_axis_index(0).right(40))
|
||||
.x_axis(step_axis)
|
||||
.y_axis(scaled_loss_axis)
|
||||
.grid(Grid::new().top(20).right(80).bottom(30).left(60))
|
||||
|
@ -114,41 +115,73 @@ fn SpectrumHistory() -> View {
|
|||
|
||||
on_mount(move || {
|
||||
create_effect(move || {
|
||||
// get the spectrum of the Hessian at each step
|
||||
let hess_eigvals = state.assembly.descent_history.with(
|
||||
// get the spectrum of the Hessian at each step, split into its
|
||||
// positive and negative parts. throw away eigenvalues that are
|
||||
// close to zero
|
||||
const ZERO_THRESHOLD: f64 = 1e-6;
|
||||
let (
|
||||
hess_eigvals_pos,
|
||||
hess_eigvals_neg
|
||||
): (Vec<_>, Vec<_>) = state.assembly.descent_history.with(
|
||||
|history| history.hess_eigvals
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(
|
||||
|(step, eigvals)| eigvals.iter().map(
|
||||
move |val| vec![step as f64, *val]
|
||||
)
|
||||
|(step, eigvals)| eigvals
|
||||
.iter()
|
||||
.filter(|&&val| val.abs() > ZERO_THRESHOLD)
|
||||
.map(
|
||||
move |&val| (step, val)
|
||||
)
|
||||
)
|
||||
.flatten()
|
||||
.collect::<Vec<_>>()
|
||||
.partition(|&(_, val)| val > 0.0)
|
||||
);
|
||||
|
||||
// initialize the chart axes and series
|
||||
let step_axis = Axis::new();
|
||||
let eigval_axis = Axis::new();
|
||||
let step_axis = Axis::new()
|
||||
.type_(AxisType::Category)
|
||||
.boundary_gap(false);
|
||||
let eigval_axis = Axis::new().type_(AxisType::Log);
|
||||
|
||||
// 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
|
||||
// load empty data vectors, but that turns out not to clear the
|
||||
// chart: it instead leads to previous data being re-used
|
||||
let mut eigval_series = Scatter::new().symbol_size(7);
|
||||
if hess_eigvals.len() > 0 {
|
||||
eigval_series = eigval_series.data(hess_eigvals);
|
||||
} else {
|
||||
eigval_series = eigval_series.data(vec![None::<f64>, None::<f64>]);
|
||||
}
|
||||
let eigval_series_pos = Scatter::new()
|
||||
.symbol_size(4.5)
|
||||
.data(
|
||||
if hess_eigvals_pos.len() > 0 {
|
||||
hess_eigvals_pos
|
||||
.into_iter()
|
||||
.map(into_time_point)
|
||||
.collect()
|
||||
} else {
|
||||
vec![vec![Some(0.0), None::<f64>]]
|
||||
}
|
||||
);
|
||||
let eigval_series_neg = Scatter::new()
|
||||
.symbol(Symbol::Diamond)
|
||||
.symbol_size(6.0)
|
||||
.data(
|
||||
if hess_eigvals_neg.len() > 0 {
|
||||
hess_eigvals_neg
|
||||
.into_iter()
|
||||
.map(|(step, val)| (step, -val))
|
||||
.map(into_time_point)
|
||||
.collect()
|
||||
} else {
|
||||
vec![vec![Some(0.0), None::<f64>]]
|
||||
}
|
||||
);
|
||||
let chart = Chart::new()
|
||||
.animation(false)
|
||||
.data_zoom(DataZoom::new().y_axis_index(0).right(40).start(0).end(100))
|
||||
.data_zoom(DataZoom::new().y_axis_index(0).right(40))
|
||||
.x_axis(step_axis)
|
||||
.y_axis(eigval_axis)
|
||||
.grid(Grid::new().top(20).right(80).bottom(30).left(60))
|
||||
.series(eigval_series);
|
||||
.series(eigval_series_pos)
|
||||
.series(eigval_series_neg);
|
||||
renderer.render(CONTAINER_ID, &chart).unwrap();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue