This guide will introduce you to the very basics associated with solving problems using the TOMLAB Optimization Environment. After this session you will have solved several different problems with a variety of solvers. The solvers you can use will depend on what you are licensed for. The total time required for these exercises has been estimated to about 45 minutes.
Useful commands when using this guide:
help *Assign (* is lp, qp, con and more) help tomRun SolverList('qp'); help snoptTL help mipSolve help minlpBBTL help cplexTL Prob - view the Problem structure used by the solvers Result - view the Result structure
Setting patterns is especially important for large-scale problems as memory needs to be managed more properly (a dense problem is normally assumed otherwise). Solver timings and recursive calls primarily applies to smaller problems but could also be important issues for larger test cases.
Passing addition variables
If the user wishes to pass additional variables to the user functions written the parameters need to be included in the Prob structure. The code snippets below illustrates how to do it.
In the main function where TOMLAB is called do:
Prob = *Assign(...); Prob.user.a = a; Prob.user.b = b; Result = tomRun('solver', Prob, 1);
If for example the objective function needs the additional variables a and b do the following in the file:
function f = myobjective(x, Prob) a = Prob.user.a; b = Prob.user.b; f = sum(x)*a + sum(x.^3)*b;
For most problems it is critical to set the proper problem patterns (ConsPattern, HessPattern and d2LPattern) for memory allocation purposes and to speed up numerical differentiation. If analytical derivatives are given only the memory benefit will be seen.
See for example the minlpQG problem and use the following code:
minlpQG Prob.ConsDiff = 1; Result = tomRun('npsol', Prob, 1); Prob.ConsDiff = 11; Result = tomRun('npsol', Prob, 1);
As can be seen only 43 constraint evaluations are done for the second run with NPSOL. The reason being that the solver (numerical differentiation routines) can see which variables to perturb at the same time from the ConsPattern.
Comparing different solvers with solution times under 5-10 seconds may yield incorrect results. When running a problem and a solver for the first time general overhead and loading of dll's consume the majority of the time. The following code illustrates the problem:
clear all Prob = probInit('lp_prob', 1); R = tomRun('minos', Prob, 1); R = tomRun('minos', Prob, 1);
The first run may report a solution time around 0.3 seconds, while the second run shows that the real time spent on optimization is less than 0.01 seconds. When evaluating different solver solutions, all tests need to be run at least twice (more recommended).
There are ways to avoid the extra overhead associated with the driver routines tomRun. For example one can call the solver directly.
clear all Prob = probInit('lp_prob', 1); Prob = ProbCheck(Prob, 'minos'); R = minosTL(Prob); PrintResult(R);
This is especially important when recursively calling the solver.
When doing recursive calls to a solver and modifying some of the inputs (not the size of the problem) one should call the solver as shown above. This minimizes the overhead during the solver call.
Warm start is commonly used when doing recursive calls. Several of the TOMLAB solvers support warm start.
clear all Prob = probInit('lp_prob', 1); R = tomRun('minos', Prob, 1); Prob = WarmDefSOL('minos', Prob, R); R = tomRun('minos', Prob, 1);
Similar code will work for MINOS (also LP-, QP-MINOS), SNOPT, SQOPT, NPSOL, NLSSOL, LPOPT, QPOPT, LSSOL.
When running the TOMLAB /MINLP solvers the following code is needed. Observe that only BQPD and filterSQP can be warm started, while miqpBB and minlpBB accept a starting point.
clear all Prob = probInit('con_prob', 10); R = tomRun('filterSQP', Prob, 1); Prob = WarmDefDUNDEE('filterSQP', Prob, R); R = tomRun('filterSQP', Prob, 1);
It is also possible to warm start TOMLAB /CPLEX for LP problems (only simplex solvers). When doing this one has to supply a basis. See 'help cplex' for more information.
The global solvers in the TOMLAB Base Module and TOMLAB /CGO are easily warm started. One simply sets Prob.WarmStart = 1 before calling the solver the second round.
There are several routines and functionality in TOMLAB for verifying problem setup. checkDerivs can be used to check the absolute error of user supplied derivatives. checkFuncs make general user function checking and problem validation. One can also check the derivatives with for example SNOPT, MINOS and NPSOL. The code below illustrates how to generate a print file for further analysis.
clear all Prob = probInit('con_prob', 10); Prob.SOL.optPar(1) = 111111; % Major print level Prob.SOL.optPar(2) = 10; % Minor print level Prob.SOL.optPar(13) = 3; % Verify level Prob.SOL.PrintFile = 'snoptP.txt'; % SNOPT print file name Prob.SOL.SummFile = 'snoptS.txt'; % SNOPT summary file name R = tomRun('snopt', Prob, 1);
The print file snoptP.txt will provide details on the user supplied derivatives. Observe that this checking should never be done for production code. Once the derivatives have been verified it is recommended to set Prob.SOL.optPar(13) = -1 to avoid one extra function call.
There is an optimization toolbox interface included in /tomlab/optim. These routines can be used for quick testing of the TOMLAB capabilities when problems are setup for use with optimization toolbox. In general, the routines should be avoided as unnecessary overhead is introduced from the format conversion. If one has embedded calls they should be used as is.
TOMLAB supports all types of Matlab functions; anonymous, nested and general sub-functions.
The following file illustrates various combinations of the options:
Open the file for viewing, and execute funchandleQG; in Matlab.
% funchandleQG is a small example for defining and solving problems % using nested functions, anonymous functions and subfunctions. function [R1, R2, R3] = funchandleQG Name='funchandleQG'; x_L = [0 0 0 0 0 ]'; x_U = [inf inf 1 1 1 ]'; A = [1 0 1 0 0 ; ... 0 1.333 0 1 0 ; ... 0 0 -1 -1 1 ]; b_L = ; b_U = [1.6 ; 3 ; 0]; c_L = [1.25;3]; c_U = c_L; x_0 = ones(5,1); HessPattern = spalloc(5,5,0); ConsPattern = [ 1 0 1 0 0; ... 0 1 0 1 0 ]; % Example with local sub functions Prob1 = minlpAssign(@my_f, @my_g, @my_H, HessPattern, ... x_L, x_U, Name, x_0, , , , , ... A, b_L, b_U, @my_c, @my_dc, @my_d2c, ... ConsPattern, c_L, c_U); R1 = tomRun('knitro', Prob1, 1); constr = @(x) [ x(1)^2+x(3) ; sqrt(x(2)^3)+1.5*x(4)]; % Example with local sub functions and anonymous function % One directly into assign routine and one as variable input Prob2 = minlpAssign(@(x) [2 3 1.5 2 -0.5]*x, @my_g, @my_H, HessPattern, ... x_L, x_U, Name, x_0, , , , , ... A, b_L, b_U, constr, @my_dc, @my_d2c, ... ConsPattern, c_L, c_U); R2 = tomRun('knitro', Prob2, 1); % Example with local sub functions, anonymous function % and nested function Prob3 = minlpAssign(@mynested_f, @my_g, @my_H, HessPattern, ... x_L, x_U, Name, x_0, , , , , ... A, b_L, b_U, constr, @my_dc, @my_d2c, ... ConsPattern, c_L, c_U); function f = mynested_f(x) f = [2 3 1.5 2 -0.5]*x; end R3 = tomRun('knitro', Prob3, 1); end function f = my_f(x) f = [2 3 1.5 2 -0.5]*x; end function g = my_g(x) g = [2 3 1.5 2 -0.5]'; end function H = my_H(x) H = spalloc(5,5,0); end function c = my_c(x) c = [ x(1)^2+x(3) ; sqrt(x(2)^3)+1.5*x(4)]; end function dc = my_dc(x) dc = [2*x(1) 0.0 1.0 0.0 0.0 ; ... 0.0 1.5*sqrt(x(2)) 0.0 1.5 0.0]; end function d2c = my_d2c(x,lam) d2c = spalloc(5,5,2); d2c(1,1) = lam(1)*2; d2c(2,2) = lam(2)*3/(4*sqrt(x(2))); end