TomSym
TOMSYM is a symbolic manipulation package which is part of the TOMLAB optimization software suite. It simplifies the work of defining optimization problems by automatically generating derivatives, m-code and other things needed for the numeric solvers to work as efficiently as possible.
TOMSYM is also the foundation on which PROPT is built. We recommend that PROPT users read through this guide first, before continuing on to the PROPT manual.
Solving optimization problems
An optimization problem is defined by an objective function and a set of constraints. Problems are usually classified depending on the properties of the objective and constraints, and different types of numeric techniques are used for different types of problems. For example, solvers usually handle quadratic objectives and linear constraints more efficiently than general nonlinear ones.
TOMSYM provides the ezsolve function for solving any type of optimization problem. It examines the objective and constraints to determine the problem type, and then automatically selects the numeric solver which it deems most apropriate.
For example, here we find a minimum to a second degree polynomial in two variables
>> toms a b
>> ezsolve(a^2 + 4*b^2 + 3*a - 2*b - a*b)
Problem type appears to be: qp
Time for symbolic processing: 0.040062 seconds
Starting numeric solver
=====* * * ===================================================================
TOMLAB - Tomlab Optimization Inc. Development license 999001. Valid to 2013-02
===============================================================================
Problem: 1: f_k -2.266666666666666607
f(x_0) 0.000000000000000000
Solver: CPLEX. EXIT=0. INFORM=1.
CPLEX Barrier QP solver
Optimal solution found
FuncEv 2 GradEv 2 ConstrEv 2 Iter 2
CPU time: 0.010000 sec. Elapsed time: 0.002605 sec.
ans =
a: -1.4667
b: 0.0667
Looking at the output from the solver, we see that ezsolve determined the problem type to be QP (quadratic programming) and selected CPLEX at the solver. The problem was then solved numerically and the solution is returned as a matlab structure with fields corresponding to the symbol names.
Most solvers handle the linear constraints separately from the nonlinear ones. With TOMSYM, this happens transparently. The linear constraints are collected and passed to the solver in the form of a (sparse) matrix, while the nonlinear ones are passed as m-code, together with m-code for their derivatives.
Symbolic matrices
With TOMSYM, a single symbol can be a scalar, vector or matrix. Expressions that involve matrix symbols are stored only once. This behaviour is defferent from many symbolic algebra packages, where symbolic arrays are treated as arrays of symbols (i.e. one symbolic expression is stored for each position in the array).
Symbolic matrices can be manipulated just like normal MATLAB matrices. For example, * gives the matrix product, and .* gives the element-wise product of two matrices.
>> A = tom('A',2,2)
A = tomSym(2x2):
A
>> b = tom('b',2,1)
b = tomSym(2x1):
b
>> y = A*b
y = tomSym(2x1):
A*b
In this example A is 2-by-2 matrix symbol, b is a 2-by-1 column vector symbol, and the matrix expression y is also a 2-by-1 column vector.
Taking the derivative of a vector expression with respect to a vector symbol gives the so-called Jacobian matrix.
>> derivative(y,b) ans = tomSym(2x2): A
Derivatives involving matrices are computed as if the matrices were vectors, with elements taken in column-first order.
>> derivative(y,A) ans = tomSym(2x4): kron(b',eye(2))
A consequence of this definition is that the derivative of a matrix with respect to itself is the identity matrix.
>> full(derivative(A,A))
ans =
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
Vectorized code
In languages such as C or FORTRAN, "loops" are very common occurences. Matlab, on the other hand, uses a syntax where loops can usually be replaced by vectorized expressions. An example is the matrix product, which can be coded in C using three nested for loops, but in Matlab is accomplished by simply using the operator *.
tomSym is intended for use with vectorized code. It supports all the functions that are typically needed to generated vectorized expressions, such as sum, diff, repmat, sparse, etc.
Complex numbers
The solvers in the TOMLAB suite work with real numbers. Still, some equations are more easily expressed using complex numbers.
The tomCmplx class makes it possible to represent symbolic complex expressions as pairs of ordinary tomSym expressions. For example:
>> toms a b >> z = tomCmplx(a,b) z = tomCmplx(1x1): Real part: a Imaginary part: b >> z2 = z.*z z2 = tomCmplx(1x1): Real part: a*a-b*b Imaginary part: a*b+b*a
The set of operations allowed for tomCmplx expressions is currently very limited. Element-wise addition, subtraction, multiplication, division and exponentiation are supported, as are real(), imag(), abs() and angle() which return real-valued tomSym expressions.
If a tomCmplx is used in an equation, then a cell array of two equations is generated; One for the real and one for the imaginary component. A complex number can never be used in an inequality, so <= and >= are not supported for tomCmplx. (But inequalities can be used on the real and imaginary parts, as those are normal tomSym expressions.)
What tomSym cannot do
There are a few things that are possible in Matlab code, but which tomSym currently cannot do. Below we list the most common issues, and their work-arounds.
Variable-size matrixes
The size of a tomSym object cannot depend on the value of another tomSym object. This has to do with the fact that numeric solvers typically allocate memory only at the start of an optimization run, and then expect the number of unknowns to remain constant. (This may change in the future.)
Indexes into double arrays
Replacing values in arrays
Conditional execution
N-dimensional arrays for N > 2
TOMSYM works with scalars, vectors, and two-dimensional matrices. Although Matlab can handle arrays of any dimension, TOMSYM cannot. The work-around is to use tomArray, which transforms tomSym symbols into arrays of any dimension. (TomArray also allows for a GAMS-style indexing which is a bit different from Matlab syntax, but convenient when formulating optimization problems involving high-dimension arrays.)
Note that TOMSYM supports interpn with high-dimensional numeric data, although the symbolic inputs must be of dimension less than two.
Limits
TOMSYM evaluates expressions by generating matlab code. (It does some mathematical simplification to improve efficiency, but only to a limited extent.) If an expression evaluates to 0/0 or 0*Inf, then NaN (Not a Number) will be returned. When the solver encounters a NaN, it will often just quit (although some solvers include back-tracking algorithms.)
This means that expressions such as a*log(a) can not be used if a might become zero. In this case it might be prudent to add a constraint a >= 1e-8 (or similar) to help the solver stay out of trouble.
Interfaces
TOMSYM is most commonly used for setting up optimization problems for the TOMLAB suite of numeric solvers. However, it is also possible to use it by itself to generate m-code or to interface it with your own code.
Generating m-code with TOMSYM
It is possible to convert any TOMSYM object into MATLAB code via the mcode and mfile commands. This makes it possible, for example, to use derivatives computed by TOMSYM in your own applications outside of TOMLAB.
Because it is very inefficient to store large matrices as code, the mcode/mfile commands also return a cell array named tempD which contains the numeric data from the TOMSYM object. The tempD array must be provided whenever the code is executed.
Connection to TOMLAB
When TOMLAB is used without TOMSYM, optimization problems are represented via a Prob structure, which typically contain function handles to the objective and constraint functions, and ideally also their derivatives. TOMSYM uses a symbolic representation of the objective and constraints to create this Prob structure, including the m-code needed, and pass it directly to the solver. This and enables the user to focus on the modelling task rather than tedious implementation details such as coding up the derivative for each nonlinear constraint.
Advanced users may extract the Prob structure from TOMSYM using the sym2prob function, and then manipulate this structure further before calling the solver. This makes it possible, for example, to optimize the autogenerated code, or set special solver flags.
When the same problem needs to be re-solved many times for varying values of a parameter, the fastest way is usually to create the Prob structure once, and then change the parameter value using setparameter
Connection to PROPT
PROPT is a solver for dynamic optimization problems which uses a collocation mehtod. PROPT is based on TOMSYM, and uses the same symbolic engine. The output of PROPT's collocate family of functions is ordinary TOMSYM arrays which means that PROPT and TOMSYM code can be mixed freely.
Improving speed
Speed is often important when solving optimization problems. The main advantage of tomSym is that it allows for high overall speed. The time it takes from the moment you start coding until the solution presented should be as short as possible. Often, much more time is spent coding than numerically solving the problem, so emphasis should be placed on writing code that is easy to read and debug. Sometimes, however, the time used by the numerical solver is critical, and this section gives som hints on how to make it run faster.
Pre-process the symbolic problem
See tomsym_mpc.m
Avoid loops
Manipulating tomSym expressons inside a loop can be very slow. For example, the following loop generates a long symbolic expression.
>> v = tom('v',1,10);
>> s = 0;
>> for i = 1:length(v); s = s+v(i); end
>> disp(s);
((((((((v(1)+v(2))+v(3))+v(4))+v(5))+v(6))+v(7))+v(8))+v(9))+v(10)
In this case, the loop can simply be replaced by s = sum(v), which will run much faster.
Use separate symbols for separate things
It is common in some optimization frameworks to use a single vector x for all the optimization variables, and then using indexes to represent different things. (x(1) is position in m, x(2) is speed in m/s, etc.)
Doing this in tomSym is a bad idea for several reasons. The generated code will be less efficient, because tomSym tries to generated derivatives with respect to the whole vector but it is only used one element at a time. Automatic scaling will not work as expected, becuase tomSym tries to scale the entire vector by one factor, when the individual elements may have different magnintude.
Also, it is much easier to read code that uses x for position and v for speed, than code that uses x for both.
Don't vectorize expressions that don't belong together
It is faster say { x >= 0, cos(x) >= 0.5 } than [x; cos(x)] >= [ 0; 0.5 ].
In the first case we have one linear constraint and one nonlinar constraint. The solver will handle these separately, and linear constraints are treated much more efficiently than nonlinear ones. In the second case we have a nonlinear constraint with two components. TomSym is unable to separate the linear component, so both are handled by the nonlinear part of the solver.
Avoid using abs and max
It is much more efficient to say { v >= -1, v <= 1 } than { max(abs(v)) <= 1 }. Again, this is because linear constraints are treated more efficiently than nonlinear ones. For the same reason it is faster to minimize y under the constraint { v >= -y, v <= y } than to minimize max(abs(x)).
The rewriteV function (called by ezsolve) attempts to get rid of instances of abs and max, but it is only able to handle simple cases.
Don't put equations in the objective
Using an objective that requires solving equations, such as sum((Ab).^2)) is very inefficient. A better way to handle this is to rewrite the objective as sum(x.^2) and add the constraint A*x==b. This formulation gives much more information to the solver, and avoids having to compute the derivative of the solution to an equation (which is numerically unstable.)
For this reason, functions like fzero should never be used with tomSym. The function tomfzero can often be used in its place.
Balance the scale
Equations like a/1000000 + b ==1 don't work well with numeric solvers, because a and b will have very different magnitudes in the solution.
One way to overcome this problem is to set OPTIONS.scale = 'auto', which will cause ezsolve to try to guess a scaling factor for each unknown. This works best if each variable has been given reasonable upper and lower bounds.
Another way to fix the problem is to use scaled variable from the start. For example, if we use the scaled variable as, and let a equal 1000000*as, then the equation becomes as + b ==1, which is well-scaled.
Multiply rather than divide
Multiplication is "less nonlinear" than division. Hence, equations like m*x ==f typically result in better convergence than x ==f/m. This is particularly true for matrices: M*x ==f can be much faster than x ==Mf.
This method also prevents the problem where the solver shuts down because of a division-by-zero. (However, if m is zero then f== is a solution, so watch out!)
Supported functions
Below is a list of functions that are overloaded for tomSym. That is: these functions accept symbolic input and then return symbolic expressions whose derivatives can be computed symbolically.
Most of these are standard Matlab functions, but some are special functions that are mostly used internally by tomSym for efficiency reasons.
>> help tomSym/Contents
@TOMSYM
Files
abs - tomSym/abs - Overloaded function
acos - tomSym/acos - Overloaded function
acosh - tomSym/acosh - Overloaded function
acot - tomSym/acot - Overloaded function
acoth - tomSym/acoth - Overloaded function
acsc - tomSym/acsc - Overloaded function
acsch - tomSym/acsch - Overloaded function
angle - tomSym/angle - Overloaded function
asec - tomSym/asec - Overloaded function
asech - tomSym/asech - Overloaded function
asin - tomSym/asin - Overloaded function
asinh - tomSym/asinh - Overloaded function
atan - tomSym/atan - Overloaded function
atan2 - tomSym/atan2 - Overloaded function
atanh - tomSym/atanh - Overloaded function
bsxfun - tomSym/bsxfun - Overloaded function
cat - tomSym/cat - Overloaded function
ceil - tomSym/ceil - Overloaded function
char - tomSym/char - Convert tomSym to char.
complementary - complementary - overloaded function
conj - tomSym/conj - Overloaded function
constpart - tomSym/constpart - The zeroth term of the McLau
cos - tomSym/cos - Overloaded function
cosh - tomSym/cosh - Overloaded function
cot - tomSym/cot - Overloaded function
csc - tomSym/csc - Overloaded function
ctranspose - tomSym/ctranspose - Overloaded operator
cumsum - tomSym/cumsum - Overloaded function
dblquad - tomSym/dblquad - Numeric double quadrature
derivative - tomSym/derivative - The symbolic derivative of
derivatives - tomSym/derivatives - The symbolic derivatives o
det - tomSym/det - Overloaded function
diag - tomSym/diag - Overloaded function
diff - tomSym/diff - Difference - Overloaded function
DiracDelta - tomSym/DiracDelta - Placeholder derivative of a
DiracDeltas - tomSym/DiracDeltas - Placeholder derivative of
disp - tomSym/disp - Command window display of a tomSy
display - tomSym/display - Command window display of a to
double - tomSym/double - Convert tomSym constant to doub
end - tomSym/end - Overloaded - Get the last index of
eps - tomSym/rem - Overload the remainder function
eq - tomSym/eq - Overloaded ==operator
erf - tomSym/erf - Overloaded function
erfc - tomSym/erfc - Overloaded function
erfcinv - tomSym/erfcinv - Overloaded function
erfinv - tomSym/erfinv - Overloaded function
estpattern - tomSym/estpattern - Estimate the sparsity patte
eval - tomSym/eval - Evaluate a tomSym object in the c
exp - tomSym/exp - Overloaded function
extractConstraints - extrectContraitns - Move constraints from subje
eye - tomSym/eye - Overloaded function
ezplot - tomSym/ezplot - Function plot
feval - tomSym/feval - tomSym-compatible function call.
fix - tomSym/fix - Overloaded function
fliplr - tomSym/fliplr - Overloaded function
flipud - tomSym/flipud - Overloaded function
floor - tomSym/floor - Overloaded function
full - tomSym/full - Overloaded function
fzero - tomSym/fzero - same as tomfzero
gamma - tomSym/gamma - Overloaded function
gammainc - tomSym/gammainc - Overloaded function
gammaln - tomSym/gammaln - Overloaded function
ge - tomSym/ge - Overloaded >= operator
getdiag - tomSym/getdiag - Getdiag for tomSym.
gt - tomSym/gt - Overloaded > operator
holdInterpolationMatrix - holdInterpolationMatrix - Value matrix for inte
horzcat - tomSym/horzcat - Overload the [] operator
hownonlinear - hownonlinear - overloaded function
ifThenElse - tomSym/ifThenElse - Symbolic if/then/else
imag - tomSym/imag - Overloaded function
interp1 - tomSym/interp1 - Overloaded function
interp1h - tomSym/interp1h - Overloaded function
interp1l - tomSym/interp1l - Overloaded function
interp1s - tomSym/interp1s - Overloaded function
interp1sDot - tomSym/interp1sDot - Derivative of interp1s, ov
interp2 - TOMSYM/INTERP2 - overloaded function
interpn - TOMSYM/INTERPN - overloaded function
inv - tomSym/inv - Overloaded function
isdependent - tomSym/isdependent - Determine if f is dependen
isempty - tomSym/isempty - Overloaded function
isequal - tomSym/isequal - Overloaded function
isfinite - tomSym/isfinite - Overloaded function
isinf - tomSym/isinf - Overloaded function
isnan - tomSym/isnan - Overloaded function
isreal - tomSym/isreal - Overloaded function
istomsymbol - tomSym/istomsymbol - Returns "true" if a is a t
kkt1 - kkt1 - First order Karush-Kuhn-Tucker condition
kron - tomSym/kron - Overload the Kronecker product
ldivide - tomSym/ldivide - Overload the .\ left division
le - tomSym/ge - Overloaded <= operator
length - tomSym/length - Get the length of a tomSym obje
linearInterpolationMatrix - linearInterpolationMatrix - Value matrix for in
linspace - tomSym/linspace - Overloaded function
log - tomSym/log - Overloaded function
log10 - tomSym/log10 - Overloaded function
log2 - tomSym/log2 - Overloaded function
logm - tomSym/logm - Overloaded function
lookup - tomSym/lookup - Overloaded function
lt - tomSym/lt - Overloaded < operator
mat2str - tomSym/mat2str - Convert tomSym to a string.
max - tomSym/max - Overloaded
maxIndicator - maxIndicator - Overloaded function
mcode - tomSym/mcode - Generate m-code from a tomSym ob
mcodestr - tomSym/mcodestr - Convert tomSym to m-code
MI - tomSym/MI - Matrix Inequality
min - tomSym/min - Overloaded
minus - tomSym/minus - Overload the minus operator
mldivide - tomSym/mrdivide - Overload the left divide oper
mod - tomSym/mod - Overloaded function
monofun - monofun - Overloaded function
monoinv - monoinv - Overloaded function
mpower - tomSym/mpower - Overload the mpower operator
mrdivide - tomSym/mrdivide - Overload the divide operator
mtimes - tomSym/mtimes - Overload the multiplication ope
nnz - tomSym/spy - Number of nonzero matrix elements
nOperands - tomSym/nOperands - Get number of operands from
norm - tomSym/norm - Overloaded function
not - tomSym/not - Overloaded function
num2str - tomSym/num2str - Convert tomSym to a string.
numel - tomSym/numel - Returns prod(size(a)) for tomSym
operand - tomSym/operand - Get an operand from a tomSym
operands - tomSym/operands - Get all operands from a tomSy
operator - tomSym/operator - Get the operator from a tomSy
pattern - tomSym/pattern - The sparsity pattern of a tomS
plus - tomSym/plus - Overload the plus operator
positiveSemidefinite - tomSym/positiveSemidefinite - Overloaded functi
power - tomSym/power - Overload the mpower operator
ppnval - TOMSYM/PPNVAL - Overloaded function
ppval - tomSym/ppval - Overloaded function
prod - tomSym/prod - Overloaded function
prodJ1 - tomSym/prodJ1 - overloaded function
prodJ1J1 - tomSym/prodJ1J1 - overloaded function
psi - tomSym/psi - Overloaded function
quad - tomSym/quad - Numeric quadrature
rdivide - tomSym/rdivide - Overload the ./ division opera
real - tomSym/real - Overloaded function
rem - tomSym/rem - Overload the remainder function
repmat - tomSym/repmat - Overloaded function
reshape - tomSym/reshape - Overloaded function
rewriteV - rewriteV - Rewrite an optimization problem to a
round - tomSym/round - Overloaded function
scalecolumns - tomSym/scalecolumns - Overloaded function
scalerows - tomSym/scalerows - Overloaded function
sec - tomSym/sec - Overloaded function
setdiag - tomSym/setdiag - Setdiag for tomSym.
setSymmetric - tomSym/setSymmetric - Create a symmetric matrix
sign - tomSym/sign - Overloaded function
sin - tomSym/sin - Overloaded function
sinh - tomSym/sinh - Overloaded function
size - tomSym/size - Get the size of a tomSym object
smplus - tomSym/smplus - Scalar + matrix addition
smtimes - tomSym/smtimes - Scalar x matrix multiplication
sparse - tomSym/sparse - Overloaded function
spdiags - tomSym/spdiags - Overloaded function
spower - tomSym/spower - Element-wise power to a scalar
spy - tomSym/spy - Visualize sparsity pattern for a t
sqrt - tomSym/sqrt - Overloaded function
sub2ind - tomSym/sub2ind - Overloaded function.
submatrix - tomSym/submatrix - Overloaded function
subsasgn - tomSym/subsasgn - Subscripted assignemnt, overl
subsindex - tomSym/subsindex - Not possible
subsref - tomSym/subsref - Object index lookup
subststruct - tomSym/subststruct - Substitue tomSym symbols f
subsymb - tomSym/subsymb - Get a sub-symbol from a tomSym
sum - tomSym/sum - Overloaded function
sym - tomSym/sym - convert a tomSym object to a symbo
sym2eig - sym2eig Convert symbolic constraints into an ei
sym2prob - sym2prob - Compile symbolic function/constraint
symbols - tomSym/symbols - List all symbols used in a tom
tan - tomSym/tan - Overloaded function
tanh - tomSym/tanh - Overloaded function
times - tomSym/times - Overload the .* multiplication o
tomCmp - tomCmp - Test if the operator of a tomSym is of
tomnumeric - tomSym/tomnumeric - isnumeric for tomSym
tomSym - tomSym/tomSym - Class constructor
tomUnGlobalize - tomSym/tomUnGlobalize - undo the effect of tomG
trace - tomSym/trace - Overloaded function
transpose - tomSym/transpose - Overloaded operator
tril - tomSym/tril - Overloaded function
triu - tomSym/triu - Overloaded function
uminus - tomSym/uminus - Overloaded function
uplus - tomSym/uplus - Overloaded function
vec - tomSym/vec - Transform a tomSym object into a c
vertcat - tomSym/vertcat - Overload the [;] operator
wrap - tomSym/wrap - Overloaded
wrapJ - tomSym/wrapJ - Overloaded
In addition to the functions on this list, there are many functions that are supported because they consist entirely of other functions that are supported. For example, it is possible that functions which you have written yourself already work with tomSym. The easiest way to find out if a function is supported is to try to use it. If you don't get an error message, then it works!
We constantly add new functions to TOMSYM. If there's a function that you think would be useful, let us know and we might include it in a future TOMLAB release.