From d7e187905f266ab8e69a5f975800b9839d66c73f Mon Sep 17 00:00:00 2001 From: John Dyreby Date: Sat, 28 Feb 2026 11:59:07 -0600 Subject: [PATCH 1/3] docs: pre-0.5.0 doc cleanup (#263) - Fix broken intra-doc links in twine-observers lib.rs: PlotObserver and ShowConfig are behind the plot feature gate, so the links broke when docs built without it. Use cfg_attr to include linked text only when the feature is active. - Add crate-level docs to twine-core: describes the shared abstractions (Model, Snapshot, Observer, problem traits) that make up the crate. - Add module-level docs to twine-solvers equation and optimization modules: explains the role of each module and lists the available solvers. - Clarify in README that GoodEnough is an example observer, not a real type, and that the point is its genericity over capability traits. --- README.md | 2 +- crates/core/src/lib.rs | 12 ++++++++++++ crates/observers/src/lib.rs | 12 ++++++++++-- crates/solvers/src/equation.rs | 12 ++++++++++++ crates/solvers/src/optimization.rs | 13 +++++++++++++ 5 files changed, 48 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7597211..c4a0c91 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ let solution = bisection::solve( // solution.status = StoppedByObserver ``` -`GoodEnough` is not tied to bisection. It works with any solver whose events expose a residual and whose actions support early stopping. The real power shows up in domain-specific observers — for example, an observer that recognizes a thermodynamic constraint violation and tells the solver to search elsewhere, turning an unsolvable problem into a solvable one. +`GoodEnough` is just an example, but notice what makes it work: it's generic over `E: HasResidual` and `A: CanStopEarly`, not over bisection specifically. Any observer written against capability traits like these works across all solvers that expose them — not just bisection. The real power shows up in domain-specific observers — for example, an observer that recognizes a thermodynamic constraint violation and tells the solver to search elsewhere, turning an unsolvable problem into a solvable one. ## Crates diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index bec7c15..1aafa0d 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,3 +1,15 @@ +//! Core traits and types for the Twine framework. +//! +//! This crate defines the shared abstractions that solvers, observers, and +//! models build on: +//! +//! - [`Model`] — a callable that maps a typed input to a typed output +//! - [`Snapshot`] — a captured input/output pair from a model call +//! - [`Observer`] — receives solver events and optionally returns control actions +//! - [`EquationProblem`], [`OptimizationProblem`], [`OdeProblem`] — problem +//! traits that adapt solver variables to model inputs and extract metrics from +//! outputs + mod model; mod observer; mod problems; diff --git a/crates/observers/src/lib.rs b/crates/observers/src/lib.rs index 21f161e..0a6cf8f 100644 --- a/crates/observers/src/lib.rs +++ b/crates/observers/src/lib.rs @@ -23,8 +23,16 @@ //! //! # Features //! -//! - `plot` — Enables [`PlotObserver`] and [`ShowConfig`] for visualizing solver -//! behavior via egui. This feature adds dependencies on `eframe` and `egui_plot`. +#![cfg_attr( + feature = "plot", + doc = "- `plot` — Enables [`PlotObserver`] and [`ShowConfig`] for visualizing solver \ + behavior via egui. This feature adds dependencies on `eframe` and `egui_plot`." +)] +#![cfg_attr( + not(feature = "plot"), + doc = "- `plot` — Enables `PlotObserver` and `ShowConfig` for visualizing solver \ + behavior via egui. This feature adds dependencies on `eframe` and `egui_plot`." +)] //! //! [`Observer`]: twine_core::Observer //! [`HasResidual`]: traits::HasResidual diff --git a/crates/solvers/src/equation.rs b/crates/solvers/src/equation.rs index 2b0abb3..5fda786 100644 --- a/crates/solvers/src/equation.rs +++ b/crates/solvers/src/equation.rs @@ -1,3 +1,15 @@ +//! Solvers for equation problems — finding roots of systems of equations. +//! +//! An [`EquationProblem`] maps solver variables `x: [f64; N]` to model inputs, +//! calls the model, and computes residuals. Solvers in this module drive those +//! residuals toward zero. +//! +//! # Solvers +//! +//! - [`bisection`] — guaranteed convergence on a bracketed interval +//! +//! [`EquationProblem`]: twine_core::EquationProblem + mod evaluate; pub use evaluate::{EvalError, EvaluateResult, Evaluation, evaluate}; diff --git a/crates/solvers/src/optimization.rs b/crates/solvers/src/optimization.rs index ed25ec1..227e8a0 100644 --- a/crates/solvers/src/optimization.rs +++ b/crates/solvers/src/optimization.rs @@ -1,3 +1,16 @@ +//! Solvers for optimization problems — minimizing or maximizing an objective. +//! +//! An [`OptimizationProblem`] maps solver variables `x: [f64; N]` to model +//! inputs, calls the model, and extracts a scalar objective. Solvers in this +//! module search for the `x` that minimizes or maximizes that objective. +//! +//! # Solvers +//! +//! - [`golden_section`] — derivative-free search over a bracketed interval for +//! unimodal functions +//! +//! [`OptimizationProblem`]: twine_core::OptimizationProblem + mod evaluate; pub use evaluate::{EvalError, EvaluateResult, Evaluation, evaluate}; From 6ac6030ebbede7adfed85cb60ec43feb5d9d9ed6 Mon Sep 17 00:00:00 2001 From: John Dyreby Date: Sat, 28 Feb 2026 12:02:39 -0600 Subject: [PATCH 2/3] docs: fix observer trait import path in README The capability traits live in twine_observers::traits, not at the crate root. The old import wouldn't compile. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4a0c91..2c9d378 100644 --- a/README.md +++ b/README.md @@ -95,7 +95,7 @@ Solvers are domain-agnostic and know nothing about what your model represents. O ```rust use twine_core::Observer; -use twine_observers::{HasResidual, CanStopEarly}; +use twine_observers::traits::{HasResidual, CanStopEarly}; /// Logs each iteration and stops early when the residual is good enough. struct GoodEnough { tolerance: f64, min_iters: usize, iter: usize } From d598e7d33e4d1a7de7fb4effc5746d8412cbe036 Mon Sep 17 00:00:00 2001 From: John Dyreby Date: Sat, 28 Feb 2026 12:06:54 -0600 Subject: [PATCH 3/3] docs: drop em dash in README observer paragraph --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2c9d378..351a1e6 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ let solution = bisection::solve( // solution.status = StoppedByObserver ``` -`GoodEnough` is just an example, but notice what makes it work: it's generic over `E: HasResidual` and `A: CanStopEarly`, not over bisection specifically. Any observer written against capability traits like these works across all solvers that expose them — not just bisection. The real power shows up in domain-specific observers — for example, an observer that recognizes a thermodynamic constraint violation and tells the solver to search elsewhere, turning an unsolvable problem into a solvable one. +`GoodEnough` is just an example, but notice what makes it work: it's generic over `E: HasResidual` and `A: CanStopEarly`, not over bisection specifically. Any observer written against capability traits like these works across all solvers that expose them, not just bisection. The real power shows up in domain-specific observers — for example, an observer that recognizes a thermodynamic constraint violation and tells the solver to search elsewhere, turning an unsolvable problem into a solvable one. ## Crates