|
A nonlinear minimization problem solving routine - nag_opt_nlp (e04ucc). |
We demonstrate two ways of supplying the pair of functions - either as C code or as Octave code. There are advantages to both of these approaches. Since we need to write C code anyway, in order to interface between Octave and NAG, adding the user-supplied functions as C code is relatively easy. This approach avoids some of the problems associated with converting between C types and Octave types.
The disadvantage of writing the user-supplied functions as C code is that you need to rewrite the functions and regenerate the interface code every time you want to solve a different minimization problem. Although it is harder at first, a more flexible approach is to write an interface that lets the user-supplied functions be written as Octave code.
#include <nag.h>
#include <nage04.h>
void nag_opt_nlp(Integer n, Integer nclin, Integer ncnlin, const double a[], Integer tda, const double bl[], const double bu[],
void(*objfun)(Integer n, const double x[], double *objf, double g[], Nag_Comm *comm),
void(*confun)(Integer n, Integer ncnlin, const Integer needc[], const double x[], double conf[], double conjac[], Nag_Comm *comm),
double x[], double *objf, double g[], Nag_E04_Opt *options, Nag_Comm *comm, NagError *fail);
For argument descriptions consult the e04ucc document in the
NAG C Library Manual [6].
To keep things simple, we are going to return only the integer value of fail.code.
#include <octave/oct.h>
#define Complex NagComplex
#define MatrixType NagMatrixType
#include <nag.h>
#include <nage04.h>
extern "C" {
static void objfun(Integer n, double x[], double *objf,
double objgrd[], Nag_Comm *comm);
static void confun(Integer n, Integer ncnlin, Integer needc[],
double x[], double conf[], double conjac[],
Nag_Comm *comm);
}
DEFUN_DLD (nag_opt, args, ,
"Calls nag_opt_nlp (e04ucc), which minimizes an arbitrary smooth function \n\
subject to constraints using a sequential quadratic programming (SQP) method.\n\
As many first derivatives as possible should be supplied by you; \n\
any unspecified derivatives are approximated by finite differences. \n\
It is not intended for large sparse problems.\n\
nag_opt_nlp (e04ucc) may also be used for unconstrained, bound-constrained \n\
and linearly constrained optimization.\n")
{
// Variable to store function output values
octave_value_list retval;
// Retrieve input arguments from args
Integer n = args(0).int_value();
Integer nclin = args(1).int_value();
Integer ncnlin = args(2).int_value();
Matrix a(args(3).matrix_value());
Integer tda = args(4).int_value();
NDArray bl(args(5).array_value());
NDArray bu(args(6).array_value());
NDArray x(args(7).array_value());
dim_vector dv (1); dv(0) = n;
NDArray objgrd(dv);
// Declare local variables
double objf;
Nag_Comm comm;
NagError fail;
INIT_FAIL(fail);
// Call NAG routine
nag_opt_nlp(n, nclin, ncnlin, a.fortran_vec(), tda, bl.fortran_vec(),
bu.fortran_vec(), objfun, confun, x.fortran_vec(), &objf,
objgrd.fortran_vec(), E04_DEFAULT, &comm, &fail);
// Assign output arguments to retval
retval(0) = objf;
retval(1) = objgrd;
retval(2) = fail.code;
return retval;
}
static void objfun(Integer n, double x[], double *objf,
double objgrd[], Nag_Comm *comm)
{
/* Routine to evaluate objective function and its 1st derivatives. */
if (comm->flag == 0 || comm->flag == 2)
*objf = x[0] * x[3] * (x[0] + x[1] + x[2]) + x[2];
if (comm->flag == 2)
{
objgrd[0] = x[3] * (2.0*x[0] + x[1] + x[2]);
objgrd[1] = x[0] * x[3];
objgrd[2] = x[0] * x[3] + 1.0;
objgrd[3] = x[0] * (x[0] + x[1] + x[2]);
}
} /* objfun */
static void confun(Integer n, Integer ncnlin, Integer needc[],
double x[], double conf[], double conjac[],
Nag_Comm *comm)
{
/* Routine to evaluate the nonlinear constraints and
* their 1st derivatives.
*/
#define CONJAC(I,J) conjac[(I-1)*n + J - 1]
Integer i, j;
if (comm->first)
{
/* First call to confun. Set all Jacobian elements to zero.
* Note that this will only work when con_deriv = TRUE
* (the default; see Section 4 (confun) and 8.2 (con_deriv)).
*/
for (j = 1; j <= n; ++j)
for (i = 1; i <= ncnlin; ++i)
CONJAC(i,j) = 0.0;
}
if (needc[0] > 0)
{
if (comm->flag == 0 || comm->flag == 2)
conf[0] = x[0] * x[0] + x[1] * x[1] + x[2] * x[2] + x[3] * x[3];
if (comm->flag == 2)
{
CONJAC(1,1) = x[0] * 2.0;
CONJAC(1,2) = x[1] * 2.0;
CONJAC(1,3) = x[2] * 2.0;
CONJAC(1,4) = x[3] * 2.0;
}
}
if (needc[1] > 0)
{
if (comm->flag == 0 || comm->flag == 2)
conf[1] = x[0] * x[1] * x[2] * x[3];
if (comm->flag == 2)
{
CONJAC(2,1) = x[1] * x[2] * x[3];
CONJAC(2,2) = x[0] * x[2] * x[3];
CONJAC(2,3) = x[0] * x[1] * x[3];
CONJAC(2,4) = x[0] * x[1] * x[2];
}
}
} /* confun */
Points to note about this code:
% mkoctfile nag_opt.cc -L/opt/NAG/cll6a08dg/lib -lnagc_nag -I/opt/NAG/cll6a08dg/includewhere:
octave:1> a = [1.0;1.0;1.0;1.0]; octave:2> bl = [1.0,1.0,1.0,1.0,-1.0E+25,-1.0E+25,25.0]; octave:3> bu = [5.0,5.0,5.0,5.0,20.0,40.0,1.0E+25]; octave:4> x = [1.0,5.0,5.0,1.0]; octave:5> [objf,objgrd,ifail] = nag_opt(4,1,2,a,4,bl,bu,x)
(If you get an error message saying that a library cannot be located, see the tip given in Example 1).
#include <octave/oct.h>
#include <octave/parse.h>
#define Complex NagComplex
#define MatrixType NagMatrixType
#include <nag.h>
#include <nage04.h>
extern "C" {
static void objfunC(Integer n, double x[], double *objf,
double objgrd[], Nag_Comm *comm);
static void confunC(Integer n, Integer ncnlin, Integer needc[],
double x[], double conf[], double conjac[],
Nag_Comm *comm);
}
DEFUN_DLD (nag_opt2, args, ,
"Calls nag_opt_nlp (e04ucc), which minimizes an arbitrary smooth function \n\
subject to constraints using a sequential quadratic programming (SQP) method.\n \
As many first derivatives as possible should be supplied by you; \n \
any unspecified derivatives are approximated by finite differences. \n \
It is not intended for large sparse problems.\n \
nag_opt_nlp (e04ucc) may also be used for unconstrained, bound-constrained \n \
and linearly constrained optimization.\n")
{
// Variable to store function output values
octave_value_list retval;
// Retrieve input arguments from args
Integer n = args(0).int_value();
Integer nclin = args(1).int_value();
Integer ncnlin = args(2).int_value();
Matrix a(args(3).matrix_value());
Integer tda = args(4).int_value();
NDArray bl(args(5).array_value());
NDArray bu(args(6).array_value());
struct fcnptrs {octave_function *obj, *con;} fcns;
fcns.obj = args(7).function_value();
fcns.con = args(8).function_value();
NDArray x(args(9).array_value());
dim_vector dv (1); dv(0) = n;
NDArray objgrd(dv);
// Declare local variables
double objf=0.0;
Nag_Comm comm;
NagError fail;
INIT_FAIL(fail);
// Store Octave functions handles in communication structure comm
comm.p = (Pointer) &fcns;
// Call NAG routine
nag_opt_nlp(n, nclin, ncnlin, a.fortran_vec(), tda, bl.fortran_vec(),
bu.fortran_vec(),objfunC, confunC, x.fortran_vec(),&objf,
objgrd.fortran_vec(), E04_DEFAULT, &comm, &fail);
// Assign output arguments to retval
retval(0) = objf;
retval(1) = objgrd;
retval(2) = fail.code;
return retval;
}
static void objfunC(Integer n, double x[], double *objf,
double objgrd[], Nag_Comm *comm)
{
// Retrieve Octave objfun handle
struct fcnptrs {octave_function *obj, *con;} *fcns;
fcns = (struct fcnptrs *) comm->p;
octave_function *fcn = (octave_function *) fcns->obj;
// Copy C arrays into the Octave type ones
dim_vector dv (1); dv(0) = n;
NDArray x_oct(dv), objgrd_oct(dv);
octave_idx_type i;
for (i=0;i<n;i++) {
x_oct.elem(i) = x[i];
objgrd_oct.elem(i) = objgrd[i];
}
// Assign objfun input arguments
octave_value_list newargs, retval;
newargs(0)=octave_value(comm->flag);
newargs(1)=octave_value(x_oct);
newargs(2)=octave_value(*objf);
newargs(3)=octave_value(objgrd_oct);
int nargout=2;
// Call Octave objfun function
retval=feval(fcn,newargs,nargout);
// Retrieve output arguments from retval
*objf=retval(0).double_value();
objgrd_oct = retval(1).array_value();
// Copy output arrays back into C type arrays
for (i=0;i<n;i++) {
objgrd[i]=objgrd_oct.elem(i);
x[i]=x_oct.elem(i);
}
} /* objfunC */
static void confunC(Integer n, Integer ncnlin, Integer needc[],
double x[], double conf[], double conjac[],
Nag_Comm *comm)
{
#define CONJAC(I,J) conjac[(I-1)*n + J - 1]
// Retrieve Octave confun handle
struct fcnptrs {octave_function *obj, *con;} *fcns;
fcns = (struct fcnptrs *) comm->p;
octave_function *fcn = (octave_function *) fcns->con;
// Copy C arrays into the Octave type arrays and matrices
dim_vector dv (1); dv(0) = ncnlin;
dim_vector dv2 (1); dv2(0) = n;
NDArray needc_oct(dv), conf_oct(dv), x_oct(dv2);
Matrix conjac_oct(ncnlin,n);
octave_idx_type i,j;
for (i=0;i<ncnlin;i++) {
needc_oct.elem(i) = needc[i];
conf_oct.elem(i) = conf[i];
for (j=0;j<n;j++) {
x_oct.elem(j) = x[j];
conjac_oct.elem(i,j) = CONJAC(i+1,j+1);
}
}
// Assign confun input arguments
octave_value_list newargs, retval;
newargs(0)=octave_value(comm->flag);
newargs(1)=octave_value(ncnlin);
newargs(2)=octave_value(n);
newargs(3)=octave_value(needc_oct);
newargs(4)=octave_value(x_oct);
newargs(5)=octave_value(conjac_oct);
newargs(6)=octave_value(comm->first);
int nargout=2;
// Call Octave confun function
retval=feval(fcn,newargs,nargout);
// Retrieve output arguments from retval
conf_oct=retval(0).array_value();
conjac_oct = retval(1).matrix_value();
// Copy output arrays and matrices back into C type arrays
for (i=0;i<ncnlin;i++) {
needc[i] = needc_oct.elem(i);
conf[i] = conf_oct.elem(i);
for (j=0;j<n;j++) {
x[j]=x_oct.elem(j);
CONJAC(i+1,j+1) = conjac_oct.elem(i,j);
}
}
} /* confunC */
Points to note about this code (other than in the previous code):
function [objf,objgrd]=objfun(mode,x,objf,objgrd)
% Routine to evaluate objective function and its 1st derivatives.
if mode == 0 || mode == 2
objf = x(1) * x(4) * (x(1) + x(2) + x(3)) + x(3);
end
if mode == 2
objgrd(1) = x(4) * (2.0*x(1) + x(2) + x(3));
objgrd(2) = x(1) * x(4);
objgrd(3) = x(1) * x(4) + 1.0;
objgrd(4) = x(1) * (x(1) + x(2) + x(3));
end
endfunction
function [c, cjac] = confun(mode, ncnln, n, needc, x, cjac, nstate)
% Routine to evaluate the nonlinear constraints and their 1st derivatives.
% Function Body
if nstate
% First call to confun. Set all Jacobian elements to zero.
cjac = zeros(ncnln,n);
end
if needc(1) > 0
if mode == 0 || mode == 2
c(1) = x(1) * x(1) + x(2) * x(2) + x(3) * x(3) + x(4) * x(4);
end
if mode == 2
cjac(1,1) = x(1) * 2.0;
cjac(1,2) = x(2) * 2.0;
cjac(1,3) = x(3) * 2.0;
cjac(1,4) = x(4) * 2.0;
end
end
if needc(2) > 0
if mode == 0 || mode == 2
c(2) = x(1) * x(2) * x(3) * x(4);
end
if mode == 2
cjac(2,1) = x(2) * x(3) * x(4);
cjac(2,2) = x(1) * x(3) * x(4);
cjac(2,3) = x(1) * x(2) * x(4);
cjac(2,4) = x(1) * x(2) * x(3);
end
end
endfunction
% mkoctfile nag_opt2.cc -L/opt/NAG/cll6a08dg/lib -lnagc_nag -I/opt/NAG/cll6a08dg/includewhere:
octave:1> a = [1.0;1.0;1.0;1.0]; octave:2> bl = [1.0,1.0,1.0,1.0,-1.0E+25,-1.0E+25,25.0]; octave:3> bu = [5.0,5.0,5.0,5.0,20.0,40.0,1.0E+25]; octave:4> x = [1.0,5.0,5.0,1.0]; octave:5> [objf,objgrd,ifail] = nag_opt2(4,1,2,a,4,bl,bu,@objfun,@confun,x)
Parameters to e04ucc
--------------------
Number of variables........... 4
Linear constraints............ 1 Nonlinear constraints......... 2
start................... Nag_Cold
step_limit.............. 2.00e+00 machine precision....... 1.11e-16
lin_feas_tol............ 1.05e-08 nonlin_feas_tol......... 1.05e-08
optim_tol............... 3.26e-12 linesearch_tol.......... 9.00e-01
crash_tol............... 1.00e-02 f_prec.................. 4.37e-15
inf_bound............... 1.00e+20 inf_step................ 1.00e+20
max_iter................ 50 minor_max_iter.......... 50
hessian................. Nag_FALSE
f_diff_int.............. Automatic c_diff_int.............. Automatic
obj_deriv............... Nag_TRUE con_deriv............... Nag_TRUE
verify_grad....... Nag_SimpleCheck print_deriv............ Nag_D_Full
print_level......... Nag_Soln_Iter minor_print_level..... Nag_NoPrint
outfile................. stdout
Verification of the objective gradients.
----------------------------------------
All objective gradient elements have been set.
Simple Check:
The objective gradient seems to be ok.
Directional derivative of the objective 8.15250000e-01
Difference approximation 8.15249734e-01
Verification of the constraint gradients.
-----------------------------------------
All constraint gradient elements have been set.
Simple Check:
The Jacobian seems to be ok.
The largest relative error was 2.29e-07 in constraint 2
Maj Mnr Step Merit function Violtn Norm Gz Cond Hz
0 4 0.0e+00 1.738281e+01 1.2e+01 7.1e-01 1.0e+00
1 1 1.0e+00 1.703169e+01 1.9e+00 4.6e-02 1.0e+00
2 1 1.0e+00 1.701442e+01 8.8e-02 2.1e-02 1.0e+00
3 1 1.0e+00 1.701402e+01 5.4e-04 3.1e-04 1.0e+00
4 1 1.0e+00 1.701402e+01 9.9e-08 7.0e-06 1.0e+00
5 1 1.0e+00 1.701402e+01 4.6e-11 1.1e-08 1.0e+00
Exit from NP problem after 5 major iterations, 9 minor iterations.
Varbl State Value Lower Bound Upper Bound Lagr Mult Residual
V 1 LL 1.00000e+00 1.00000e+00 5.00000e+00 1.0879e+00 0.0000e+00
V 2 FR 4.74300e+00 1.00000e+00 5.00000e+00 0.0000e+00 2.5700e-01
V 3 FR 3.82115e+00 1.00000e+00 5.00000e+00 0.0000e+00 1.1789e+00
V 4 FR 1.37941e+00 1.00000e+00 5.00000e+00 0.0000e+00 3.7941e-01
L Con State Value Lower Bound Upper Bound Lagr Mult Residual
L 1 FR 1.09436e+01 None 2.00000e+01 0.0000e+00 9.0564e+00
N Con State Value Lower Bound Upper Bound Lagr Mult Residual
N 1 UL 4.00000e+01 None 4.00000e+01 -1.6147e-01 -3.5264e-11
N 2 LL 2.50000e+01 2.50000e+01 None 5.5229e-01 -2.8791e-11
Optimal solution found.
Final objective value = 1.7014017e+01
objf = 17.014
objgrd =
14.5723
1.3794
2.3794
9.5641
ifail = 0