resopt 0.2.0

Declarative constrained residual optimization in Rust
Documentation
  • Coverage
  • 20.73%
    40 out of 193 items documented1 out of 99 items with examples
  • Size
  • Source code size: 111.31 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 12.95 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 41s Average build duration of successful builds.
  • all releases: 43s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • Repository
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • djikeh

resopt

resopt is a Rust crate for declarative residual optimization with optional linear constraints.

It models problems of the form:

minimize_x   loss(Ax - b)
subject to   Cx = d
             Gx <= h
             l <= x <= u

The crate is designed around a small public API:

  • build a residual problem from matrices and vectors
  • add equality constraints, inequality constraints, and bounds
  • validate and classify the problem structure
  • solve with a backend such as Clarabel

Features

  • clarabel (default): enables the Clarabel backend

Current backend support:

  • Loss::L2Squared: solved by the Clarabel backend
  • Loss::L1, Loss::LInf, Loss::Huber: accepted by the model API, but not yet solved by Clarabel

Installation

[dependencies]
resopt = "0.2.0"

To disable the default backend:

[dependencies]
resopt = { version = "0.2.0", default-features = false }

Quick Start

use resopt::{
    ConstrainedResidualProblemBuilder, Loss, Matrix, SolveStatus,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let a = Matrix::from_row_major(
        3,
        2,
        vec![
            1.0, 0.0,
            0.0, 1.0,
            1.0, 1.0,
        ],
    )?;

    let problem = ConstrainedResidualProblemBuilder::new()
        .matrix(a)
        .target(vec![1.0, 2.0, 3.0])
        .loss(Loss::L2Squared)
        .build()?;

    let result = problem.solve()?;

    assert_eq!(result.status(), SolveStatus::Solved);
    assert!(result.solution().is_some());

    Ok(())
}

Constrained Example

use resopt::{
    Bounds, ConstrainedResidualProblem, LinearEqualities, LinearInequalities,
    LinearResidual, Loss, Matrix,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let residual = LinearResidual::new(
        Matrix::from_row_major(3, 2, vec![1.0, 0.0, 0.0, 1.0, 1.0, 1.0])?,
        vec![1.0, 2.0, 2.5],
    )?;

    let eq = LinearEqualities::new(
        Matrix::from_row_major(1, 2, vec![1.0, -1.0])?,
        vec![0.0],
    )?;

    let ineq = LinearInequalities::new(
        Matrix::from_row_major(1, 2, vec![1.0, 1.0])?,
        vec![3.0],
    )?;

    let bounds = Bounds::new(
        vec![Some(0.0), Some(0.0)],
        vec![Some(10.0), Some(10.0)],
    )?;

    let result = ConstrainedResidualProblem::new(residual, Loss::L2Squared)?
        .add_equalities(eq)?
        .add_inequalities(ineq)?
        .with_bounds(bounds)?
        .solve()?;

    println!("{:?}", result.status());
    Ok(())
}

See examples/basic_l2.rs and examples/clarabel_constrained_l2.rs for runnable examples.

Crate Status

The core modeling API is stable enough for experimentation, but the solver surface is still intentionally small.

For Loss::L2Squared, the Clarabel backend now applies an explicit residual formulation and optional scaling through SolveOptions, which improves robustness on poorly scaled problems.

If you publish results or build production workflows on top of resopt, pin the crate version and validate solver behavior on your problem family.

Development

The repository includes a pre-commit hook installer:

./scripts/install-git-hooks.ps1

The hook runs:

  • cargo fmt --check
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --all-features