A few years a ago we introduced an official rust API for MOSEK. The API extended our optimizer API to the rust language. The optimizer API is designed to be a thin interface to the native C optimizer API.
However, MOSEK has in addition to the optimizer API also the fusion API. The fusion API is specifically designed to build conic optimization models in a simple and expressive manner. With its focus on model building the fusion API acts as a compliment to the optimizer API. That compliment has now been extended to Rust.
However, due to some language specific attributes of Rust in combination of how the fusion API is constructed there is not a straight forward way to extend the fusion API to Rust. Hence, we like to introduce mosekcomodel!
The Rust crate mosekcomodel is a Rust package for formulating and solving convex conic optimization models. While it looks somewhat like the MOSEK fusion API (for Python, Java, .NET and C++), it is a
fully Rust native package exploiting Rust's type system zero-cost abstractions to make it simpler and faster to write correct models.
The crate provides a model-oriented interface for defining conic models and a
library of functionality for formulating affine expressions.
- Linear bounds: Equalities and inequalities,
- Second order conic constraints: Quadric cone and rotated quadratic cone,
- Semidefinite cone and scaled vectorized semidefinite cone,
- primal and dual power cones
- Primal and dual geometric mean cones, and
- Primal and dual exponential cones.
use mosekcomodel::*; | |
let mut model = Model::new(Some("MyModel")); | |
let x = model.variable(Some("x"), nonnegative().with_shape(&[3])); | |
model.constraint(Some("c"), x.add(x.mul(2.0)), in_range(-5.0,5.0)); | |
... | |
model.solve(); | |
... | |
let xsol = model.primal_solution(SolutionType::Default, &x); |
use mosekcomodel::*; | |
/// Computes the optimal portfolio for a given risk | |
/// | |
/// # Arguments | |
/// * `n` Number of assets | |
/// * `mu` An n dimmensional vector of expected returns | |
/// * `gt` A matrix with n columns so (GT')*GT = covariance matrix | |
/// * `x0` Initial holdings | |
/// * `w` Initial cash holding | |
/// * `gamma` Maximum risk (=std. dev) accepted | |
/// # Returns | |
/// The expected return for the optimal portfolio. | |
fn basic_markowitz( n : usize, | |
mu : &[f64], | |
gt : &NDArray<2>, | |
x0 : &[f64], | |
w : f64, | |
gamma : f64) -> f64 { | |
let mut model = Model::new(Some("Basic Markowitz")); | |
// Redirect log output from the solver to stdout for debugging. | |
// if uncommented. | |
model.set_log_handler(|msg| print!("{}",msg)); | |
// Defines the variables (holdings). Shortselling is not allowed. | |
let x = model.variable(Some("x"), nonnegative().with_shape(&[n]))); | |
// Maximize expected return | |
model.objective(Some("obj"), Sense::Maximize, x.dot(mu)); | |
// The amount invested must be identical to intial wealth | |
model.constraint(Some("budget"), x.sum(), equal_to(w+x0.iter().sum::<f64>())); | |
// Imposes a bound on the risk | |
model.constraint(Some("risk"), | |
vstack![Expr::from(gamma).reshape(&[1]), | |
gt.mul(&x)], in_quadratic_cone()); | |
// Solves the model. | |
model.solve(); | |
let xlvl = model.primal_solution(SolutionType::Default, &x).unwrap(); | |
mu.iter().zip(xlvl.iter()).map(|(&a,&b)| a*b).sum() | |
} |