4.4. Example 4
非線形最小化ルーチン、E04UCF |
内容
- nAG Fortran Libraryマニュアルからのサブルーチン仕様
- Javaプログラム中でのネイティブ関数の宣言
- Javaプログラムのコンパイル
- C用のヘッダファイルの生成
- ネイティブ関数のCによる実装
- 共用ライブラリ/DLLの作成
- プログラムの実行
- クイックサマリ
- nAG Fortran Libraryマニュアルからのサブルーチン仕様
- Javaプログラム中でのネイティブ関数の宣言
- Javaプログラムのコンパイル 次に示すのはJavaプログラム Minimization.java のソースコードである。
- E04UCF のルーチン OBJFUN, CONFUN に対応する形で2つのJavaメソッドが用意されている。ここでは nAG Fortran Library マニュアル中の E04UCF の用例プログラムで規定される最小化問題を解くことにする。
- Fortran Library ルーチン E04UEF に対する JNI インタフェースとしては、E04UCF に対するオプショナルな設定も伝えられるようなものを宣言する。Javaプログラムにはこのルーチンに対するいくつかのコールが含まれている。
- C用のヘッダファイルの生成 Minimization.java のコンパイルが終わると、javah を使ってCヘッダファイルを作成することができる。
- ネイティブ関数のCによる実装 ヘッダファイル Minimization.h が作成できたところで、今度は Java_Minimization_e04ucf のCコードによる実装に移る。
- 共用ライブラリ/DLLの作成 このステップはOS依存となる。
- Linux上での作成
% gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \ MinimizationImp.c % ld -G -z defs MinimizationImp.o -o libnagCJavaInterface.so \ /opt/nAG/fll6a22df/lib/libnag_nag.so -lm -lc -lpthread -lgfortran
本用例においては nAG Fortran Library の gfortran 版にリンクしているため、-lgfortran を使った gfortran 実行時ライブラリへのリンクが必要となる点に注意。
他のUNIX系マシン上ではさらなるライブラリの追加がリンク時に必要となる場合がある(Example 1 の注意参照)。
- Windows上でのVisual C++による作成
C:\> cl -DWINDOWS -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32 /Gz -LD MinimizationImp.c "c:\Program Files\nAG\fldll224zl\lib\FLDLL224Z_nag.lib" -FenagCJavaInterface.dll
- プログラムの実行 これまでの操作がすべて正常に行われたとすると、次のコマンドによってプログラムを実行できる。
- クイックサマリ 2つのソースファイル Minimization.java と MinimizationImp.c が与えられたとして、次のコマンドを発行する。
- Javaクラスのコンパイル:
% javac Minimization.java
- ヘッダファイルの作成:
% javah -jni Minimization
- インタフェースライブラリのコンパイル:
- (Linux)
% gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \ MinimizationImp.c % ld -G -z defs MinimizationImp.o -o libnagCJavaInterface.so \ /opt/nAG/fll6a22df/lib/libnag_nag.so -lm -lc -lpthread
ただしディレクトリ名称 /opt/jdk1.6.0_11/include, /opt/jdk1.6.0_11/include/linux, /opt/nAG/fll6a22df/lib はJavaと nAG Fortran Library のインストレーションに合せて適宜調整を要す。 - (Windows/Visual C++)
C:\> cl -DWINDOWS -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32 /Gz -LD MinimizationImp.c "c:\Program Files\nAG\fldll224zl\lib\FLDLL224Z_nag.lib" -FenagCJavaInterface.dll
ただしディレクトリ名称 c:\jdk1.6.0_11\include, c:\jdk1.6.0_11\include\win32, c:\fldll224z はJavaと nAG Fortran Library のインストレーションに合せて適宜調整を要す。また FLDLL224Z_nag.lib は nAG Fortran Library のコピーである。
- (Linux)
- Javaプログラムの実行:
% java Minimization
Fortran Library マニュアルによればサブルーチン E04UCF に対するプロトタイプは次のとおりである。
SUBROUTINE E04UCF(N, NCLIN, NCNLN, LDA, LDCJ, LDR, A, BL, BU, CONFUN, 1 OBJFUN, ITER, ISTATE, C, CJAC, CLAMDA, OBJF, OBJGRD, 2 R, X, IWORK, LIWORK, WORK, LWORK, IUSER, USER, IFAIL) INTEGER N, NCLIN, NCNLN, LDA, LDCJ, LDR, ITER, 1 ISTATE(N+NCLIN+NCNLN), IWORK(LIWORK), LIWORK, LWORK, 2 IUSER(*), IFAIL DOUBLE PRECISION A(LDA,*), BL(N+NCLIN+NCNLN), 1 BU(N+NCLIN+NCNLN), C(*), CJAC(LDCJ,*), 2 CLAMDA(N+NCLIN+NCNLN), OBJF, OBJGRD(N), R(LDR,N), 3 X(N), WORK(LWORK), USER(*) EXTERNAL CONFUN, OBJFUNサブルーチン E04UCF は任意の滑らかな関数 f(x) を制約のもとで最小化する機能を提供する。その場合の制約には変数に対する単純な境界の他に、線形の制約条件、滑らかな非線形の制約条件が含まれる。
最小化の対象である関数 f(x) は E04UCF に対するユーザ提供ルーチン引数 OBJFUN によってその値が評価される。この OBJFUN に対する宣言は次のとおり。
SUBROUTINE OBJFUN(MODE, N, X, OBJF, OBJGRD, NSTATE, IUSER, USER) INTEGER MODE, N, NSTATE, IUSER(*) DOUBLE PRECISION X(N), OBJF, OBJGRD(N), USER(*)ルーチン OBJFUN は目的関数の勾配についても評価できるものでなくてはならない。さらに最小化問題に対する非線形の制約条件を評価するためにはユーザ提供ルーチン引数 CONFUN が必要となる。
SUBROUTINE CONFUN(MODE, NCNLN, N, LDCJ, NEEDC, X, C, CJAC, NSTATE, 1 IUSER, USER) INTEGER MODE, NCNLN, N, LDCJ, NEEDC(NCNLN), NSTATE, 1 IUSER(*) DOUBLE PRECISION X(N), C(NCNLN), CJAC(LDCJ,N), USER(*)
すべてのルーチン引数に関する詳細については nAG Fortran Library マニュアル中の E04UCF ルーチン仕様を参照されたい。
nAG Fortran Library はエラーコードを返すのに単純な整数 IFAIL を用いる(C Library の場合には NagError 構造体が用いられる)。
nAG Fortran Library からのルーチンをコールするわけであるが、インタフェースコードは依然Cで実装する。従ってプログラム全体としてはJava, C, Fortranという3種類の言語が混在した形になる。
// Declaration of the Native (C) function private native int e04ucf(int n, int nclin, int ncnln, double[] a, int lda, double[] bl, double[] bu, String confun, String objfun, double[] objf, double[] objgrd, double[] x);これは int を応答として返すメソッドを意味する。なお、許されるすべての引数を受渡しするわけではない点に注意。例えば配列 cjac, clamda, istate は含まれていない。もしそれらが含んでいる情報をJavaに返したいのであればそれらの引数を追加すれば良いのであるが、ここでは含めずにおく。また ifail 引数も使用しないことから、エラーコードの通知には int 戻り値を用いる。
Example 3 の場合と同様、サブルーチンの引数を直接JavaからCに引渡すことはできないので、ここでは文字列引数 confun と objfun を介してメソッドの名前のみを引渡すことにする。
public class Minimization { // Declaration of C native function, nAG routine e04ucf private native int e04ucf(int n, int nclin, int ncnln, double[] a, int lda, double[] bl, double[] bu, String confun, String objfun, double[] objf, double[] objgrd, double[] x); // An interface to e04uef, an option setting routine for e04ucf private native void e04uef(String option); static { System.loadLibrary("nagCJavaInterface"); } /* A routine to evaluate the nonlinear constraint functions and Jacobian. This gets called from nAG routine e04ucf via the Java Native Interface. N.B. cjac is stored as a 1D array rather than 2D array for convenience. */ private void confun(int mode, int ncnln, int n, int ldcj, int[] needc, double[] x, double[] c, double[] cjac, int nstate) { 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 (int j=0; j<n; j++) { for (int i=0; i<ncnln; i++) { // Notice how we address the array cjac so that contents // are in the order required by a 2D Fortran array. cjac[i+j*ldcj] = 0.0; } } } 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*ldcj] = 2.0e0*x[0]; cjac[0+1*ldcj] = 2.0e0*x[1]; cjac[0+2*ldcj] = 2.0e0*x[2]; cjac[0+3*ldcj] = 2.0e0*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*ldcj] = x[1]*x[2]*x[3]; cjac[1+1*ldcj] = x[0]*x[2]*x[3]; cjac[1+2*ldcj] = x[0]*x[1]*x[3]; cjac[1+3*ldcj] = x[0]*x[1]*x[2]; } } } /* A routine to evaluate the objective function and its gradient. This gets called from nAG routine e04ucf via the Java Native Interface */ private double objfun(int mode, int n, double[] x, double[] objgrd, int nstate) { double objf = 0.0; 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]*(2.0e0*x[0]+x[1]+x[2]); objgrd[1] = x[0]*x[3]; objgrd[2] = x[0]*x[3] + 1.0e0; objgrd[3] = x[0]*(x[0]+x[1]+x[2]); } return objf; } // Main program public static void main(String args[]) { Minimization nlp = new Minimization(); // Pass the names of the constraint function and the objective // function evaluation routines. nlp.Solve("confun", "objfun"); } private void Solve(String confunction, String objfunction) { // n -- the number of variables (excluding slacks) int n = 4; // nclin -- the number of linear constraints int nclin = 1; // ncnln -- the number of nonlinear constraints int ncnln = 2; // a[lda*n] -- array of linear constraints, where // lda = max(1, nclin). Although the nAG routine e04ucf // has A as a two dimensional matrix, for ease of access via the // Java Native Interface (JNI) it is much easier to store it as // a one dimensional array in Java. We still require the // value lda which Fortran will be told is the leading dimension // of its 2D array. int lda = java.lang.Math.max(1,nclin); double[] a; a = new double[lda*n]; // a[i+j*lda] references array element a[i,j] in Fortran order. a[0+0*lda] = 1.0; a[0+1*lda] = 1.0; a[0+2*lda] = 1.0; a[0+3*lda] = 1.0; // bl[n+nclin+ncnln] -- lower bounds for all the variables and general constraints double[] bl = {1.0, 1.0, 1.0, 1.0, -1.0e+25, -1.0e+25, 25.0}; // bu[n+nclin+ncnln] -- upper bounds for all the variables and general constraints double[] bu = {5.0, 5.0, 5.0, 5.0, 20.0, 40.0, 1.0e+25}; // x[n] -- initial estimate of the solution double[] x = {1.0, 5.0, 5.0, 1.0}; // objf[1] -- an array of length 1 to hold the final objective // function value computed by e04ucf double[] objf = new double[1]; // objgrd[n] -- an array to hold the gradient of the objectve function, // computed by e04ucf double[] objgrd = new double[n]; // ifail -- output error variable. int ifail; int i; // Set some options for e04ucf e04uef("Nolist"); // Turn off echoing of options by e04uef e04uef("Print Level = 0"); // Turn off e04ucf internal monitoring information System.out.println(" Running e04ucf example program from Java"); System.out.println(" ----------------------------------------"); System.out.println(" Problem:"); System.out.println(""); System.out.println(" Minimize F(x) = x0*x3*(x0 + x1 + x2) + x2"); System.out.println(" Subject to bounds"); for (i = 0; i < n; i++) System.out.println(" " + bl[i] + " <= x" + i + " <= " + bu[i]); System.out.println(" General linear constraint"); System.out.println(" x0 + x1 + x2 + x3 <= 20"); System.out.println(" Nonlinear constraints"); System.out.println(" x0^2 + x1^2 + x2^2 + x3^2 <= 40"); System.out.println(" x0*x1*x2*x3 >= 25"); // Call the nAG Library routine e04ucf via the Java Native Interface ifail = e04ucf(n, nclin, ncnln, a, lda, bl, bu, confunction, objfunction, objf, objgrd, x); // Output some results System.out.println(""); System.out.println(" Results returned by nAG nonlinear minimization routine e04ucf"); System.out.println(" -------------------------------------------------------------"); System.out.println(" Fail code ifail = " + ifail); if (ifail == 0) { System.out.println(" Final objective function value = " + objf[0]); System.out.println(" Solution vector x:"); for (i = 0; i < n; i++) System.out.println(" x[" + i + "] = " + x[i]); } } }このプログラムに関する注意事項:
Javaプログラムをコンパイルするには次のコマンドを使用すれば良い。
% javac Minimization.java
% javah -jni Minimization生成されたヘッダファイル Minimization.h には2つの JNI 関数に対応する次の2つの関数プロトタイプが含まれている。
JNIEXPORT jint JNICALL Java_Minimization_e04ucf (JNIEnv *, jobject, jint, jint, jint, jdoubleArray, jint, jdoubleArray, jdoubleArray, jstring, jstring, jdoubleArray, jdoubleArray, jdoubleArray); JNIEXPORT void JNICALL Java_Minimization_e04uef (JNIEnv *, jobject, jstring);
5.1 Cインタフェースライブラリ用ソースコード
次はファイル MinimizationImp.c の内容を記したものである。#include "Minimization.h" #include <math.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <jni.h> /* Nasty global variables to store pointers to Java environment and methods so that we can use them in different parts of this C code. */ JNIEnv *globalJavaEnv; jobject globaljavaobject; jmethodID globalConfunID; jmethodID globalObjfunID; /* This routine has the interface required by nAG routine e04ucf for argument confun. It makes calls back to the Java version of confun */ void confunFun(int *mode, int *ncnln, int *n, int *ldcj, int needc[], double x[], double c[], double cjac[], int *nstate, int iuser[], double user[]) { int i; int *jneedcpt; double *jxpt, *jcpt, *jcjacpt; /* First create some Java arrays to pass to confun */ jintArray jneedc = (*globalJavaEnv)->NewIntArray(globalJavaEnv, *ncnln); jdoubleArray jx = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, *n); jdoubleArray jc = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, *ncnln); jdoubleArray jcjac = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, ((*ldcj)*(*n))); /* Copy input arguments to Java arrays needc and x */ jneedcpt = (*globalJavaEnv)->GetIntArrayElements(globalJavaEnv, jneedc, 0); jxpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jx, 0); for (i = 0; i < *ncnln; i++) jneedcpt[i] = needc[i]; for (i = 0; i < *n; i++) jxpt[i] = x[i]; /* Release array elements back to Java (this puts the values back into Java arrays jneedc and jx) */ (*globalJavaEnv)->ReleaseIntArrayElements(globalJavaEnv, jneedc, jneedcpt, 0); (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jx, jxpt, 0); /* Call the Java method via its method ID */ (*globalJavaEnv)->CallVoidMethod(globalJavaEnv, globaljavaobject, globalConfunID, (jint)(*mode), (jint) (*ncnln), (jint)(*n), (jint)(*ldcj), jneedc, jx, jc, jcjac, (jint)(*nstate)); /* Copy results from Java arrays back to C arrays */ jcpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jc, 0); jcjacpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jcjac, 0); for (i = 0; i < *ncnln; i++) c[i] = jcpt[i]; for (i = 0; i < (*ldcj)*(*n); i++) cjac[i] = jcjacpt[i]; /* Release array elements back to Java to free memory */ (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jc, jcpt, 0); (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jcjac, jcjacpt, 0); } /* This routine has the interface required by nAG routine e04ucf for argument objfun. It makes calls back to the Java version of objfun */ void objfunFun(int *mode, int *n, double x[], double *objf, double objgrd[], int *nstate, int iuser[], double user[]) { int i; double *jobjgrdpt; /* First create some Java arrays to pass to objfun */ jdoubleArray jx = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, *n); jdoubleArray jobjgrd = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, *n); /* Copy input array x to Java array jx */ double *jxpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jx, 0); for (i = 0; i < *n; i++) jxpt[i] = x[i]; /* Release array elements back to Java (this puts the values into jx) */ (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jx, jxpt, 0); /* Call Java objfun which fills in array objgrd and returns objf */ *objf = (*globalJavaEnv)->CallDoubleMethod(globalJavaEnv, globaljavaobject, globalObjfunID, (jint) (*mode), (jint) (*n), jx, jobjgrd, (jint) (*nstate)); /* Get results back from Java to C array objgrd */ jobjgrdpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jobjgrd, 0); for (i = 0; i < *n; i++) objgrd[i] = jobjgrdpt[i]; /* Release array elements back to Java to free memory */ (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jobjgrd, jobjgrdpt, 0); } JNIEXPORT jint JNICALL Java_Minimization_e04ucf ( JNIEnv *env, jobject object, jint n, jint nclin, jint ncnln, jdoubleArray a, jint lda, jdoubleArray bl, jdoubleArray bu, jstring confun, jstring objfun, jdoubleArray objf, jdoubleArray objgrd, jdoubleArray x ) { /* Local variables and arrays */ int ldcj; int ldr; int iter; int *istate; double *c; double *cjac; double *clamda; double *r; int liwork; int *iwork; int lwork; double *work; int ifail; /* N.B. we choose not to use iuser and user arrays in our evaluation functions, so these are empty arrays. */ int iuser[1]; double user[1]; jclass cls; const char *confunction; const char *objfunction; jdouble *a_pt, *bl_pt, *bu_pt, *x_pt, *objf_pt, *objgrd_pt; /* Array leading dimension information required by the Fortran routine */ if (ncnln > 0) ldcj = ncnln; else ldcj = 1; ldr = n; /* Compute the amount of workspace we need to supply to e04ucf */ liwork = 3 * n + nclin + 2 * ncnln; if (ncnln == 0 && nclin == 0) lwork = 20 * n; else if (ncnln == 0 && nclin > 0) lwork = 2 * n*n + 20 * n + 11 * nclin; else lwork = 2 * n*n + n*nclin + 2*n*ncnln + 20*n + 11*nclin + 21*ncnln; /* Allocate arrays of appropriate size. */ /* Note that we store cjac as a one dimensional array rather than a 2D array as in Fortran, for convenience in communication with Java. */ istate = (int *)malloc((n+nclin+ncnln)*sizeof(int)); c = (double *)malloc((ncnln)*sizeof(double)); cjac = (double *)malloc((ldcj*n)*sizeof(double)); clamda = (double *)malloc((n+nclin+ncnln)*sizeof(double)); r = (double *)malloc((ldr*n)*sizeof(double)); iwork = (int *)malloc((liwork)*sizeof(int)); work = (double *)malloc((lwork)*sizeof(double)); /* Copy the Java env pointers to global space so that confunFun and objfunFun can access them. */ globalJavaEnv = env; globaljavaobject = object; /* Get hold of the name of the user's Java evaluation functions. */ confunction = (*env)->GetStringUTFChars(env, confun, 0); objfunction = (*env)->GetStringUTFChars(env, objfun, 0); /* Now we have the Java evaluation function names we can use them to get hold of handles (method IDs) to the functions. Once more, the method IDs are stored globally so that confunFun and objfunFun can use them. Note that the Java function signatures must be correct. You can find out the signatures after compiling the Java program Minimization.java by using the command % javap -private -s Minimization */ cls = (*env)->GetObjectClass(env, object); globalConfunID = (*env)->GetMethodID(env, cls, confunction, "(IIII[I[D[D[DI)V"); globalObjfunID = (*env)->GetMethodID(env, cls, objfunction, "(II[D[DI)D"); /* Free up the Java string argument so we don't leak memory. */ (*env)->ReleaseStringUTFChars(env, confun, confunction); (*env)->ReleaseStringUTFChars(env, objfun, objfunction); if (globalConfunID == 0) { printf("Cannot find confun method \"%s\" with signature \"(IIII[I[D[D[DI)V\"\n", confunction); return -1; } if (globalObjfunID == 0) { printf("Cannot find objfun method \"%s\" with signature \"(II[D[DI)D\"\n", objfunction); return -1; } /* Extract the arrays from Java */ a_pt = (*env)->GetDoubleArrayElements(env, a, 0); bl_pt = (*env)->GetDoubleArrayElements(env, bl, 0); bu_pt = (*env)->GetDoubleArrayElements(env, bu, 0); objf_pt = (*env)->GetDoubleArrayElements(env, objf, 0); objgrd_pt = (*env)->GetDoubleArrayElements(env, objgrd, 0); x_pt = (*env)->GetDoubleArrayElements(env, x, 0); /* Call to main nAG Library routine e04ucf */ ifail = -1; #ifdef WINDOWS E04UCF #else e04ucf_ #endif (&n, &nclin, &ncnln, &lda, &ldcj, &ldr, a_pt, bl_pt, bu_pt, confunFun, objfunFun, &iter, istate, c, cjac, clamda, objf_pt, objgrd_pt, r, x_pt, iwork, &liwork, work, &lwork, iuser, user, &ifail); /* Release the array elements back to Java and free memory. */ (*env)->ReleaseDoubleArrayElements(env, a, a_pt, 0); (*env)->ReleaseDoubleArrayElements(env, bl, bl_pt, 0); (*env)->ReleaseDoubleArrayElements(env, bu, bu_pt, 0); (*env)->ReleaseDoubleArrayElements(env, objf, objf_pt, 0); (*env)->ReleaseDoubleArrayElements(env, objgrd, objgrd_pt, 0); (*env)->ReleaseDoubleArrayElements(env, x, x_pt, 0); return ifail; } // Interface to option setting routine e04uef JNIEXPORT void JNICALL Java_Minimization_e04uef (JNIEnv *env, jobject object, jstring option) { const char *coption; /* Get hold of the Java option string. */ coption = (*env)->GetStringUTFChars(env, option, 0); /* Call the option setting routine */ #ifdef WINDOWS E04UEF(coption, strlen(coption)); #else e04uef_(coption, strlen(coption)); #endif /* Free up the Java string argument so we don't leak memory. */ (*env)->ReleaseStringUTFChars(env, option, coption); }
5.2 Cコードの解説
Java_Minimization_e04ucf という名前の関数はJavaで宣言されたメソッド e04ucf をCで実装したものである。
それぞれ目的関数と非線形の制約を評価するためのJavaメソッド objfun, confun を直接 nAG Fortran Library ルーチン E04UCF に引渡すことはできないので、それらをC関数中にラップ(wrap)する必要がある。それらがそれぞれ objfunFun, confunFun という名前のC関数である。
void objfunFun(int *mode, int *n, double x[], double *objf, double objgrd[], int *nstate, int iuser[], double user[]); void confunFun(int *mode, int *ncnln, int *n, int *ldcj, int needc[], double x[], double c[], double cjac[], int *nstate, int iuser[], double user[]);これらの関数は nAG Library ルーチン E04UCF によって必要とされる引数の型と応答の型を持つ。mode のようなすべてのスカラ引数はFortranで要求されるようにポインタとして引渡される点に注意。また引数 cjac はFortranルーチンによって指定される2次元配列ではなく、1次元配列として宣言されている点にも注意を要する。これは2次元のJava配列がFortranの2次元配列に簡単にマップできないという事情による。
objfunFun, confunFun 内でやることと言えば、対応するJavaメソッドをコールするだけである。ここでもやはりトリックはJavaに対するコール方法を知ることができるという点にある。objfun に対しては JNI 関数 CallDoubleMethod を(Java中でこのメソッドは倍精度の戻り値を持つと定義してあるため)、confun に対しては CallVoidMethod を用いてこれを行う。
Javaメソッド objfun と confun に対しては共に配列引数を引渡す必要がある。これらのメソッドはこの配列要素に対して情報をセットする。Example 3 の場合と同様、これら2メソッドのメソッドIDを求めた後、それらをCコード中のグローバル変数内に格納する。それによってそれらの情報はCで書かれた評価関数内、及びメインの JNI 関数からもアクセスできるようになる。これらのIDは JNI 関数 GetMethodID へのコールに際し、適切な名前と署名(signatures)を引渡すことによって得ることができる。なお、Javaメソッドの署名を求める良い方法はJavaプログラム Minimization.java をコンパイルした後、コマンド
% javap -private -s Minimizationを用いる方法である。
問題はC関数 objfunFun, confunFun がJavaメソッドに対しJava配列を引渡す必要があるにもかかわらず、C(またはFortran)スタイルの配列しか持ち合わせていないという点にある。従ってCコードはJava配列を生成した上で、C配列の内容をJava配列中にコピーする必要がある。Javaの倍精度配列を生成するためには、JNI 関数 NewDoubleArray を使用した上で GetDoubleArrayElements をコールし配列要素へのCポインタを求め、さらにJavaメソッドをコールする前に ReleaseDoubleArrayElements を用いてCの内容をJava配列中にコピーする。Javaメソッドから戻ったら再度 GetDoubleArrayElements をコールし結果を得る。整数配列の場合、JNI 関数 NewIntArray, GetIntArrayElements, ReleaseIntArrayElements が適切である。ただしデータを正しい位置に正しいタイミングで得るためには、これらの JNI 関数を正しい順序でコールすることが肝要である。MinimizationImp.c 中の用例を参照されたい。
5.3 結果のJavaへの引渡し
この例においては、すべての結果はネイティブメソッドへのJavaコールから得られる配列引数を介してJavaに返される。ただしエラーコード IFAIL は例外で、それは関数名を介して返される。なお、最適な関数値を含む引数 objf を長さ1の配列として宣言している点に注意。これはJavaメソッドがスカラ引数の内容をアップデートできず、配列の内容であればアップデートが可能という事情による。
関連するコンパイラフラグについては Example 1 セクション7 を参照されたい。Windows環境下で作成する場合、Cコンパイラスイッチ -DWINDOWS が追加されている点にも注意。これはCコードに対し、Fortran Library ルーチン名 E04UCF, E04UEF が大文字で与えられなくてはならない(Windows版 nAG Fortran Library の要請でもある)ことを伝える意味を持つ。UNIX系マシンの場合、Cからのコールに際しての名称は通常小文字でアンダスコアが付加された形となる(例:"e04ucf_")。
% java Minimization期待される出力は次のとおり。
Running e04ucf example program from Java ---------------------------------------- Problem: Minimize F(x) = x0*x3*(x0 + x1 + x2) + x2 Subject to bounds 1.0 <= x0 <= 5.0 1.0 <= x1 <= 5.0 1.0 <= x2 <= 5.0 1.0 <= x3 <= 5.0 General linear constraint x0 + x1 + x2 + x3 <= 20 Nonlinear constraints x0^2 + x1^2 + x2^2 + x3^2 <= 40 x0*x1*x2*x3 >= 25 Results returned by nAG nonlinear minimization routine e04ucf ------------------------------------------------------------- Fail code ifail = 0 Final objective function value = 17.014017289134703 Solution vector x: x[0] = 1.0 x[1] = 4.742999642848296 x[2] = 3.821149976895378 x[3] = 1.379408294178579
(ライブラリが見つけられないという趣旨のエラーメッセージが発行された場合には、Example 1 の ヒント を参照)この例の場合、nAG C Library にではなく nAG Fortran Library に対してリンクしているため、関連する環境変数が nAG Fortran Library を含むディレクトリをポイントするように設定する必要がある。
Copyright 2009 Numerical Algorithms Group
Page last updated 2009-04-21 14:12:29 mick
[NP3671]