Tuesday, June 17, 2025

Introducing MosekCOModel for Rust

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.

The model
The models that mosekcomodel can formulate has the form 
$min/max$ $c^Tx + c_f$
$subject$ $to:$ $A x + b \in K_1  \times ... \times K_m $
                        $x \in C_1 \times ... \times C_k$

Where $Ax+b$ is an affine expression in the variable $x$ and $K_i$ and $C_i$ are conic sets. Additionally, mosekcomodel supports integer variables and disjunctive constraints  

Currently mosekcomodel supports all cone types that MOSEK supports:
  • 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.
The API
The three basic concepts in the API are the model, the variable and the constraint.

The model object defines the API for setting up the model and communicating it to an underlying solver. Through this object constraints and variables are created. A variable can be regarded as an n-dimensional array of scalar variables, the dimensionality is part of the type, while the actual dimensions are only defined at runtime. By extension, expressions created from variables are also n-dimensional arrays of scaler affine expressions. When a constraint is created from an expression and a domain, the shape of the expression and the domain must match, and the constraint inherits this dimensionality and shape.

The outline of a simple model could look like this:


Unfortunately, while Rust does allow operator overloading, the definitions for the operators for plus, minus, multiplication and indexing, that would be useful are too restrictive for this use. So for now we are stuck with functions like .add() and .mull()

Constraint expressions are purely linear, and the crate provides functions to create and manipulate expressions: Reshaping, transposing, stacking, slicing and multiplying by matrices, vectors and constants and so on.

Expression evaluation is lazy, meaning that an expression is only evaluated once it is used in a constraint.

Example: Portfolio optimization
The following is a working example of a basic conic quadratic Markowitz portfolio model implemented with mosekcomodel.



Conclusions
While the mosekcomodel is under active development, it is already a very useful tool for formulating optimization models in Rust. What it needs most right now is people using it and giving us feedback!