Skip to content

Repository files navigation

Twine

Twine is a Rust framework for defining and solving numerical problems.

Models are useful for solving problems. Twine ties together your Model, a Problem you want to solve, and a Solver that does the work.

How It Works

Define a model:

use std::convert::Infallible;use twine_core::Model;/// A simple polynomial: f(x) = x³ - 3xstructPolynomial;implModelforPolynomial{typeInput = f64;typeOutput = f64;typeError = Infallible;fncall(&self,x:&f64) -> Result<f64,Self::Error>{Ok(x.powi(3) - 3.0* x)}}

Find where the output equals a target by defining an equation problem:

use std::convert::Infallible;use twine_core::EquationProblem;use twine_solvers::equation::bisection;/// Drive the model output to a target value.structTarget(f64);implEquationProblem<1>forTarget{typeInput = f64;typeOutput = f64;typeError = Infallible;fninput(&self,x:&[f64;1]) -> Result<f64,Self::Error>{Ok(x[0])}fnresiduals(&self,_input:&f64,output:&f64) -> Result<[f64;1],Self::Error>{Ok([output - self.0])}}let solution = bisection::solve_unobserved(&Polynomial,&Target(-2.0),[0.0,2.0],&bisection::Config::default(),).unwrap();// solution.x = 1.0 (where x³ - 3x = -2)

Find the minimum or maximum by defining an optimization problem with the same model:

use std::convert::Infallible;use twine_core::OptimizationProblem;/// Define an objective from the model input and output./// Solvers choose whether to minimize or maximize.structObjectiveOutput;implOptimizationProblem<1>forObjectiveOutput{typeInput = f64;typeOutput = f64;typeError = Infallible;fninput(&self,x:&[f64;1]) -> Result<f64,Self::Error>{Ok(x[0])}fnobjective(&self,_input:&f64,output:&f64) -> Result<f64,Self::Error>{Ok(*output)}}// Use with any optimization solver// golden_section::minimize(&Polynomial, ObjectiveOutput, [-2.0, 2.0]) → x = 1.0// golden_section::maximize(&Polynomial, ObjectiveOutput, [-2.0, 2.0]) → x = -1.0// Same model, same problem, same bracket, just minimize vs maximize

These examples use a simple polynomial, but the same pattern works with any Model, including large, multi-physics engineering systems.

Observers

Solvers are domain-agnostic and know nothing about what your model represents. Observers bridge that gap by receiving events during execution and steering solver behavior based on domain knowledge you provide.

use twine_core::Observer;use twine_observers::traits::{HasResidual,CanStopEarly};/// Logs each iteration and stops early when the residual is good enough.structGoodEnough{tolerance:f64,min_iters:usize,iter:usize}impl<E:HasResidual,A:CanStopEarly>Observer<E,A>forGoodEnough{fnobserve(&mutself,event:&E) -> Option<A>{self.iter += 1;let r = event.residual();println!("iter {}: residual = {r:.6}",self.iter);ifself.iter >= self.min_iters && r.abs() < self.tolerance{returnSome(A::stop_early());}None}}let observer = GoodEnough{tolerance:0.1,min_iters:5,iter:0};let solution = bisection::solve(&Polynomial,&Target(0.0),[0.0,3.0],&bisection::Config::default(), observer,).unwrap();// iter 1: residual = 2.500000// iter 2: residual = -2.750000// iter 3: residual = -0.484375// iter 4: residual = 0.785156// iter 5: residual = 0.097656// 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.

Crates

  • twine-core: The Model trait, Problem traits, and the Observer trait.
  • twine-solvers: Solver algorithms organized by problem type (e.g., equation::bisection, optimization::golden_section).
  • twine-observers: Capability traits for cross-solver observers (e.g., HasResidual, CanStopEarly) and visualization tools like PlotObserver.

Twine Models

Twine is domain-agnostic by design. For opinionated, domain-specific models and model-building tools, see the companion project Twine Models.

About

A Rust framework for defining and solving numerical problems

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages