This example illustrates the basic functionality of the toolbox and shows how the generated code and the interfaces are used. More details can be found in the user guide.
Let us consider the following parametric problem (the parametric problem data is indicated in red).
where is a rotated box given by
and is a shifted Euclidean ball given by
Using FiOrdOs syntax, this problem is specified as follows. Note that the value 'param'
defines a problem data to be parametric.
X1 = EssBox(2, 'l',[-1;0.5], 'u',[1;2], 'rot',[cos(pi/6),-sin(pi/6);sin(pi/6),cos(pi/6)]); X2 = EssBall(2, 'r','param', 'shift',[1;-1]); X = SimpleSet(X1,X2); op = OptProb('H',diag([1,2,3,4]), 'g','param', 'X',X, 'Ae',[0,1,-1,0], 'be','param');
After specifying the problem, the solver is configured and finally C-code is generated. During the setup, default solver settings can be modified.
The procedure is as follows:
%instantiate solver s = Solver(op,'approach','dual', 'algoOuter','gm', 'algoInner','fgm'); %optionally change settings, e.g. %-maximum number of iterations s.setSettings('algoOuter', 'maxit',100); s.setSettings('algoInner', 'maxit',90); %-gradient-map stopping criterion s.setSettings('algoOuter', 'stopg',true, 'stopgEps',1e-3); s.setSettings('algoInner', 'stopg',true, 'stopgEps',1e-5); %generate solver code s.generateCode('prefix','demo_');
Remarks:
'dual'
), which uses two algorithms. In this example, the gradient method ('gm'
) is chosen as the outer algorithm to maximize the dual function ('algoOuter'
) whereas the fast gradient method ('fgm'
) solves the inner problem to determine the dual function's gradient ('algoInner'
).'maxit'
is the maximum number of iterations after which an algorithm terminates in any case.'stopg'
), an algorithm terminates if the norm of the gradient-map is below the specified accuracy ('stopgEps'
). 'prefix'
defines the common prefix for the generated file names, interface functions and data types.s = Solver(op)
) or let FiOrdOs display all possible approaches/algorithm(s) for your problem (command Solver.analyze(op)
). For this problem, there is also the primal-dual approach available; it can be selected by s = Solver(op, 'approach', 'primal-dual')
The following table gives an overview on all generated files. The files in parentheses are created when executing the corresponding makefiles.
filename | description | |
---|---|---|
C-solver | demo_solver.h | solver interface |
demo_solver.c | solver implementation | |
Matlab interface | demo_mex.c | MEX source (wrapper for C-solver) |
demo_mex_make.m | makefile creating: | |
(demo_mex.mex* ) | -MEX-file | |
demo_mex.m | Help file | |
Simulink interface | demo_sfun.c | S-Function source (wrapper for C-solver) |
demo_sfun_make.m | makefile creating: | |
(demo_sfun.mex* ) | -S-Function MEX-file | |
(demo_sfun_lib.mdl ) | -Simulink library (containing a masked subsystem block that encapsulates demo_sfun.mex*) | |
demo_sfun_lib_aux.m | auxiliary file for demo_sfun_lib.mdl |
The interface defined in demo_solver.h
consists of the two functions demo_init
and demo_solve
, which work on four structs of types demo_Params
, demo_Settings
, demo_Result
, demo_Work
.
The following C-program gives an example for how to use the interface. The program solves our problem for one specific realization of the parametric problem data.
#include "demo_solver.h" #include <stdio.h> int main() { /* define and initialize the four structs */ demo_Params params = {0}; demo_Settings settings = {0}; demo_Result result = {0}; demo_Work work = {0}; demo_init(¶ms, &settings, &result, &work); /* define all parametric data */ params.g[0] = 4; params.g[1] = 3; params.g[2] = 1; params.g[3] = 2; params.be[0] = 0.8; params.X2.r[0] = 1; /* optionally change some settings, e.g. */ settings.algoOuter.maxit = 30; /* solve the problem */ demo_solve(¶ms, &settings, &result, &work); /* use solution availabe in struct result */ printf("d: %g\n", result.d); printf("iter: %d\n", result.iter); return 0; }
Note that the struct settings
contains all solver settings that can be modified at runtime. They are initialized by demo_init
to the values defined during the solver setup in Matlab.
The following code gives an example for compiling and linking the main file demo_main.c
and the solver demo_solver.c
in Linux.
Finally, the resulting executable called demo
is run.
gcc demo_main.c demo_solver.c -lm -o demo ./demo
Invoking the makefile by
demo_mex_make()
builds the binary MEX-file demo_mex.mex*
, which then can be called like any Matlab function.
The following Matlab code gives an example for how to use the MEX-file. The code solves our problem for one specific realization of the parametric problem data.
%define all parametric data mparams = struct(); mparams.g = [4;3;1;2]; mparams.be = 0.8; mparams.X2.r = 1; %optionally change some settings, e.g. msetgs = struct(); msetgs.algoOuter.maxit = 30; %call MEX-file mres = demo_mex(mparams,msetgs); %use result mres mres.x
Remarks:
mparams
, msetgs
, mres
correspond to the C-structs params
, settings
, result
.mparams
and msetgs
, is availabe by typing help demo_mex
Invoking the makefile by
demo_sfun_make()
builds the S-Function MEX-file demo_sfun.mex*
and the Simulink library demo_sfun_lib.mdl
.
The library contains the following masked subsystem block, which can be instantiated by copying it into a Simulink model.
block | description | |
---|---|---|
block inputs | parametric problem data | |
mask parameters | solver settings sample time |
|
block outputs | results of optimization |
Note that for some solver settings that can be accessed via the block mask, the data source can be configured as external. This means that the solver setting becomes a block input. As an example, this allows one to perform warm-starting as shown in the following figure.