This article was originally published on the NAG Blog. Further items may be posted on the blog from time to time.
This article goves examples of C++/CLR use and of using methods from objects rather than static methods as callback parameters to the NAG methods.
It takes the existing C# example for the optimisation routine e04uc and modifies it in two ways. Firstly converting to C++/CLR, and then modifying the example so that the objective function being minimised is a method of an object (of locally declared class NagDelegate) and to demonstrate two ways in which data may be passed between successive calls to this callback parameter.
Note that C++/CLR has extended syntax (notably ^ and gcnew in this example) which declare managed data that will be managed and potentially garbage collected by the .NET runtime system.
Once the class and its method are defined, the only thing that needs to be done to pass this method to the e04uc method is to declare the delegate object using the syntax
E04::E04UC_OBJFUN^ objfunE =
gcnew E04::E04UC_OBJFUN(nagdelegate, &NagDelegate::objfun);
Unlike the situation in C#, you need to specify both the class and the particular method as two separate parameters when declaring the delegate.
As mentioned above, the method NagDelegate::objfun demonstrates two ways in which data may be accessed, in both cases here the data is a counter reporting how many times the objective function was evaluated by the optimisation algorithm. static_counter is a static variable and counter is a local field of the NagDelegate object. As may be seen by the results when the program is run, both methods work as expected, returning the same value.
static_counter++; counter++;
The C++ example program is shown in full at the end of this post. To run it using Visual Studio, just start a new project and replace the default program by the code below. You must then select the project references and add a reference to the NagLibrary32 or NagLibrary64 .NET assemblies. The program should then run and produce the output shown.
// csharpfromcpp.cpp : main project file. #include "stdafx.h" using namespace System; using namespace NagLibrary; static int static_counter; public ref class NagDelegate { public: int counter; void objfun(int% mode, int n, array <double>^ x, double% objf, array <double>^ objgrd, int nstate) { // Routine to evaluate objective function // and its 1st derivatives. static_counter++; counter++; double one = 1.00e0; double two = 2.00e0; if ((mode == 0) || (mode == 2)) { objf = x[0] * x[3] * (x[0] + x[1] + x[2]) + x[2]; } // if ((mode == 1) || (mode == 2)) { objgrd[0] = x[3] * (two * x[0] + x[1] + x[2]); objgrd[1] = x[0] * x[3]; objgrd[2] = x[0] * x[3] + one; objgrd[3] = x[0] * (x[0] + x[1] + x[2]); } // } }; void confun( int% mode, int ncnln, int n, array< int >^ needc, array< double >^ x, array <double>^ c, array <double,2>^ cjac, int nstate) { // Routine to evaluate the nonlinear constraints // and their 1st derivatives. double zero = 0.00e0; double two = 2.00e0; int i, j; if (nstate == 1) { // First call to confun. Set all Jacobian elements to zero. // Note that this will only work when 'Derivative Level = 3' // (the default; see Section 11.2). for (j = 1; j <= n; j++) { for (i = 1; i <= ncnln; i++) { cjac[i - 1, j - 1] = zero; } } } // if (needc[0] > 0) { if ((mode == 0) || (mode == 2)) { c[0] = (x[0]) * (x[0]) + (x[1]) * (x[1]) + (x[2]) * (x[2]) + (x[3]) * (x[3]); } if ((mode == 1) || (mode == 2)) { cjac[0, 0] = two * x[0]; cjac[0, 1] = two * x[1]; cjac[0, 2] = two * x[2]; cjac[0, 3] = two * x[3]; } } // if (needc[1] > 0) { if ((mode == 0) || (mode == 2)) { c[1] = x[0] * x[1] * x[2] * x[3]; } if ((mode == 1) || (mode == 2)) { cjac[1, 0] = x[1] * x[2] * x[3]; cjac[1, 1] = x[0] * x[2] * x[3]; cjac[1, 2] = x[0] * x[1] * x[3]; cjac[1, 3] = x[0] * x[1] * x[2]; } } // } int main(array<System::String ^> ^args) { double objf = 0.0; int i, ifail, iter, j, n, nclin, ncnln; Console::WriteLine(L"e04uc Example Program Results"); // Skip heading in data file n = 4; nclin = 1; ncnln = 2; array< double, 2 >^ a = gcnew array< double, 2 >( nclin, n); a[0, 0]=1; a[0, 1]=1; a[0, 2]=1; a[0, 3]=1; array< double >^ bl = gcnew array< double >(n + nclin +ncnln); bl[0] = 1.0; bl[1] = 1.0; bl[2] = 1.0; bl[3] = 1.0; bl[4] = -1.0e+25; bl[5] = -1.0e+25; bl[6] = 25.0; array< double >^ bu = gcnew array< double >(n + nclin +ncnln); bu[0] = 5.0; bu[1] = 5.0; bu[2] = 5.0; bu[3] = 5.0; bu[4] = 20.0; bu[5] = 40.0; bu[6] = 1.0e25; array< double >^ c = gcnew array< double >(ncnln); array< double, 2 >^ cjac = gcnew array< double, 2 >(ncnln, n); array< double >^ clamda = gcnew array< double >(n + nclin +ncnln); array< double >^ objgrd = gcnew array< double >(n); array< double, 2 >^ r = gcnew array< double, 2 >(n, n); array< double >^ x = gcnew array< double >(n); x[0] = 1.0; x[1] = 5.0; x[2] = 5.0; x[3] = 1.0; array< int >^ istate = gcnew array< int >(n + nclin +ncnln); E04::e04ucOptions^ options = gcnew E04::e04ucOptions() ; E04::E04UC_CONFUN^ confunE = gcnew E04::E04UC_CONFUN(confun); NagDelegate^ nagdelegate = gcnew NagDelegate(); E04::E04UC_OBJFUN^ objfunE = gcnew E04::E04UC_OBJFUN(nagdelegate, &NagDelegate::objfun); E04::e04uc(n, nclin, ncnln, a, bl, bu, confunE, objfunE, iter, istate, c, cjac, clamda, objf, objgrd, r, x, options, ifail); Console::WriteLine("The objective value on return is {0}. ", objf); Console::WriteLine("Objfun has been called {0} times, using class member", nagdelegate->counter); Console::WriteLine("Objfun has been called {0} times, using a static variable", static_counter); return 0; }
When run the following output is produced:
e04uc Example Program Results The objective value on return is 17.0140172891347. Objfun has been called 7 times, using class member Objfun has been called 7 times, using a static variable