YANE-Framework Tutorial 1.1.0

Tutorial page of class yane::SqpFortran::SqpFortran, yane::SqpNagC3::SqpNagC and yane::YaneIpopt::YaneIpopt

Setup

In this section we shortly explain the structure of a minimization problem in standard form (NLP)

\begin{eqnarray*} \mbox{Minimize} \; F(x) && \\ \mbox{subject to} \; G(x) & = & 0 \\ H(x) & \geq & 0 \end{eqnarray*}

for the class yane::MinProg::NLP objects.

Functions

For numerical purposes it is preferable to divide the minimization problem in standard form (NLP) nto 3 general types of functions: the objective function, the box constraints and the nonlinear constraints. These are the types of functions which we now define for the example problem

\begin{eqnarray*} \mbox{Minimize} && x_1 \cdot x_4 \cdot ( x_1 + x_2 + x_3 ) + x_3 \\ \mbox{subject to} \; x_1^2 + x_2^2 + x_3^2 + x_4^2 & = & 40 \\ x_1 \cdot x_2 \cdot x_3 \cdot x_4 & \geq & 25 \\ x_3 & \geq & 4 \\ x_i & \in & [1, 5] \qquad \forall i \in \{1, \ldots, 4 \} \end{eqnarray*}

First, we identify the objective function:

void objectivFunction ( const double *x, double *fx, void *params )
{
        fx[0] = x[0] * x[3] * ( x[0] + x[1] + x[2] ) + x[2];
}

Secondly, the possibly nonlinear constraints are given by

void restrictionFunction ( const double *x, double *restx, void *params )
{
        restx[0] = 40.0 - ( x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3] ); // = 0.0
        restx[1] = -25.0 + x[0] * x[1] * x[2] * x[3];         // >= 0.0
        // restx[2] = -4.0 + x[2];              // => x[2] >= 4.0;
}

Note that the equality constraints always have to be defined at the beginning of the (nonlinear) constraints.
Last, we define the box constraints for the optimization variables via

void boxConstraints ( int dimension, double * lb, double * ub )
{
        for ( int i = 0; i < n; i++ )
        {
                        lb[i] = 1.0;
                        ub[i] = 5.0;
        }
}

Main Program

Having defined the example problem we now show how the main program can be defined to solve this problem.

Libraries

The first thing to do is to include all required methods for input, output and the optimization itself.

#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cstdlib>
#include <string>

#include <yane/utils.h>

#include <yaneipopt.h>
#include <sqpnagc3.h>
#include <sqpfortran.h>

Namespaces

Since all optimization methods are defined in the own namespaces we declare to use those namespaces.

using namespace std;
using namespace yane::Utils;

using namespace yane::YaneIpopt;
using namespace yane::SqpNagC3;
using namespace yane::SqpFortran;

The main elements of the Main Program

Within the main program, we first define the minimizer objects as objects of the parenting class yane::MinProg::NLP.

        yane::MinProg::NLP *sqpf, *sqpnagc, *ipopt, **minimizer;

Next, we define the size of the problem and allocate the memory for all problem variables:

        int n = 4;                              // Dimension of the problem
        int nrest = 2;                          // Number of constrains (both equality and inequality)
        int nrest_eq = 1;                       // Number of equality constraints
        double optval = 0.0;                    // Optimal solution
        double *x0 = new double[n];             // Startvalue
        double *x = new double[n];              // Optimization variable
        double *lb = new double[n];             // Lower Bound
        double *ub = new double[n];             // Upper Bound
        double *lambda = new double[nrest];     // Array for Lagrangian multipliers of the nonlinear constraints
        double *rest_jac = new double[n*nrest]; // Array for the Jacobian matrix of the constraints

Since we are working with iterative solvers, we have to provide an initial guess of the optimization variable. For our example, we consider $ x_0 = (1, 5, 5, 1) $. At this point, we also define the values of the box constraints. Note that this can be done later in the program as well.

        // Initialization of the startvalue and the boxed constraints
        x0[0] = 1.0;
        x0[1] = 5.0;
        x0[2] = 5.0;
        x0[3] = 1.0;
        boxConstraints ( n, lb, ub );

Knowing the size of the problem, we can now construct and initialize our optimization objects. To simplify the usage of these different optimizers, we summarize them in one object array.

        // Creation and initalization of the optimization routine objects
        names[0] = "SqpFortran";
        sqpf = new yane::SqpFortran::SqpFortran ( n, objectivFunction, restrictionFunction, nrest, nrest_eq );
        sqpf->setAccuracy ( 1.0e-8 );
        sqpf->setMaxIterations ( 1000 );
        sqpf->setAbortTime ( 0.002 );

        names[1] = "SqpNagC";
        sqpnagc = new yane::SqpNagC3::SqpNagC ( n, objectivFunction, restrictionFunction, nrest, nrest_eq );
        sqpnagc->setAccuracy ( 1.0e-8 );
        sqpnagc->setMaxIterations ( 1000 );

        names[2] = "YaneIpopt";
        ipopt = new yane::YaneIpopt::YaneIpopt ( n, objectivFunction, restrictionFunction, nrest, nrest_eq );
        ipopt->setAccuracy ( 1.0e-8 );
        ipopt->setMaxIterations ( 1000 );

        // Auxiliary MinProg-Array
        minimizer = new yane::MinProg::NLP*[3];
        minimizer[0] = sqpf;
        minimizer[1] = sqpnagc;
        minimizer[2] = ipopt;

Now we have completely defined the minimization problem and are ready to start the optimization. To this end, we execute the following code for each of the solvers.

        for ( int i = 0; i < n; i++ )
        {
                x[i] = x0[i];
        }

        try
        {
                minimizer[k]->calcMin ( x, &optval, lb, ub );
        }
        catch ( yane::MinProg::SolverWarning e )
        {
                cout << e.what() << endl;
        }
        duration = timer.elapsedSeconds() * 1000.0;

        try
        {
                minimizer[k]->getLagrangeParameters ( lambda );
        }
        catch ( yane::MinProg::MinProgException e )
        {
                cout << e.what() << endl;
        }

        try
        {
                minimizer[k]->getRestrictionJacobi ( rest_jac );
        }
        catch ( yane::Utils::Exception e )
        {
                cout << e.what() << endl;
        }

In order to compare the results of these routines, the following lines can be used to view (parts of) the computed data:

        objectivFunction ( x, &optval, ( void* ) 0 ); // calculation of the optimal value

        // Print solution and other information
        cout << names[k] << endl;
        cout << "Calculation Time: " << fixed << setw ( 6 ) << setprecision ( 4 ) << duration << " ms" << endl;
        cout << "Optimalvalue: " << setw ( 12 ) << setprecision ( 7 ) << optval << endl;
        cout << "Optimalsolution:" << endl;
        for ( int i = 0; i < n; i++ )
                cout << setw ( 12 ) << setprecision ( 7 ) << x[i];
        cout << endl;

        cout << "Lagrangian Multipliers of the constrains: " << endl;
        for ( int i = 0; i < nrest; i++ )
                cout << setw ( 12 ) << setprecision ( 7 ) << lambda[i];
        cout << endl;

        cout << "Jacobian matrix of the constraints:" << endl;
        switch ( minimizer[k]->memoryModel() )
        {
                case yane::MinProg::BYLINE:
                        for ( int i = 0; i < n; i++ )
                        {
                                for ( int j = 0; j < nrest; j++ )
                                        cout << setw ( 12 ) << setprecision ( 7 ) << rest_jac[i+j*n];
                                cout << endl;
                        }
                        break;
                case yane::MinProg::BYCOLUMN:
                        for ( int i = 0; i < n; i++ )
                        {
                                for ( int j = 0; j < nrest; j++ )
                                        cout << setw ( 12 ) << setprecision ( 7 ) << rest_jac[i*nrest+j];
                                cout << endl;
                        }
                        break;
        }

Last, before terminating the program, we delete all allocated objects and variables:

        for ( int i = 0; i < 3; i++ )
        {
                delete minimizer[i];
        }
        delete[] minimizer;
        delete[] names;
        delete[] x0;
        delete[] x;
        delete[] lb;
        delete[] ub;

Results

Upon execution of this program, we obtain the following output:

SqpFortran
Calculation Time: 0.0800 ms
Optimalvalue:   17.0140173
Optimalsolution:
   1.0000000   4.7429997   3.8211499   1.3794083
Lagrangian Multipliers of the constraints:
   0.1614684   0.5522937
Jacobian matrix of the constraints:
  -2.0000100  25.0000000
  -9.4860094   5.2709259
  -7.6423098   6.5425332
  -2.7588266  18.1237128


SqpNagC
Calculation Time: 0.8090 ms
Optimalvalue:   17.0140173
Optimalsolution:
   1.0000000   4.7429997   3.8211499   1.3794083
Lagrangian Multipliers of the constraints:
   0.1614684   0.5522937
Jacobian matrix of the constraints:
  -2.0000100  25.0000000
  -9.4860094   5.2709259
  -7.6423098   6.5425332
  -2.7588266  18.1237128


YaneIpopt
Calculation Time: 8.4260 ms
Optimalvalue:   17.0140174
Optimalsolution:
   1.0000000   4.7429997   3.8211499   1.3794083
Lagrangian Multipliers of the constraints:
  -0.1614684  -0.5522937
Jacobian matrix of the constraints:
  -2.0000100  25.0000002
  -9.4860094   5.2709260
  -7.6423098   6.5425332
  -2.7588266  18.1237128
Author:
Michael Bodenschatz <michael.bodenschatz@uni-bayreuth.de>