1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
//!
//! # 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](https://mosek.com) via [mosek.rust](https://crates.io/crates/mosek), supporting
//! - Linear and conic variables and constraints
//! - Integer variables
//! - Disjunctive constraints
//! - `mosekcomodel_highs` a backend for [HIGHS](https://highs.dev/), 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:
//! - Non-positive or non-negative orthant (see [nonpositive], [nonnegative], [less_than] and
//! [greater_than]).
//! - Unbounded values (see [unbounded]).
//! - Fixed values (see [zero] and [equal_to])
//! - Second order cone(s) (see [in_quadratic_cone], [in_quadratic_cones]):
//! $$
//! \\left\\{ x \\in R^n | x_1^2 \\geq \\left\\Vert x_2^2 + \\cdots + x_n^2 \\right\\Vert^2, x₁ \\geq 0 \\right\\}
//! $$
//! - Rotated second order cone(s) (see [in_rotated_quadratic_cone], [in_rotated_quadratic_cones]):
//! $$
//! \\left\\{ x \in R^n | \\frac{1}{2} x_1 x_2 \geq \\left\\Vert x_3^2 + \\cdots + x_n^2 \\right\\Vert^2, x_1, x_2 \\geq 0 \\right\\}
//! $$
//!<!-- - Symmetric positive semidefinite cone(s) if dimension `n > 1` (see [in_psd_cone], [in_psd_cones]).-->
//! - 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 [IntoExpr]s rather than
//! [ExprTrait]s. 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:
//!
//! ```rust
//! // 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](http://solve.mosek.com) or an instance running locally:
//! ```
//! use mosekcomodel::*;
//! use mosekcomodel::optserver::Model;
//!
//! 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 ];
//! let x = m.variable(Some("x"), greater_than(vec![0.0,0.0,0.0,0.0]));
//! // ...
//! # _ = 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));
//! # m.objective(Some("obj"), Sense::Maximize, x.dot(c.as_slice()));
//!
//! 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.
//!
//! ```rust
//! 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, >, &x0, W, gamma);
//! ```
//! <script type="text/javascript" id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"> </script>
//extern crate mosek;
extern crate itertools;
//pub mod solver;
//pub mod variable_index;
use WorkStack;
pub use ;
pub use ;
//pub type Model = ModelAPI<model::mosekmodel::MosekModel>;
pub use ;
pub use ;
pub use Variable;
pub use ;
pub use ;