From 402f5609c0af9fb54eb038e8cefffb259463a121 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Mon, 9 Jun 2025 22:21:34 -0700 Subject: [PATCH] Add a realization status indicator --- app-proto/examples/common/mod.rs | 4 +-- app-proto/main.css | 42 ++++++++++++++++++++++------ app-proto/src/assembly.rs | 48 +++++++++++++++++++++----------- app-proto/src/diagnostics.rs | 40 +++++++++++++++++++++++++- 4 files changed, 105 insertions(+), 29 deletions(-) diff --git a/app-proto/examples/common/mod.rs b/app-proto/examples/common/mod.rs index b1b91b5..9b6c492 100644 --- a/app-proto/examples/common/mod.rs +++ b/app-proto/examples/common/mod.rs @@ -11,8 +11,8 @@ pub fn print_title(title: &str) { pub fn print_realization_diagnostics(realization_result: &RealizationResult) { let RealizationResult { result, history } = realization_result; println!(); - if let Err(ref msg) = result { - println!("❌️ {msg}"); + if let Err(ref message) = result { + println!("❌️ {message}"); } else { println!("✅️ Target accuracy achieved!"); } diff --git a/app-proto/main.css b/app-proto/main.css index fb16698..a73d5a5 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -18,6 +18,17 @@ body { font-family: 'Fira Sans', sans-serif; } +.invalid { + color: var(--text-invalid); +} + +.status { + width: 20px; + text-align: center; + font-family: 'Noto Emoji'; + font-style: normal; +} + /* sidebar */ #sidebar { @@ -138,6 +149,7 @@ details[open]:has(li) .element-switch::after { } .regulator-input { + margin-right: 4px; color: inherit; background-color: inherit; border: 1px solid var(--border); @@ -159,14 +171,6 @@ details[open]:has(li) .element-switch::after { border-color: var(--border-invalid); } -.status { - width: 20px; - padding-left: 4px; - text-align: center; - font-family: 'Noto Emoji'; - font-style: normal; -} - .regulator-input.invalid + .status::after, details:has(.invalid):not([open]) .status::after { content: '⚠'; color: var(--text-invalid); @@ -174,8 +178,28 @@ details[open]:has(li) .element-switch::after { /* diagnostics */ -#loss-history { +#diagnostics { margin: 10px; +} + +#realization-status { + display: flex; +} + +#realization-status .status { + margin-right: 4px; +} + +#realization-status .status::after { + content: '✓'; +} + +#realization-status.invalid .status::after { + content: '⚠'; +} + +#loss-history { + margin-top: 10px; background-color: var(--display-background); border-radius: 8px; } diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index f466108..9a4b934 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -553,6 +553,7 @@ pub struct Assembly { pub elements_by_id: Signal>>, // realization diagnostics + pub realization_status: Signal>, pub descent_history: Signal } @@ -563,6 +564,7 @@ impl Assembly { regulators: create_signal(BTreeSet::new()), tangent: create_signal(ConfigSubspace::zero(0)), elements_by_id: create_signal(BTreeMap::default()), + realization_status: create_signal(Ok(())), descent_history: create_signal(DescentHistory::new()) } } @@ -699,32 +701,44 @@ impl Assembly { ); /* DEBUG */ - // report the outcome of the search - if let Err(ref msg) = result { - console_log!("❌️ {msg}"); + // report the outcome of the search in the browser console + if let Err(ref message) = result { + console_log!("❌️ {message}"); } else { console_log!("✅️ Target accuracy achieved!"); } console_log!("Steps: {}", history.scaled_loss.len() - 1); console_log!("Loss: {}", history.scaled_loss.last().unwrap()); - // record realization diagnostics + // report the loss history self.descent_history.set(history); - if let Ok(Realization { config, tangent }) = result { - /* DEBUG */ - // report the tangent dimension - console_log!("Tangent dimension: {}", tangent.dim()); - - // read out the solution - for elt in self.elements.get_clone_untracked() { - elt.representation().update( - |rep| rep.set_column(0, &config.column(elt.column_index().unwrap())) - ); + match result { + Ok(Realization { config, tangent }) => { + /* DEBUG */ + // report the tangent dimension + console_log!("Tangent dimension: {}", tangent.dim()); + + // report the realization status + self.realization_status.set(Ok(())); + + // read out the solution + for elt in self.elements.get_clone_untracked() { + elt.representation().update( + |rep| rep.set_column(0, &config.column(elt.column_index().unwrap())) + ); + } + + // save the tangent space + self.tangent.set_silent(tangent); + }, + Err(message) => { + // report the realization status. the `Err(message)` we're + // setting the status to has a different type than the + // `Err(message)` we received from the match: we're changing the + // `Ok` type from `Realization` to `()` + self.realization_status.set(Err(message)) } - - // save the tangent space - self.tangent.set_silent(tangent); } } diff --git a/app-proto/src/diagnostics.rs b/app-proto/src/diagnostics.rs index 6d3d145..a73a793 100644 --- a/app-proto/src/diagnostics.rs +++ b/app-proto/src/diagnostics.rs @@ -10,9 +10,37 @@ use sycamore::prelude::*; use crate::AppState; +// a realization status indicator +#[component] +pub fn RealizationStatus() -> View { + let state = use_context::(); + let realization_status = state.assembly.realization_status; + view! { + div( + id="realization-status", + class=realization_status.with( + |status| match status { + Ok(_) => "", + Err(_) => "invalid" + } + ) + ) { + div(class="status") + div { + (realization_status.with( + |status| match status { + Ok(_) => "Target accuracy achieved".to_string(), + Err(message) => message.clone() + } + )) + } + } + } +} + // a plot of the loss history from the last realization #[component] -pub fn Diagnostics() -> View { +pub fn LossHistory() -> View { const CONTAINER_ID: &str = "loss-history"; let state = use_context::(); let renderer = WasmRenderer::new_opt(None, Some(180)).theme(Theme::Walden); @@ -62,4 +90,14 @@ pub fn Diagnostics() -> View { view! { div(id=CONTAINER_ID) } +} + +#[component] +pub fn Diagnostics() -> View { + view! { + div(id="diagnostics") { + RealizationStatus {} + LossHistory {} + } + } } \ No newline at end of file