JAVAからNAG数値計算ライブラリを利用する : Example 4

テクニカルレポート

4.4. Example 4

非線形最小化ルーチン、E04UCF

ここではユーザ提供の2つの評価関数を引数として取る NAG Fortran Library サブルーチン E04UCF をコールする方法を示す。この例ではCインタフェースコード上でどのようにしてJava配列を生成するかの手順についても説明する。この例は Imperial College, London の Sebastien Girard からの触発に負うところが大きい。

内容

  1. NAG Fortran Libraryマニュアルからのサブルーチン仕様
  2. Javaプログラム中でのネイティブ関数の宣言
  3. Javaプログラムのコンパイル
  4. C用のヘッダファイルの生成
  5. ネイティブ関数のCによる実装
  6. 共用ライブラリ/DLLの作成
  7. プログラムの実行
  8. クイックサマリ

  1. NAG Fortran Libraryマニュアルからのサブルーチン仕様
  2. 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 ルーチン仕様を参照されたい。

  3. Javaプログラム中でのネイティブ関数の宣言
  4. 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に引渡すことはできないので、ここでは文字列引数 confunobjfun を介してメソッドの名前のみを引渡すことにする。

  5. Javaプログラムのコンパイル
  6. 次に示すのはJavaプログラム Minimization.java のソースコードである。
    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]);
          }
      }
     
    } 
    
    このプログラムに関する注意事項:

    • E04UCF のルーチン OBJFUN, CONFUN に対応する形で2つのJavaメソッドが用意されている。ここでは NAG Fortran Library マニュアル中の E04UCF の用例プログラムで規定される最小化問題を解くことにする。

    • Fortran Library ルーチン E04UEF に対する JNI インタフェースとしては、E04UCF に対するオプショナルな設定も伝えられるようなものを宣言する。Javaプログラムにはこのルーチンに対するいくつかのコールが含まれている。

    Javaプログラムをコンパイルするには次のコマンドを使用すれば良い。

      % javac Minimization.java 
    

  7. C用のヘッダファイルの生成
  8. Minimization.java のコンパイルが終わると、javah を使ってCヘッダファイルを作成することができる。

      % 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); 
    

  9. ネイティブ関数のCによる実装
  10. ヘッダファイル Minimization.h作成できたところで、今度は Java_Minimization_e04ucf のCコードによる実装に移る。

    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メソッドがスカラ引数の内容をアップデートできず、配列の内容であればアップデートが可能という事情による。

  11. 共用ライブラリ/DLLの作成
  12. このステップは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
      

    関連するコンパイラフラグについては Example 1 セクション7 を参照されたい。Windows環境下で作成する場合、Cコンパイラスイッチ -DWINDOWS が追加されている点にも注意。これはCコードに対し、Fortran Library ルーチン名 E04UCF, E04UEF が大文字で与えられなくてはならない(Windows版 NAG Fortran Library の要請でもある)ことを伝える意味を持つ。UNIX系マシンの場合、Cからのコールに際しての名称は通常小文字でアンダスコアが付加された形となる(例:"e04ucf_")。

  13. プログラムの実行
  14. これまでの操作がすべて正常に行われたとすると、次のコマンドによってプログラムを実行できる。

      % 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 を含むディレクトリをポイントするように設定する必要がある。

  15. クイックサマリ
  16. 2つのソースファイル Minimization.javaMinimizationImp.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 のコピーである。

    • Javaプログラムの実行:
        % java Minimization 
      

Copyright 2009 Numerical Algorithms Group
Page last updated 2009-04-21 14:12:29 mick
[NP3671]


関連情報
Privacy Policy  /  Trademarks