• Tidak ada hasil yang ditemukan

WRAPPING C WITH C++ FOR OO NUMERICS IN .NET .1 Introduction

Dalam dokumen COMPUTATIONAL FINANCE - untag-smd.ac.id (Halaman 79-88)

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D64– [64–74/11]

21.11.2003 3:29PM

Chapter 7

Epilogue

7.1 WRAPPING C WITH C++ FOR OO NUMERICS IN .NET

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D65– [64–74/11]

21.11.2003 3:29PM

The recent introduction of .NET assemblieshas now substantially improved this situation. Briefly, the classes in an assembly can be coded in any of the .NET languages and then used by any other .NET language. It is thus possible to create assemblies inmanaged C++that provide class wrappers for C routines, and then use these from C# and VB.NET software, see Challa and Laksberg (2002).

Code excerpt 7.1 shows the ANSI function prototypes of our four NAG C functions. The managed Cþþ code used to create an assembly that wraps the C functions is displayed in Code excerpt 7.2. We have called this assembly naglib, and it defines the namespace NAGLIB and the managed class NAG_FUNCTIONS which provides functions to access native C routines contained within the DLL

‘nagc’. The Code excerpt 7.2 is only meant for illustrative purposes and is not intended to be a statement of good programming practice. However, we have included some useful features such as flagging errors and setting default parameter values via the constructorNAG_FUNCTIONS().

/* declaration of function pointer E04DGC_FUN */

typedef void (*E04DGC_FUN) (long, double *, double *, double *, Nag_Comm *);

/* declaration of function pointer D01AJC_FUN */

typedef double (*D01AJC_FUN) (double);

/* declaration of function prototypes */

void e04dgc (long n, E04DGC_FUN objfun, double x[], double *objf, double grad[], Nag_E04_0pt *options, Nag_Comm *user_comm, NagError *fail);

void f02aac(long n, double *a, long tda, double *r, NagError *fail);

void d01ajc(D01AJC_FUN f, double a, double b, double epsabs, double epsrel, long max_num_subint, double *result, double *abserr, Nag_QuadProgress *qp, NagError *fail);

double s15abc(double x, NagError *fail);

Code excerpt 7.1 The ANSI function pointers and function prototypes for the NAG C routines.

The typesNag_Comm,NagError,Nag_E04_0pt, andNag_QuadProgress are declared in the header filenag_types.hwhich is included in the header filenag.h

extern ‘‘C’’ {

#include <stdio.h>

#include <nag.h>

#include <nag_stdlib.h>

}

#using <mscorlib.dll>

using namespace System::Runtime::InteropServices;

using namespace System;

namespace NAGLIB {

public _ _delegate double INTEGRAND_FUN_TYPE(Double x);

public _ _delegate void OBJ_FUN_TYPE (Int32 n, double *x, double *objf, double *g, Int32 comm);

[D11Import(‘‘nagc’’)]

extern ‘‘C’’ void e04dgc (Int32 n, OBJ_FUN_TYPE *f, Double *x, Double *objf,

Double *g, Nag_E04_Opt *options, Int32 comm, NagError *flag);

[D11Import (‘‘nagc’’)]

extern ‘‘C’’ void e04xxc (Nag_E04_Opt *options);

[D11Import (‘‘nagc’’)]

extern ‘‘C’’ void d01ajc (INTEGRAND_FUN_TYPE *f, Double a, Double b,

Double epsabs, Double epsrel, Int32 max_num_subint, Double* result, Double *abserr, Nag_QuadProgress *qp, NagError *flag);

[D11Import (‘‘nagc’’)]

extern ‘‘C’’ void f02aac (Int32 n, Double *a, Int32 tda, Double *r, NagError *eflag);

Epilogue 65

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D66– [64–74/11]

21.11.2003 3:29PM

[D11Import (‘‘nagc’’)]

extern ‘‘C’’ Double s15abc(Double x);

public _ _gc class NAG_FUNCTIONS {

public:

Double QUADRATURE_epsabs;

Double QUADRATURE_epsrel;

Int32 QUADRATURE_max_subint;

NAG_FUNCTIONS()

{ // the constructor: set default values QUADRATURE_epsabs¼0.0;

QUADRATURE_epsrel¼0.0001;

QUADRATURE_max_subint¼200;

}

void REAL_SYMM_EIGEN (Int32 n, Double *a, Int32 tda, Double *r, Int32 *flag) {

NagError eflag;

INIT_FAIL(eflag);

f02aac(n, a, tda, r, &eflag);

*flag¼(Int32)eflag.code;

}

Double CUM_NORM (Double x) { return s15abc(x);

}

void OPTIMIZE (Int32 n, Double *x, Double *g, Double *objf, Int32 *flag, OBJ_FUN_TYPE *the_fun) {

NagError eflag;

Nag_E04_Opt options;

INIT_FAIL (eflag);

e04xxc (&options);

options.print_level¼Nag_NoPrint;

options.list¼0;

options.verify_grad¼Nag_NoCheck;

e04dgc(n, the_fun, x, objf, g, &options, (Int32)0, &eflag);

*flag¼(Int32)eflag.code;

}

void QUADRATURE (Double a, Double b, Double *result, Double *abserr, Int32 *flag, INTEGRAND_FUN_TYPE *the_fun) {

Nag_QuadProgress qp;

NagError eflag;

INIT_FAIL(eflag);

d01ajc(the_fun, a, b, QUADRATURE_epsabs, QUADRATURE_epsrel, QUADRATURE_max_subint, result, abserr,

&qp, &eflag);

*flag¼(Int32)eflag.code;

} };

}

Code excerpt 7.2 The managed Cþþcode used to create the assemblynaglibwhich contains the namespace NAGLIB, and wraps the NAG C library functions in the class NAG_FUNCTIONS

It can be seen that the code is almost standard Cþþ and (in constrast to the equivalent COM approach) could easily be ported to UNIX platforms. We will now consider each non-standard Cþþ(that is Microsoft specific) feature in turn.

Importing Dynamic Link Library (DLL) functions

The NAG C library routines used are contained in a DLL called ‘nagc’. Here each function is imported into the Cþþproject by name; for instance:

[D11Import (‘‘nagc’’)]

extern ‘‘C’’ Double s15abc(Double x);

66 Using Numerical Software Components within Microsoft Windows

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D67– [64–74/11]

21.11.2003 3:29PM

is used to import the function s15abc, which computes the cumulative normal distribution.

Managed and unmanaged code

The directive gcindicates that the code is managed and memory is allocated on the garbage collected (GC) heap; unmanaged code is indicated by nogc.

The data types Double and Int32

In Code excerpt 7.2 the .NET data typesDoubleandInt32have been used so that the assembly can be accessed by both C# and VB.NET code.

All managed .NET code, written in VB.NET, C#, and Cþþ, is compiled to the same intermediate language (IL) code. In order to permit interoperability within .NET there is a common type system (CTS) which standardizes the basic data types across all languages.

A summary of the .NET data types corresponding to the Cþþtypesdoubleand longis given in the Table 7.1.

Delegates

In the case of numerical integration and optimization a user-defined function, orcall- back function, needs to be passed as a parameter to the NAG C library routine. This is achieved in .NET by declaring a delegate with the samesignature(that is return type and parameter types) as the callback function. For example

public _ _delegate double INTEGRAND_FUN_TYPE(Double x);

declares the delegate INTEGRAND_FUN_TYPE with a signature corresponding to functions that return aDoubleand have a singleDoubleparameter passed by value.

This delegate is used by the numerical integration routine d01ajcfor defining the integrand. It can be seen that the declaration of a delegate is similar to the declaration of a function prototype with the additional words public (or private) and delegate. Also the declaration and use of delegates in Code excerpt 7.2 has similarities with the declaration and use of function pointers in Code excerpt 7.1.

A more complicated delegate example is:

public _ _delegate void OBJ_FUN_TYPE (Int32 n, double *x,double * objf, double *g, Int32 comm);

Table 7.1 The correspondence between data types used by Cþþ, C#, VB.NET, and the .NET CTS

Cþþ C# VB.NET CTS Size in bytes

long int Integer Int32 4

double double Double Double 8

Epilogue 67

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D68– [64–74/11]

21.11.2003 3:29PM

which declares the delegateOBJ_FUN_TYPE, with a signature that applies to sub- routines (that is a functions which return void) with parameters of type Int32and double *. As can be seen, here it was found necessary to usedouble *instead of the more general Double *. This delegate is used by the numerical optimization routinee04dgcfor specifying the objective function to be minimized.

7.1.3 Accessing the assemblynaglibfrom C#

In this section we show how the previously described assembly naglib can be accessed from a C# console project created using Visual Studio .NET. The C# code is presented in Code excerpt 7.3 and a screen view of the project is shown in Figure 7.1.

It can be seen that the C# code defines the two classesDCLASSandRUNIT.

DCLASSis derived from the classNAG_FUNCTIONSand supplies the definitions for the callback functions used by the member functionsOPTIMIZEandQUADRATURE.

The classRUNITonly contains the member functionMain. This function is run by the example console application, and all the computations are performed by a single numeric object (calledtt) of typeDCLASS.

Figure 7.1 A view of the C# example project. The Object Browser displays the assemblynaglib, the namespace NAGLIB, the delegatesINTEGRAND_FUN_TYPE, andOBJ_FUN_TYPE, and also the member functions of the classNAG_FUNCTIONS: CUM_NORM,OPTIMIZE,QUADRATURE, andREAL_SYMM_EIGEN

68 Using Numerical Software Components within Microsoft Windows

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D69– [64–74/11]

21.11.2003 3:29PM

using System;

using System.Runtime.InteropServices;

using NAGLIB;

namespace USE_NAGLIBS {

class DCLASS : NAG_FUNCTIONS

{public unsafe void objfun (Int32 n, Double *x, Double *objf, Double *g, Int32 comm) {Double ex1, x1, x2;

ex1¼Math.Exp(x[0]);

x1¼x[0];

x2¼x[1];

*objf¼ex1*(4.0*x1*x1þ2.0*x2*x2þ4.0*x1*x2þ2.0*x2þ1.0);

g[0]¼4.0*ex1*(2.0*x1þx2)þ*objf;

g[1]¼2.0*ex1*(2.0*x2þ2.0*x1þ1.0);

}

public Double the_integrand_c(Double x) {

Double pi¼Math.PI;

Double val;

val¼(x*Math.Sin(x*30.0)/1.0x*x/(pi*pi*4.0));

return val;

}}

class RUNIT {

static unsafe void Main(string[] args) {

Int32 tda¼4, n¼4, n2¼2, j, flag¼0;

Double [] r¼new Double [30];

Double [] x2¼new Double [2];

Double [] g¼new Double [2];

Double a1, b1, objf¼0.0, abserr¼0.0, the_answer, x;

Double [,] a¼new Double[n,n];

DCLASS tt¼new DCLASS();

x¼ 1.0;

the_answer¼tt.CUM_NORM(x);

Console.WriteLine (‘‘The value of the cumulative normal¼{0,8:F4}’’, the_answer);

INTEGRAND_FUN_TYPE myfun_c¼new INTEGRAND_FUN_TYPE(tt.the_integrand_c);

OBJ_FUN_TYPE myobjfun¼new OBJ_FUN_TYPE (tt.objfun);

a1¼0.0;

b1¼Math.PI*2.0;

flag¼0;

the_answer¼0.0;

tt.QUADRATURE (a1, b1, ref the_answer, ref abserr, ref flag, myfun_c);

Console.WriteLine (‘‘The integral (default maximum number of subintervals)¼{0,8:F6}’’, the_answer);

flag¼0;

tt.QUADRATURE_max_subint¼3;

tt.QUADRATURE (a1, b1, ref the_answer, ref abserr, ref flag, myfun_c);

Console.WriteLine (‘‘The integral (maximum number of subintervals set to 3)¼{0,8:F6}’’, the_answer);

x2[0]¼ 1.0;

x2[1]¼1.0;

n2¼2;

flag¼0;

tt.OPTIMIZE (n2, ref x2[0], ref g[0], ref objf, ref flag, myobjfun);

Console.Write (‘‘The optimization solution vector is:’’);

for (j¼0; j < 2;þþj) {

Console.Write(‘‘{0,8:F4}’’, x2[j]);

}

Console.WriteLine();

Console.WriteLine (‘‘The value of the objective function is: {0,8:E4}’’,objf);

flag¼0;

//first row a[0,0]¼0.5;

a[0,1]¼0.0;

a[0,2]¼2.3;

a[0,3]¼ 2.6;

// second row a[1,0]¼0.0;

a[1,1]¼0.5;

Epilogue 69

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D70– [64–74/11]

21.11.2003 3:29PM

a[1,2]¼ 1.4;

a[1,3]¼ 0.7;

// third row a[2,0]¼2.3;

a[2,1]¼ 1.4;

a[2,2]¼0.5;

a[2,3]¼0.0;

//fourth row a[3,0]¼ 2.6;

a[3,1]¼ 0.7;

a[3,2]¼0.0;

a[3,3]¼0.5;

tt.REAL_SYMM_EIGEN(n, ref a[0,0], tda, ref r[0], ref flag);

Console.Write(‘‘The Eigenvalues are:’’);

for (j¼0; j <¼3;þþj){

Console.Write (‘‘{0,8:F4}’’, r[j]);

}

Console.WriteLine();

} } }

Code excerpt 7.3 Example C# code which uses the assemblynaglibin a C# console application

The value of the cumulative normal¼0.1587

The integral (default maximum number of subintervals)¼ 2.303835 The integral (maximum number of subintervals set to 3)¼ 3.168259 The optimization solution vector is : 0.50001.0000

The value of the objective function is: 2.7457E014 The Eigenvalues are:3.00001.0000 2.0000 4.0000

Code excerpt 7.4 The output from Code excerpt 7.3

We will now briefly discuss some of the important features of the code, for more information on C# see Robinsonet al. (2001).

General points

The assembly containing the namespaceNAGLIBis accessed with the statement using NAGLIB; which occurs on the third line of the C# code listing.

We useMath.PIto return the value of,Math.Sin(x)to compute sin(x), and Math.Exp(x) to evaluate exp(x). These functions are members of the class Math which is contained in the namespaceSystem.

The keyword unsafe

This directive is necessary because C# does not really support pointers. The key- word unsafeallows us to use pointers and thus easily pass scalars and arrays by reference to the managed Cþþ class NAG_FUNCTIONS contained in the name- space NAGLIB.

Declaring numeric objects and using simple member functions

The statementDCLASS tt = new DCLASS()creates a numeric objectttwith the type of the derived classDCLASS. SinceDCLASSwas derived fromNAG_FUNCTIONSit allows access not only to the public member functions objfun andthe_integrand_c, 70 Using Numerical Software Components within Microsoft Windows

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D71– [64–74/11]

21.11.2003 3:29PM

but also the public member functions ofNAG_FUNCTIONS:CUM_NORM, OPTIMIZE, QUADRATURE, andREAL_SYMM_EIGEN. This means that we can compute the cumu- lative normal distribution, and perform eigenvalue computations by using statements of the form:

the_answer¼tt.CUM_NORM(x);

flag¼0;

// first row a[0,0]¼0.5;

a[0,1]¼0.0;

// fourth row a[3,0]¼ 2.6;

a[3,1]¼ 0.7;

a[3,2]¼0.0;

a[3,3]¼0.5;

tt.REAL_SYMM_EIGEN(n, ref a[0,0], tda, ref r[0], ref flag);

We note that the keywordrefis used to pass the address ofa[0, 0], r[0],and flagto the member functionREAL_SYMM_EIGEN.

Using numeric objects with member functions requiring delegates

We will now consider how to call the numerical integration functionQUADRATURE.

This is achieved using the following C# statement:

INTEGRAND_FUN_TYPE myfun_c¼new INTEGRAND_FUN_TYPE (tt.the_integrand_c);

to declare (and also define) the delegatemyfun_c, of typeINTEGRAND_FUN_TYPE, which corresponds to the user-defined function the_ integrand_ccontained in the derived classDCLASS. The next step is to pass the appropriate parameters to the functiontt.QUADRATURE; for example:

a1¼0.0;

b1¼Math.PI*2.0;

flag¼0;

the_answer¼0.0;

tt.QUADRATURE(a1, b1, ref the_answer, ref abserr, ref flag, myfun_c);

The method of calling the numerical optimization member function is very similar.

For instance in the example code we use:

OBJ_FUN_TYPE myobjfun¼new OBJ_FUN_TYPE (tt.objfun);

x2[0]¼ 1.0;

x2[1]¼1.0;

n2¼2;

flag¼0;

tt.OPTIMIZE(n2, ref x2[0], ref g[0], ref objf, ref flag, myobjfun);

The initial parameter estimates and computed optimal values are contained in the arrayx2. The estimated gradient at the solution point is returned in the arraygand the parameterobjfcontains the value of the minimized objective function.

Epilogue 71

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D72– [64–74/11]

21.11.2003 3:29PM

7.1.4 Accessing the assemblynaglibfrom VB.NET

Here we illustrate how the assemblynaglibcan be used from VB.NET; for more details on VB.NET see Barwellet al. (2002).

The assembly naglib can be used from VB.NET in a similar manner to that described for C#. This is illustrated below in Code excerpt 7.5.

Imports System

Imports System.Runtime.InteropServices Imports NAGLIB

Module Modulel Public Class DCLASS

Inherits NAG_FUNCTIONS

Public Function the_integrand_c(ByVal x As Double) As Double Dim pi As Double

Dim val As Double pi¼Math.PI

val¼(x * Math.Sin(x * 30) / 1x * x / (pi * pi * 4)) Return val

End Function End Class Sub Main()

Dim x, the_answer As Double Dim flag, j, tda, n As Integer Dim a(,), r(), a1, b1, abserr As Double Dim tt As New DCLASS()

Dim myfun As New INTEGRAND_FUN_TYPE(AddressOf tt.the_integrand_c) a1¼0

b1¼Math.PI * 2 flag¼0

tt.QUADRATURE(a1, b1, the_answer, abserr, flag, myfun)

Console.WriteLine(‘‘The integral (default number of subintervals)¼{0,8:F4}’’, the_answer) tt.QUADRATURE_max_subint¼3

flag¼0

tt.QUADRATURE(a1, b1, the_answer, abserr, flag, myfun)

Console.WriteLine (‘‘The integral (number of subintervals set to 3)¼{0,8:F4}’’, the_answer) x¼ 1

the_answer¼tt.CUM_NORM(x)

Console.WriteLine(‘‘The value of the cumulative normal¼{0,8:F4}’’, the_answer) flag¼0

n¼4 tda¼n ReDim r(n1) ReDim a(n1, n1)

’first row a(0, 0)¼0.5 a(0, 1)¼0 a(0, 2)¼2.3 a(0, 3)¼ 2.6

’second row a(1, 0)¼0 a(1, 1)¼0.5 a(1, 2)¼ 1.4 a(1, 3)¼ 0.7

’third row a(2, 0)¼2.3 a(2, 1)¼ 1.4 a(2, 2)¼0.5 a(2, 3)¼0

’fourth row a(3, 0)¼ 2.6 a(3, 1)¼ 0.7 a(3, 2)¼0 a(3, 3)¼0.5

72 Using Numerical Software Components within Microsoft Windows

//SYS21///INTEGRAS/ELS/PAGINATION/ELSEVIER UK/CMF/3B2/FINALS_21-11-03/CH007.3D73– [64–74/11]

21.11.2003 3:29PM

tt.REAL_SYMM_EIGEN(n, a(0, 0), tda, r(0), flag) Console.Write(‘‘The Eigenvalues are:’’) For j¼0 To n1

Console.Write (‘‘{0,8:F4}’’, r(j)) Next j

Console.WriteLine() End Sub

End Module

Code excerpt 7.5 Example of using the numeric objects from VB.NET

The integral (default number of subintervals)¼ 2.3038 The integral (number of subintervals set to 3)¼ 3.1683 The value of the cumulative normal¼0.1587

The Eigenvalues are:3.00001.0000 2.0000 4.0000

Code excerpt 7.6 The output from Code excerpt 7.5

7.1.5 Conclusions

We have shown how to wrap C code in a managed Cþþassembly, which can then be used from within either a C# or VB.NET project.

A major benefit of this approach over COM is that the managed Cþþwrapper code can with little effort, be used on UNIX platforms. In addition, unlike COM, it is possible to create C# or VB.NET derived classes from the managed Cþþ (base) classes.

As more software supports .NET (for example Excel 2003 will) the future of OO numerics in .NET looks increasingly promising.

Dalam dokumen COMPUTATIONAL FINANCE - untag-smd.ac.id (Halaman 79-88)