The OPTLSO Procedure

Introductory Examples

The following introductory examples illustrate how to get started using the OPTLSO procedure.

A Bound-Constrained Problem

Consider the simple example of minimizing the Branin function,

\begin{eqnarray*}  f(x) &  = &  \left(x_2-\dfrac {5.1}{4\pi ^2} x_1^2+ \dfrac {5}{\pi }x_1-6 \right)^2 +10\left(1-\dfrac {1}{8\pi }\right)\cos (x_1)+10 \end{eqnarray*}

subject to $-5 \le x_1 \le 10 $ and $0 \le x_2 \le 15$ (Jones, Perttunen, and Stuckman, 1993). The minimum function value is $ f(x^*) = 0.397887$ at $ x^* = (-\pi ,12.275),(\pi ,2.275),(9.42478,2.475).$ You can use the following statements to solve this problem:

data vardata;
   input _id_ $ _lb_ _ub_;
   datalines;
x1 -5 10
x2  0 15
;

proc fcmp outlib=sasuser.myfuncs.mypkg;
   function branin(x1, x2);
      pi = constant('PI');
      y1 = (x2-(5.1/(4*pi**2))*x1*x1+5*x1/pi-6)**2;
      y2 = 10*(1-1/(8*pi))*cos(x1);
      return (y1+y2+10);
   endsub;
run;

data objdata;
   input _id_ $ _function_ $ _sense_ $;
   datalines;
f branin min
;

options cmplib = sasuser.myfuncs;
proc optlso
   primalout = solution
   variables = vardata
   objective = objdata;
   performance nthreads=4;
run;

proc print data=solution;
run;

The OBJECTIVE= option in the PROC OPTLSO statement refers to the OBJDATA data set, which identifies BRANIN as the name of the objective function that is defined in the FCMP library sasuser.myfuncs and specifies that BRANIN should be minimized. The VARIABLES= option in the PROC OPTLSO statement names the decision variables x1 and x2 and specifies lower and upper bounds. The PERFORMANCE statement specifies the number of threads that PROC OPTLSO can use.

Figure 3.1 shows the ODS tables that the OPTLSO procedure produces by default.

Figure 3.1: Bound-Constrained Problem: Output

The OPTLSO Procedure

Performance Information
Execution Mode Single-Machine
Number of Threads 4

Problem Summary
Problem Type NLP
   
Objective Definition Set OBJDATA
Variables VARDATA
   
Number of Variables 2
Integer Variables 0
Continuous Variables 2
   
Number of Constraints 0
Linear Constraints 0
Nonlinear Constraints 0
   
Objective Definition Source OBJDATA
Objective Sense Minimize

Solution Summary
Solution Status Function convergence
Objective 0.3978873689
Infeasibility 0
Iterations 32
Evaluations 1760
Cached Evaluations 81
Global Searches 1
Population Size 80
Seed 1

Obs _sol_ _id_ _value_
1 0 _obj_ 0.39789
2 0 _inf_ 0.00000
3 0 x1 9.42482
4 0 x2 2.47508
5 0 f 0.39789


Adding a Linear Constraint

You can use a LINCON= option to specify general linear equality or inequality constraints of the following form:

\[  \sum _{j=1}^ n a_{ij} x_ j \:  \{ \le | = | \ge \}  \:  b_ i \quad \mr {for} \;  i=1,\ldots ,m  \]

For example, suppose that in addition to the bound constraints on the decision variables, you need to guarantee that the sum $ x_1 + x_2$ is less than or equal to $0.6$. To guarantee this, you can add a LINCON= option to the previous data set definitions, as in the following statements:

data lindata;
   input _id_ $ _lb_ x1 x2 _ub_;
   datalines;
a1  . 1 1  0.6
;

proc optlso
   variables = vardata
   objective = objdata
   lincon    = lindata;
run;

Here the symbol A1 denotes the name of the given linear constraints that are specified in the _ID_ column. The corresponding lower and upper bounds are specified in the _LB_ and _UB_ columns, respectively.

Nonlinear Constraints on the Decision Variables

You can specify general nonlinear equality or inequality constraints by using the NLINCON= option and adding its definition to the existing PROC FCMP library. Consider the previous problem with the following additional constraint:

\[  x_1^2 - 2x_2 \ge 14  \]

You can specify this constraint by adding a new FCMP function library and providing it a corresponding name and bounds in the NLINCON= option, as in the following statements:

data condata;
   input _id_ $ _lb_ _ub_;
   datalines;
c1  14 .
;

proc fcmp outlib=sasuser.myfuncs.mypkg;
   function c1(x1, x2);
      return (x1**2 - 2*x2);
   endsub;
run;

proc optlso
   variables = vardata
   objective = objdata
   lincon    = lindata
   nlincon   = condata;
run;

By calling PROC FCMP a second time, you can append the definition of C1 to your existing user library. That is, you do not need to redefine BRANIN after it has been added.

A Simple Maximum Likelihood Example

The following is a very simple example of a maximum likelihood estimation problem that uses the log-likelihood function:

\[  l(\mu ,\sigma ) = -\log (\sigma ) - \frac{1}{2} \left( \frac{x - \mu }{\sigma } \right) ^2  \]

The maximum likelihood estimates of the parameters $\mu $ and $\sigma $ form the solution to

\[  \max _{\mu , \sigma } \sum _ i l_ i(\mu ,\sigma )  \]

where $\sigma > 0$ and

\[  l_ i(\mu ,\sigma ) = -\log (\sigma ) - \frac{1}{2} \left( \frac{x_ i - \mu }{\sigma } \right) ^2  \]

The following sets of statements demonstrate two ways to formulate this example problem:


data lkhvar;
   input _id_ $ _lb_;
   datalines;
mu    .
sigma 0
;

data lkhobj1;
   input _id_ $ _function_ $ _sense_ $;
   datalines;
f loglkh1 max
;

In the following statements, the FCMP function is stand-alone because all the necessary data are defined within the function itself:

proc fcmp outlib=sasuser.myfuncs.mypkg;
   function loglkh1(mu, sigma);
      array x[5] / nosym (1 3 4 5 7);
      s=0;
      do j=1 to 5;
         s = s - log(sigma) - 0.5*((x[j]-mu)/sigma)**2;
      end;
      return (s);
   endsub;
run;

proc optlso
   variables = lkhvar
   objective = lkhobj1;
run;

Alternatively, you can use an external data set to store the necessary observations and run PROC OPTLSO to feed each observation to an FCMP function that processes a single line of data. In this case, PROC OPTLSO sums the results for you. This mode is particularly useful when the data set is large and possibly distributed over a set of nodes, as shown in Using External Data Sets.

The following statements demonstrate how to store the necessary observations in an external data set for use with PROC FCMP:

data logdata;
   input x @@;
   datalines;
1 3 4 5 7
;

data lkhobj2;
   input _id_ $ _function_ $ _sense_ $ _dataset_ $;
   datalines;
f loglkh2 max logdata
;

proc fcmp outlib=sasuser.myfuncs.mypkg;
   function loglkh2(mu, sigma, x);
      return (-log(sigma) -0.5*((x-mu)/sigma)**2);
   endsub;
run;

proc optlso
   variables = lkhvar
   objective = lkhobj2;
run;

In this case, for each line of data in the data set logdata, the FCMP function LOGLKH2 is called. It is important that the non-variable arguments of LOGLKH2 coincide with a subset of the column names in logdata, in this case X. However, the order in which the variables and data column names appear is not important. The following definition would work as well:


data lkhobj3;
   input _id_ $ _function_ $ _sense_ $ _dataset_ $;
   datalines;
f loglkh3 max logdata
;

proc fcmp outlib=sasuser.myfuncs.mypkg;
   function loglkh3(x, sigma, mu);
      return (- log(sigma) - 0.5*((x-mu)/sigma)**2);
   endsub;
run;

proc optlso
   variables = lkhvar
   objective = lkhobj3;
run;