odesign-derive 0.1.9

odesign is an optimal design of experiments library written in pure rust.
Documentation

odesign

odesign is an optimal design of experiments library written in pure rust. There are at least these following use cases:

  • Fast calculation of optimal designs of arbitrary linear models with custom design bounds and optimalities.
  • Research in area of optimal designs; e.g. I am working on a new optimal design feature selection algorithm, a mixture of SFFS, D-, C- and Measurements-Costs-Optimality, allowing to perform model feature selection and measurements alternating.

Get started

Please have a look at the book on odesign.rs for a high level introduction and theoretical background and at the docs on docs.rs/odesign for the implementation details.

Roadmap

  • Optimalities:
    • D-Optimality
    • C-Optimality
    • Custom Optimality
    • A-Optimality
    • Costs Optimality
  • Design Bounds:
    • Cubic Bounds
    • Custom Bounds
  • Documentation:
    • documentation of the optimal design solver backed by "adaptive grid semidefinite programming for finding optimal designs" (doi: 10.1007/s11222-017-9741-y)
  • Research:
    • New optimal design feature selection algorithm, a mixture of SFFS, D-, C- and Measurements-Costs-Optimality, allowing to perform model feature selection and measurements alternating

Basic Example

Run the example:

cargo run --release --example basic

In short, this is a basic example of an optimal design of the simple polynomial 1 + x within design bounds [-1, +1] and 101 equally distributed grid points as an init design.

use nalgebra::{SVector, Vector1};
use num_dual::DualNum;
use odesign::{
    DOptimality, Feature, FeatureFunction, FeatureSet, LinearModel, OptimalDesign, Result,
};
use std::sync::Arc;

#[derive(Feature)]
#[dimension = 1]
struct Monomial {
    i: i32,
}

impl FeatureFunction<1> for Monomial {
    fn f<D: DualNum<f64>>(&self, x: &SVector<D, 1>) -> D {
        x[0].powi(self.i)
    }
}

// f(x): 1 + x
fn main() -> Result<()> {
    let mut fs = FeatureSet::new();
    let c: Arc<_> = Monomial { i: 0 }.into();
    fs.push(c);
    let c: Arc<_> = Monomial { i: 1 }.into();
    fs.push(c);

    let lm = LinearModel::new(fs.features);

    let optimality: Arc<_> = DOptimality::new(lm.into()).into();
    let lower = Vector1::new(-1.0);
    let upper = Vector1::new(1.0);
    let q = Vector1::new(101);

    let mut od = OptimalDesign::new()
        .with_optimality(optimality)
        .with_bound_args(lower, upper)?
        .with_init_design_grid_args(lower, upper, q)?;
    od.solve();

    println!("{od}");

    Ok(())
}
// Output
// ---------- Design ----------
// Weight  Support Vector
// 0.5000  [ -1.0000 ]
// 0.5000  [ +1.0000 ]
// -------- Statistics --------
// Optimality measure: 1.000000
// No. support vectors: 2
// Iterations: 1
// ----------------------------

Advanced Example

Run the example:

cargo run --release --example polynomial-3dim

As a more complex example please take a look at this 3 dimensional polynomial example with monoms lower than a degree of 3 within a design space of [-1, -1, -1] x [+1, +1, +1] and an initial design grid of 11 x 11 x 11 points.

use nalgebra::{SVector, Vector3};
use num_dual::DualNum;
use odesign::{
    DOptimality, Feature, FeatureFunction, FeatureSet, LinearModel, OptimalDesign, Result,
};
use std::sync::Arc;

#[derive(Feature)]
#[dimension = 3]
struct Monomial {
    i: i32,
    j: i32,
    k: i32,
}

impl FeatureFunction<3> for Monomial {
    fn f<D: DualNum<f64>>(&self, x: &SVector<D, 3>) -> D {
        x[0].powi(self.i) * x[1].powi(self.j) * x[2].powi(self.k)
    }
}

// f(x, y, z):  1 + x + y + z
//              + x * y + x * z + y * y
//              + x ^ 2 + y ^ 2 + z ^ 2
fn main() -> Result<()> {
    let mut fs = FeatureSet::new();
    for i in 0..3 {
        for j in 0..3 {
            for k in 0..3 {
                if i + j + k < 3 {
                    let c: Arc<_> = Monomial { i, j, k }.into();
                    fs.push(c);
                }
            }
        }
    }

    let lm = LinearModel::new(fs.features);

    let q = Vector3::new(11, 11, 11);
    let lower = Vector3::new(-1., -1., -1.);
    let upper = Vector3::new(1., 1., 1.);
    let optimality = Arc::new(DOptimality::new(lm.into()));
    let mut od = OptimalDesign::new()
        .with_optimality(optimality)
        .with_bound_args(lower, upper)?
        .with_init_design_grid_args(lower, upper, q)?;
    od.solve();

    println!("{od}");

    Ok(())
}
// Output
// -------------- Design ---------------
// Weight	Support Vector
// 0.0689	[ -1.0000, -1.0000, -1.0000 ]
// 0.0251	[ -1.0000, -1.0000, +0.0000 ]
// 0.0689	[ -1.0000, -1.0000, +1.0000 ]
// 0.0251	[ -1.0000, +0.0000, -1.0000 ]
// 0.0205	[ -1.0000, +0.0000, +0.0000 ]
// 0.0251	[ -1.0000, +0.0000, +1.0000 ]
// 0.0689	[ -1.0000, +1.0000, -1.0000 ]
// 0.0251	[ -1.0000, +1.0000, +0.0000 ]
// 0.0689	[ -1.0000, +1.0000, +1.0000 ]
// 0.0251	[ +0.0000, -1.0000, -1.0000 ]
// 0.0205	[ +0.0000, -1.0000, +0.0000 ]
// 0.0251	[ +0.0000, -1.0000, +1.0000 ]
// 0.0205	[ +0.0000, +0.0000, -1.0000 ]
// 0.0245	[ +0.0000, +0.0000, +0.0000 ]
// 0.0205	[ +0.0000, +0.0000, +1.0000 ]
// 0.0251	[ +0.0000, +1.0000, -1.0000 ]
// 0.0205	[ +0.0000, +1.0000, +0.0000 ]
// 0.0251	[ +0.0000, +1.0000, +1.0000 ]
// 0.0689	[ +1.0000, -1.0000, -1.0000 ]
// 0.0251	[ +1.0000, -1.0000, +0.0000 ]
// 0.0689	[ +1.0000, -1.0000, +1.0000 ]
// 0.0251	[ +1.0000, +0.0000, -1.0000 ]
// 0.0205	[ +1.0000, +0.0000, +0.0000 ]
// 0.0251	[ +1.0000, +0.0000, +1.0000 ]
// 0.0689	[ +1.0000, +1.0000, -1.0000 ]
// 0.0251	[ +1.0000, +1.0000, +0.0000 ]
// 0.0689	[ +1.0000, +1.0000, +1.0000 ]
// ------------ Statistics -------------
// Optimality measure: 0.474478
// No. support vectors: 27
// Iterations: 2
// -------------------------------------

Community

Mailing lists

Tickets

The tracker on todo.sr.ht/~maunke/odesign is for confirmed bugs and confirmed feature requests only.

Before creating a ticket, search for existing (possibly already fixed) issues, on the docs or in the mailing list archives: odesign-discuss, odesign-devel.

If you cannot find anything describing your issue or if you have a question, ask on one of the the mailing lists first. You will be asked to file a ticket if appropriate.