This introduces a framework for adding more diagnostics panels.
This commit is contained in:
parent
059224e269
commit
0be7448e24
3 changed files with 124 additions and 14 deletions
|
@ -182,14 +182,23 @@ details[open]:has(li) .element-switch::after {
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#diagnostics-bar {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
#realization-status {
|
#realization-status {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#realization-status .status {
|
#realization-status .status {
|
||||||
margin-right: 4px;
|
margin-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#realization-status :not(.status) {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
#realization-status .status::after {
|
#realization-status .status::after {
|
||||||
content: '✓';
|
content: '✓';
|
||||||
}
|
}
|
||||||
|
@ -198,8 +207,12 @@ details[open]:has(li) .element-switch::after {
|
||||||
content: '⚠';
|
content: '⚠';
|
||||||
}
|
}
|
||||||
|
|
||||||
#loss-history {
|
.diagnostics-panel {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
min-height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diagnostics-chart {
|
||||||
background-color: var(--display-background);
|
background-color: var(--display-background);
|
||||||
border: 1px solid var(--border);
|
border: 1px solid var(--border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|
|
@ -3,16 +3,28 @@ use charming::{
|
||||||
WasmRenderer,
|
WasmRenderer,
|
||||||
component::{Axis, Grid},
|
component::{Axis, Grid},
|
||||||
element::AxisType,
|
element::AxisType,
|
||||||
series::Line,
|
series::{Line, Scatter},
|
||||||
theme::Theme
|
|
||||||
};
|
};
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
|
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct DiagnosticsState {
|
||||||
|
active_tab: Signal<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiagnosticsState {
|
||||||
|
fn new(initial_tab: String) -> DiagnosticsState {
|
||||||
|
DiagnosticsState {
|
||||||
|
active_tab: create_signal(initial_tab)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// a realization status indicator
|
// a realization status indicator
|
||||||
#[component]
|
#[component]
|
||||||
pub fn RealizationStatus() -> View {
|
fn RealizationStatus() -> View {
|
||||||
let state = use_context::<AppState>();
|
let state = use_context::<AppState>();
|
||||||
let realization_status = state.assembly.realization_status;
|
let realization_status = state.assembly.realization_status;
|
||||||
view! {
|
view! {
|
||||||
|
@ -38,12 +50,12 @@ pub fn RealizationStatus() -> View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// a plot of the loss history from the last realization
|
// the loss history from the last realization
|
||||||
#[component]
|
#[component]
|
||||||
pub fn LossHistory() -> View {
|
fn LossHistory() -> View {
|
||||||
const CONTAINER_ID: &str = "loss-history";
|
const CONTAINER_ID: &str = "loss-history";
|
||||||
let state = use_context::<AppState>();
|
let state = use_context::<AppState>();
|
||||||
let renderer = WasmRenderer::new_opt(None, Some(180)).theme(Theme::Walden);
|
let renderer = WasmRenderer::new_opt(None, Some(178));
|
||||||
|
|
||||||
on_mount(move || {
|
on_mount(move || {
|
||||||
create_effect(move || {
|
create_effect(move || {
|
||||||
|
@ -88,16 +100,100 @@ pub fn LossHistory() -> View {
|
||||||
});
|
});
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
div(id=CONTAINER_ID)
|
div(id=CONTAINER_ID, class="diagnostics-chart")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// the spectrum of the Hessian during the last realization
|
||||||
|
#[component]
|
||||||
|
fn SpectrumHistory() -> View {
|
||||||
|
const CONTAINER_ID: &str = "spectrum-history";
|
||||||
|
let state = use_context::<AppState>();
|
||||||
|
let renderer = WasmRenderer::new(478, 178);
|
||||||
|
|
||||||
|
on_mount(move || {
|
||||||
|
create_effect(move || {
|
||||||
|
// get the spectrum of the Hessian at each step
|
||||||
|
let hess_eigvals = state.assembly.descent_history.with(
|
||||||
|
|history| history.hess_eigvals
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(
|
||||||
|
|(step, eigvals)| eigvals.iter().map(
|
||||||
|
move |val| vec![step as f64, *val]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.flatten()
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
|
||||||
|
// initialize the chart axes and series
|
||||||
|
let step_axis = Axis::new();
|
||||||
|
let eigval_axis = Axis::new();
|
||||||
|
|
||||||
|
// 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 chart = Chart::new()
|
||||||
|
.animation(false)
|
||||||
|
.x_axis(step_axis)
|
||||||
|
.y_axis(eigval_axis)
|
||||||
|
.grid(Grid::new().top(20).right(40).bottom(30).left(60))
|
||||||
|
.series(eigval_series);
|
||||||
|
renderer.render(CONTAINER_ID, &chart).unwrap();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
view! {
|
||||||
|
div(id=CONTAINER_ID, class="diagnostics-chart")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component(inline_props)]
|
||||||
|
fn DiagnosticsPanel(name: &'static str, children: Children) -> View {
|
||||||
|
let diagnostics_state = use_context::<DiagnosticsState>();
|
||||||
|
view! {
|
||||||
|
div(
|
||||||
|
class="diagnostics-panel",
|
||||||
|
"hidden"=diagnostics_state.active_tab.with(
|
||||||
|
|active_tab| {
|
||||||
|
if active_tab == name {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
(children)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Diagnostics() -> View {
|
pub fn Diagnostics() -> View {
|
||||||
|
let diagnostics_state = DiagnosticsState::new("loss".to_string());
|
||||||
|
let active_tab = diagnostics_state.active_tab.clone();
|
||||||
|
provide_context(diagnostics_state);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
div(id="diagnostics") {
|
div(id="diagnostics") {
|
||||||
RealizationStatus {}
|
div(id="diagnostics-bar") {
|
||||||
LossHistory {}
|
RealizationStatus {}
|
||||||
|
select(bind:value=active_tab) {
|
||||||
|
option(value="loss") { "Loss" }
|
||||||
|
option(value="spectrum") { "Spectrum" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DiagnosticsPanel(name="loss") { LossHistory {} }
|
||||||
|
DiagnosticsPanel(name="spectrum") { SpectrumHistory {} }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -256,7 +256,7 @@ pub struct DescentHistory {
|
||||||
pub config: Vec<DMatrix<f64>>,
|
pub config: Vec<DMatrix<f64>>,
|
||||||
pub scaled_loss: Vec<f64>,
|
pub scaled_loss: Vec<f64>,
|
||||||
pub neg_grad: Vec<DMatrix<f64>>,
|
pub neg_grad: Vec<DMatrix<f64>>,
|
||||||
pub min_eigval: Vec<f64>,
|
pub hess_eigvals: Vec::<DVector<f64>>,
|
||||||
pub base_step: Vec<DMatrix<f64>>,
|
pub base_step: Vec<DMatrix<f64>>,
|
||||||
pub backoff_steps: Vec<i32>
|
pub backoff_steps: Vec<i32>
|
||||||
}
|
}
|
||||||
|
@ -267,7 +267,7 @@ impl DescentHistory {
|
||||||
config: Vec::<DMatrix<f64>>::new(),
|
config: Vec::<DMatrix<f64>>::new(),
|
||||||
scaled_loss: Vec::<f64>::new(),
|
scaled_loss: Vec::<f64>::new(),
|
||||||
neg_grad: Vec::<DMatrix<f64>>::new(),
|
neg_grad: Vec::<DMatrix<f64>>::new(),
|
||||||
min_eigval: Vec::<f64>::new(),
|
hess_eigvals: Vec::<DVector<f64>>::new(),
|
||||||
base_step: Vec::<DMatrix<f64>>::new(),
|
base_step: Vec::<DMatrix<f64>>::new(),
|
||||||
backoff_steps: Vec::<i32>::new(),
|
backoff_steps: Vec::<i32>::new(),
|
||||||
}
|
}
|
||||||
|
@ -467,11 +467,12 @@ pub fn realize_gram(
|
||||||
hess = DMatrix::from_columns(hess_cols.as_slice());
|
hess = DMatrix::from_columns(hess_cols.as_slice());
|
||||||
|
|
||||||
// regularize the Hessian
|
// regularize the Hessian
|
||||||
let min_eigval = hess.symmetric_eigenvalues().min();
|
let hess_eigvals = hess.symmetric_eigenvalues();
|
||||||
|
let min_eigval = hess_eigvals.min();
|
||||||
if min_eigval <= 0.0 {
|
if min_eigval <= 0.0 {
|
||||||
hess -= reg_scale * min_eigval * DMatrix::identity(total_dim, total_dim);
|
hess -= reg_scale * min_eigval * DMatrix::identity(total_dim, total_dim);
|
||||||
}
|
}
|
||||||
history.min_eigval.push(min_eigval);
|
history.hess_eigvals.push(hess_eigvals);
|
||||||
|
|
||||||
// project the negative gradient and negative Hessian onto the
|
// project the negative gradient and negative Hessian onto the
|
||||||
// orthogonal complement of the frozen subspace
|
// orthogonal complement of the frozen subspace
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue