# 9 Technical guidelines¶

This section contains some technical guidelines for Fusion users.

For modelling guidelines check one of the following sections:

## 9.1 Limitations¶

Fusion imposes some limitations on certain aspects of a model to ensure easier portability:

• Constraints and variables belong to a single model, and cannot as such be used (e.g. stacked) with objects from other models.
• Most objects forming a Fusion model are immutable.

The limits on the model size in Fusion are as follows:

• The maximum number of variable elements is $$2^{31}-1$$.
• The maximum size of a dimension is $$2^{31}-1$$.
• The total size of an item (the product of dimensions) is limited to $$2^{63}-1$$.

## 9.2 Memory management and garbage collection¶

Users who experience memory leaks using Fusion, especially:

• memory usage not decreasing after the solver terminates,
• memory usage increasing when solving a sequence of problems,

should make sure that the Model objects are properly garbage collected. Since each Model object links to a MOSEK task resource in a linked library, it is sometimes the case that the garbage collector is unable to reclaim it automatically. This means that substantial amounts of memory may be leaked. For this reason it is very important to make sure that the Model object is disposed of manually when it is not used any more. The necessary cleanup is performed by the method Model.dispose.

  {
Model::t M = new Model();   auto _M = finally([&]() { M->dispose(); });
// do something with M
}


This construction assures that the Model.dispose method is called when the object goes out of scope, even if an exception occurred. If this approach cannot be used, e.g. if the Model object is returned by a factory function, one should explicitly call the Model.dispose method when the object is no longer used.

Furthermore, if the Model class is extended, it is necessary to dispose of the superclass if the initialization of the derived subclass fails. One can use a construction such as:

class MyModel: Model
{
public:
MyModel(): Model()
{
bool finished = false;
try
{
//perform initialization here
finished = true;
}
catch(...)
{
dispose();
}
}
};


Sharing a Model object between threads is safe, as long as it is not accessed from more than one thread at a time. Multiple Model objects can be used in parallel without any problems.

Parallelization

The interior-point and mixed-integer optimizers in MOSEK are parallelized. By default MOSEK will automatically select the number of threads. However, the maximum number of threads allowed can be changed by setting the parameter numThreads and related parameters. This should never exceed the number of cores. See Sec. 12 (The Optimizers for Continuous Problems) and Sec. 13 (The Optimizer for Mixed-integer Problems) for more details for the two optimizer types.

The speed-up obtained when using multiple threads is highly problem and hardware dependent. We recommend experimenting with various thread numbers to determine the optimal settings. For small problems using multiple threads may be counter-productive because of the associated overhead.

By default the optimizer is run-to-run deterministic, which means that it will return the same answer each time it is run on the same machine with the same input, the same parameter settings (including number of threads) and no time limits.

## 9.4 Efficiency¶

In some cases Fusion must reformulate the problem by adding auxiliary variables and constraints before it can be represented in the optimizer’s internal format. This can cause a significant overhead. The following guidelines can help speed up the process.

Decide between sparse and dense matrices

Deciding whether a matrix should be stored in dense or sparse format is not always trivial. First, there are storage considerations. An $$n\times m$$ matrix with $$l$$ non zero entries, requires

• $$\approx n\cdot m$$ storage space in dense format,
• $$\approx 3\cdot l$$ storage space in sparse (triplet) format.

Therefore if $$l \ll n\cdot m$$, then the sparse format has smaller memory requirements. Especially for very sparse density matrices it will also yield much faster expression transformations. Also, this is the format used ultimately by the underlying optimizer task. However, there are borderline cases in which these advantages may vanish due to overhead spent creating the triplet representation.

Sparsity is a key feature of many optimization models and often occurs naturally. For instance, linear constraints arising from networks or multi-period planning are typically sparse. Fusion does not detect sparsity but leaves to the user the responsibility of choosing the most appropriate storage format.

Reduce the number of Fusion calls and level of nesting

A possible source of performance degradation is an excessive use of nested expressions resulting in a large number of Fusion calls with small model updates, where instead the model could be updated in larger chunks at once. In general, loop-free code and reduction of expression nesting are likely to be more efficient. For example the expression

$\begin{split}\sum_{i=1}^n A_i x_i\\ x_i \in \real^k, A_i \in \real^{k\times k},\end{split}$

could be implemented in a loop as

    Expression::t ee = Expr::constTerm(k, 0.0);
for(int i=0;i<n;i++)
ee = Expr::add( ee, Expr::mul((*A)[i],(*x)[i]) );


A better way is to store the intermediate expressions for $$A_i x_i$$ and sum all of them in one step:

    auto prods = new ndarray<Expression::t,1>(shape(n));
for(int i=0;i<n;i++) (*prods)[i] = Expr::mul((*A)[i],(*x)[i]);
Expression::t ee = Expr::add( std::shared_ptr<ndarray<Expression::t,1>>(prods) );


Fusion design naturally promotes this sort of vectorized implementations. See Sec. 6.7 (Vectorization) for more examples.

Do not fetch the whole solution if not necessary

Fetching a solution from a shaped variable produces a flat array of values. This means that some reshaping has to take place and that the user gets all values even if they are potentially interested only in some of them. In this case, it is better to create a slice variable holding the relevant elements and fetch the solution for this subset. See Sec. 6.6 (Stacking and views). Fetching the full solution may cause an exception due to memory exhaustion or platform-dependent constraints on array sizes.

Remove names

Variables, constraints and the objective function can be constructed with user-assigned names. While this feature is very useful for debugging and improves the readability of both the code and of problems dumped to files, it also introduces quite some overhead: Fusion must check and make sure that names are unique. For optimal performance it is therefore recommended to not specify names at all.

MOSEK is a commercial product that always needs a valid license to work. MOSEK uses a third party license manager to implement license checking. The number of license tokens provided determines the number of optimizations that can be run simultaneously.

By default a license token remains checked out from the first optimization until the end of the MOSEK session, i.e.

Starting the optimization when no license tokens are available will result in an error.

Default behaviour of the license system can be changed in several ways:

## 9.6 Deployment¶

When redistributing a C++ application using the MOSEK Fusion API for C++ 8.1.0.37, the following libraries must be included:

64-bit Linux 64-bit Windows 64-bit Mac OS
libmosek64.so.8.1 mosek64_8_1.dll libmosek64.8.1.dylib
libfusion64.so.8.1 fusion64_8_1.lib libfusion64.8.1.dylib
libiomp5.so libomp5md.dll
libcilkrts.so.5 cilkrts20.dll libcilkrts.5.dylib