NlpDefine
Defines a non-linear programming problem.
Requires Analytica Optimizer, Analytica Power Player with Optimizer, or ADE with Optimizer.
What is a non-linear program?
A non-linear program is an optimization problem where you wish to find a vector for the decision variables that minimizes (or maximizes) an objective function subject to a set of constraints and variable bounds. Special cases also include unconstrained optimization and finding a solution to a system of non-linear equations. Decision variables may be continuous or mixed-integer.
Linear and quadratic optimization problems can be formulated as NLPs, but when it is possible, you are much better off encoding these as linear or quadratic programs using LpDefine or QpDefine. Linear and quadratic formulations more readily array abstract and suffer from fewer numeric convergence issues.
Structuring an NLP
To encode an NLP in an Analyica model, you begin by identifying a decision variable in your model, which will be set to candidate solutions during the optimization search. This decision variable is nominally a one-dimensional vector, so that there will be an index associated with it, as in the following:
Index V := ['x1','x2','x3'] Variable X := Table(V)
You can also use a self-indexed table for X, in which X serves as both the index and the decision variable.
Using the values of V, your model will compute an objective variable, let's suppose it is named Y. You may have a very complex model, with large numbers of variables between X and Y, utilizing arbitrary Analytica expressions. Y is the objective. For an unconstrained optimization problem, these provide the essential pieces.
It is also possible to use a local variable for X, defined using Var..Do. X may then appear in the expression provided to NlpDefine's parameters obj, lhs, gradient, or hessian. This is often used when an NLP is defined inside a User-Defined Function.
Unconstrained Optimization
An unconstrained minimization problem can be defined using:
NlpDefine( Vars: V, X:X, Obj:Y )
Evaluation of NlpDefine this returns an NLP object, which displays as <<NLP>> in the result table. This is a problem definition, from which various pieces of information can be extracted, including the solution.
To solve the NLP, use the LpSolution function, which returns a vector, indexed by V (the index provided as the first parameter, Vars), with the solution found. However, it is also important that you check the status using LpStatusText or LpStatusNum to determine why the search terminated and whether a solution was successfully found. Thus, you may have variables such as the following:
Variable My_Nlp := NlpDefine( Vars: V, X:X, Obj: Y ) Variable NlpStatus := LpStatusText(My_Nlp) Variable X_opt := LpSolution(My_Nlp)
Note that after the search completes, the optimal solution is in X_opt, rather than in X. It is common to use a button with a script such as: (X := X_opt), where the surrounding parens are necessary, when you want X to be set.
Goal Seek
A further special case of unconstrained optimization occurs when the decision variable is a scalar, e.g., find scalar X that minimizes Y, or that minimizes (Y-g)^2. In this case, the Vars index is not necessary, and a simple problem can be defined as:
NlpDefine( X:X, Obj:Y )
Since so many of the parameters of NlpDefine are optional, it is standard to used a named-parameter syntax.
There is also a GoalSeek function in a library included with Analytica, with does not require Analyica Optimizer. The GoalSeek function uses a very simple Newton-Rapson descent algorithm that may be sufficient in many cases, but provides fewer options for diagnosing and addressing convergence problems on hard optimization problems. In general, NlpDefine provides higher quality algorithms.
Constrained Optimization
In constrained optimization, which typically is associated with NLPs, we also have one or more non-linear constraints. Here we have constraints of the form
- g_j(X,V) = b_j
Let's look at an example with a single non-linear constraint. Suppose we want to minimize surface area of a cylinder that is constrained to have a volume of 1, where surface_area and volume are computed in variables of those names. The NLP with one constraint can be defined as
NlpDefine( Vars:V, X:X, obj:surface_area, lhs:volume, sense:'=', rhs:1, lb:0 )
In this formulation, the constraint is encoded using the three parameters lhs, sense, and rhs. Of these, only the lhs can depend on X. The other two, sense and rhs, do not vary during the optimization search. We also include a variable bound here, lb, so that the dimensions (elements of X) are constrained to be non-negative. Note that the obj or lhs could be an expression referencing X directly.
When we have two or more constraints, we must also create a constraint index, to index the set of constraints. Generally the constraint index is defined as a list of labels, with each label providing a meaningful name for the constraint. It is easiest to set up the left-hand side in a table, in its own variable. (Here we've changed the volume constraint from an equality to a minimum required volume).
Index Constraints := ['min volume','taller than wide']
Variable lhs := Table(constraints)(volume-1,h-w)
Variable my_Nlp := NlpDefine( Vars:V,Constraints:constraints,obj:surface_area,
Lhs:lhs, sense:'>',rhs:0, lb:0 )
Enable comment auto-refresher