Crate mosekcomodel

Crate mosekcomodel 

Source
Expand description

§MosekModel library

MosekModel is a crate for setting up conic optimization models. The crate does not directly link to a solver - these links are implemented in separate crates, currently:

  • mosekcomodel_mosek a backend for MOSEK via mosek.rust, supporting
    • Linear and conic variables and constraints
    • Integer variables
    • Disjunctive constraints
  • mosekcomodel_highs a backend for HIGHS, supporting
    • Linear constraints
    • Integer Variables The crate defines a dummy backend that allows inputting data, but cannot solve, write or do anything useful with it.

The [Model] object encapsulates a model of the form $$ \begin{array}{ll} \mathrm{min/max} & c^t x \\ \mathrm{such\ that} & A x + b \in K_c\\ & X \in K_x \end{array} $$

where \(K_c = K_{c_0}\times\cdots\times K_{c_m}\), \(K_x = K_{x_0} \times\cdots\times K_{x_n}\), each \(K_{c_i}\) and \(K_{x_i}\) is a conic domain from the currently supported set plus an offset:

  • Primal power cone(s) (see in_power_cone, in_power_cones): $$ \left\{ x \in R^n | x_2^{\beta_1} \cdots x_k^{\beta_k} \geq \sqrt{x_{k+1}^2 \cdots x_n^2}, x_0,\ldots, x_k \geq 0 \right\} $$
  • Dual power cone(s) (see in_dual_power_cone, in_dual_power_cones): $$ \left\{ x \in R^n | (x_1/\beta_1)^{\beta_1} \cdots (x_k)^{\beta_k} \geq \sqrt{x_{k+1}^2 \cdots x_n^2}, x_0,\ldots, x_k \geq 0 \right\} $$
  • Primal exponential cone(s) (see in_exponential_cone, in_exponential_cones): $$ \left\{ x \in R^3 | x_1 \geq x_1 e^{x_3/x_2}, x_0, x_1 \geq 0 \right\} $$
  • Dual exponential cone(s) (see in_dual_exponential_cone, in_dual_exponential_cones): $$ \left\{ x \in R^3 | x_1 \geq -x_3 e^{-1} e^{x_2/x_3}, x_3 \geq 0, x_1 \geq 0 \right\} $$
  • Primal geometric mean cone(s) (see in_geometric_mean_cone, in_geometric_mean_cones): $$ \left\{ x \in R^n| (x_1\cdots x_{n-1})^{1/(n-1)} |x_n|, x_1,\ldots,x_{n-1} \geq 0\right\} $$
  • Dual geometric mean cone(s) (see in_dual_geometric_mean_cone, in_dual_geometric_mean_cones): $$ \left\{ x \in R^n | (n-1)(x_1 \cdots x_{n-1})^{1/(n-1)} |x_n|, x_1,\ldots,x_{n-1} \geq 0\right\} $$
  • Scaled vectorized positive semidefinite cone(s) (see in_svecpsd_cone, in_svecpsd_cones). For a n dimensional positive symmetric matrix this is the scaled lower triangular part of the matrix in column-major format, i.e. $$ \left\{ x \in R^{n(n+1)/2} | \mathrm{sMat}(x) \in S_+^n \right\} $$ where $$ \mathrm{sMat}(x) = \left[ \begin{array}{cccc} x_1 & x_2/\sqrt{2} & \cdots & x_n/\sqrt{2} \\ x_2/\sqrt{2} & x_n+1 & \cdots & x_{2n-1}/\sqrt{2} \\ & & \cdots & \\ x_n/\sqrt{2} & x_{2n-1}/\sqrt{2} & \cdots & x_{n(n+1_/2}^2 \end{array} \right] $$
  • Symmetric positive semidefinite cones $$ X \in \mathcal{S}^n_+ $$

§Expressions and shapes

The central traits for expressions are ExprTrait, which all objects that must act as expressions have to implement, and IntoExpr which is anything that can be turned into an ExprTrait. For example, Variable Vec<f64>, f64 and NDArray implement IntoExpr since they can be turned into a expression, while the various expression structs (e.g. Expr, expr::ExprAdd, expr::ExprStack etc.) implement ExprTrait. Note that expressions are consumed when creating new expressions, while variables and constants can be passed by reference and will be cloned. This is because the expression constructing functions accept IntoExprs rather than ExprTraits. For example, an add function might look like this:

use mosekcomodel::{ExprTrait,IntoExpr};

struct ExprAdd<const N : usize,E1,E2> 
    where 
        E1 : ExprTrait<N>,
        E2 : ExprTrait<N>
{
    e1 : E1,
    e2 : E2
}
fn add<const N : usize, E1,E2>( e1 : E1, e2 : E2 ) -> ExprAdd<N,E1::Result,E2::Result> 
    where E1 : IntoExpr<N>,
          E2 : IntoExpr<N>
{
    ExprAdd{
        e1 : e1.into_expr(),
        e2 : e2.into_expr()
    }
}

Now, when IntoExpr is implemented for all ExprTrait, as well as for both &Variable<N> and for Variable<N>, we can pass an expression, a variable or a variable reference to the function.

Constraint, domains, variables and expressions have shapes, and the latter three can be either dense or sparse meanning that some entries are fixed to zero. A shaped variable, expression and constraint is basically a multi-dimensional array of scalar variables, affine expressions and scalar constraints respectively.

When reshaping an object it is important to understand the order of the scalar elements in the multi-dimensional array. In MosekModel everything is in row-major order, i.e. for a two-dimensional array, where the first dimension is the height and the second is the width $$ \left[\begin{array}{cc} a & b \\ c & d \end{array}\right] $$ the elements are ordered as [a,b,c,d]. More generally, elements are ordered by inner dimension first.

A scalar is an n-dimensional object with n=0.

In MosekModel the dimension of variables, constraints, expressions and domains are part of the object type, so only correct combinations of dimensionality are allowed. The actual shapes still have to be checked at runtime.

§Domains

A domain is an object that indicates things like type (cone type or domain type), cone parameters, right-hand sides, shape and sparsity pattern. This is used when creating constraint or variables to define their properties. Normally, the domain is not created directly, but rather an object implementing IntoDomain (for variables) or IntoShapedDomain (for constraints) is created and passed to the relevant function that will then validate it. Such an object may be incomplete and even inconsistent. It is completed and validated when used to create varibles and constraints.

§Constraints and Variables

Variables and constraints are created through the [Model] object. Functions creating variables and constraints have two versions, a try_ and a plain version. The former will return a Result::Err whenever an error was encountered that left the [Model] in a consistent state. The latter version will panic! on any error.

§Variables

When a Variable is created in a model as [Model::variable], the model adds the necessary internal information to map a linear variable index to something in the underlying task, but after that, a variable is essentially a list of indexes of the scalar variables t a shape and sparsity. Variable objects can be stacked, indexed and sliced to obtain new variable objects.

When a model has been optimized, the variable object is used to access the parts of the solution it represents through the [Model] object.

§Constraints

A constraint is created in a [Model] from an expression (something implementing ExprTrait) and a domain using [Model::constraint]. The sparsity pattern of the domain is ignored, and a constraint is always dense. When a constraint has been created it can be indexed, sliced and stacked like a variable, and it can be used to access the relevant parts of the solution through the [Model] object.

§Expression

An expression is an n-dimensional array of scalar affine expressions. Anything that implements the trait ExprTrait can be used as an N-dimensional expression.

§Note

Please note that the package is still somewhat exprimental.

§Example: lo1

Simple linear example:

// Importing everything from mosekcomodel provides all basic functionality.
use mosekcomodel::*;
use mosekcomodel::dummy::Model;

// Create a model with the name 'lo1'
let mut m = Model::new(Some("lo1"));

let a0 = vec![ 3.0, 1.0, 2.0, 0.0 ];
let a1 = vec![ 2.0, 1.0, 3.0, 1.0 ];
let a2 = vec![ 0.0, 2.0, 0.0, 3.0 ];
let c  = vec![ 3.0, 1.0, 5.0, 1.0 ];

// Redirect log output from the solver to stdout for debugging.
// if uncommented.
m.set_log_handler(|msg| print!("{}",msg));
// Create variable 'x' of length 4
let x = m.variable(Some("x"), greater_than(vec![0.0,0.0,0.0,0.0]));

// Create constraints
_ = m.constraint(None,       x.index(1),           less_than(10.0));
_ = m.constraint(Some("c1"), x.dot(a0.as_slice()), equal_to(30.0));
_ = m.constraint(Some("c2"), x.dot(a1.as_slice()), greater_than(15.0));
_ = m.constraint(Some("c3"), x.dot(a2.as_slice()), less_than(25.0));

// Set the objective function to (c^t * x)
m.objective(Some("obj"), Sense::Maximize, x.dot(c.as_slice()));

The project does not include a solver directly, but it is possible to solve (currently linear-only) models by using the OptServer backend to offload to a MOSEK OptServer instance, for example solve.mosek.com:30080 or an instance running locally:

use mosekcomodel::*;
use mosekcomodel::optserver::Model;

let mut m = Model::new(Some("lo1"));
// ...
let x = m.variable(Some("x"), greater_than(vec![0.0,0.0,0.0,0.0]));
// ...

m.set_parameter((), optserver::SolverAddress("solve.mosek.com:30080".to_string()));

m.solve();
let (psta,dsta) = m.solution_status(SolutionType::Default);
println!("Status = {:?}/{:?}",psta,dsta);
let xx = m.primal_solution(SolutionType::Default,&x);
 println!("x = {:?}", xx);

§Example: portfolio_1_basic

Example using second order cones to model risk in a basic portfolio model.

use mosekcomodel::*;
use mosekcomodel::dummy::Model;

// 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
fn basic_markowitz( n : usize,
                    mu : &[f64],
                    gt : &NDArray<2>,
                    x0 : &[f64],
                    w  : f64,
                    gamma : f64) -> Model {
    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"), greater_than(vec![0.0;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());
    model
}

const N : usize   = 8;
const W : f64     = 59.0;
let mu            = [0.07197349, 0.15518171, 0.17535435, 0.0898094 , 0.42895777, 0.39291844, 0.32170722, 0.18378628];
let x0            = [8.0, 5.0, 3.0, 5.0, 2.0, 9.0, 3.0, 6.0];
let gamma         = 36.0;
let GT            = matrix::dense([N,N],vec![
    0.30758, 0.12146, 0.11341, 0.11327, 0.17625, 0.11973, 0.10435, 0.10638,
    0.     , 0.25042, 0.09946, 0.09164, 0.06692, 0.08706, 0.09173, 0.08506,
    0.     , 0.     , 0.19914, 0.05867, 0.06453, 0.07367, 0.06468, 0.01914,
    0.     , 0.     , 0.     , 0.20876, 0.04933, 0.03651, 0.09381, 0.07742,
    0.     , 0.     , 0.     , 0.     , 0.36096, 0.12574, 0.10157, 0.0571 ,
    0.     , 0.     , 0.     , 0.     , 0.     , 0.21552, 0.05663, 0.06187,
    0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.22514, 0.03327,
    0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.     , 0.2202 ]);

_ = basic_markowitz( N, &mu, &GT, &x0, W, gamma);

Re-exports§

pub use constraint::Constraint;
pub use constraint::ConstraintDomain;
pub use model::Sense;
pub use model::SolutionType;
pub use model::SolutionStatus;
pub use model::ModelItem;
pub use model::ModelItemIndex;
pub use model::VarDomainTrait;
pub use model::SolverParameterValue;
pub use model::BaseModelTrait;
pub use model::VectorConeModelTrait;
pub use model::PSDModelTrait;
pub use model::ModelAPI;
pub use model::Solution;
pub use matrix::Matrix;
pub use matrix::NDArray;
pub use matrix::IntoIndexes;
pub use expr::ExprTrait;
pub use expr::ExprRightMultipliable;
pub use expr::ExprLeftMultipliable;
pub use expr::ModelExprIndex;
pub use expr::IntoExpr;
pub use expr::Expr;
pub use expr::RightDottable;
pub use expr::stack;
pub use expr::hstack;
pub use expr::vstack;
pub use expr::stackvec;
pub use expr::sumvec;
pub use variable::Variable;
pub use domain::IntoDomain;
pub use domain::IntoShapedDomain;
pub use domain::LinearDomain;
pub use domain::VectorDomain;
pub use domain::PSDDomain;
pub use domain::LinearDomainType;
pub use domain::VectorDomainTrait;
pub use domain::OffsetTrait;
pub use domain::LinearRangeDomain;
pub use domain::ScalablePSDDomain;
pub use domain::ScalableVectorDomain;
pub use domain::ScalableLinearRange;
pub use domain::LinearProtoDomain;
pub use domain::PSDProtoDomain;
pub use domain::VectorProtoDomain;
pub use domain::unbounded;
pub use domain::less_than;
pub use domain::greater_than;
pub use domain::nonnegative;
pub use domain::nonpositive;
pub use domain::zero;
pub use domain::zeros;
pub use domain::equal_to;
pub use domain::in_quadratic_cone;
pub use domain::in_quadratic_cones;
pub use domain::in_svecpsd_cone;
pub use domain::in_svecpsd_cones;
pub use domain::in_rotated_quadratic_cone;
pub use domain::in_rotated_quadratic_cones;
pub use domain::in_geometric_mean_cone;
pub use domain::in_geometric_mean_cones;
pub use domain::in_dual_geometric_mean_cone;
pub use domain::in_dual_geometric_mean_cones;
pub use domain::in_exponential_cone;
pub use domain::in_exponential_cones;
pub use domain::in_dual_exponential_cone;
pub use domain::in_dual_exponential_cones;
pub use domain::in_power_cone;
pub use domain::in_power_cones;
pub use domain::in_dual_power_cone;
pub use domain::in_dual_power_cones;
pub use domain::in_psd_cone;
pub use domain::in_psd_cones;
pub use domain::in_range;
pub use disjunction::ConjunctionTrait;
pub use disjunction::DisjunctionTrait;

Modules§

constraint
disjunction
Structures and functions for formulating disjunctive constraints.
domain
This module and the submodules define domain functionality.
dummy
This module implements a dummy backend that allows inputting data, but has no support for solving or writing data.
experimental
This module defines various experimental exprssions whose usefulness has not been determined yet.
expr
The expression building operations are used to generate linear expressions for constraints. The vast majority is implemented in ExprTrait and public functions.
matrix
This module provides basic array functionality.
model
optserver
This module implements a backend that uses a MOSEK OptServer instance for solving, for example solve.mosek.com:30080.
utils
variable
Module for Variable object and related implementations

Macros§

exprcat
hstack
Stack a list of expressions in dimension 1
stack
Stack a list of expressions in a given dimension
vstack
Stack a list of expressions in dimension 0