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

テクニカルレポート

4.6. Example 6

大域的最適化ルーチン、E05JBF

ここではユーザ提供の2つのルーチンを引数として取るもう一つの nAG Fortran Library サブルーチン E05JBF をコールする方法を示す。

内容

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

  1. nAG Fortran Library マニュアルからのサブルーチン仕様
  2. Fortran Library マニュアルによればサブルーチン E05JBF に対するプロトタイプは次のとおりである。

          SUBROUTINE E05JBF(N, OBJFUN, IBOUND, IINIT, BL, BU, SDLIST,
                            LIST, NUMPTS, INITPT, MONIT, X, OBJ, COMM, LCOMM,
                            IUSER, RUSER, IFAIL
                            INTEGER N, IBOUND, IINIT, SDLIST, NUMPTS(N),
                            INITPT(N), LCOMM, IUSER(*), IFAIL
                            DOUBLE PRECISION BL(N), BU(N), LIST(N,SDLIST),
                            X(N), OBJ, COMM(LCOMM), RUSER(*)
                            EXTERNAL OBJFUN, MONIT 
    
    サブルーチン E05JBF は単純な境界制約条件のみを持つ比較的小規模の大域的最適化問題に対する解法機能を提供する。それは変数に対する一群の境界型制約条件のもとで非線形関数の大域的最適値を見出す。

    最適化の対象である関数 f(x) は E05JBF に対するユーザ提供ルーチン引数 OBJFUN によってその値が評価される。この OBJFUN に対する宣言は次のとおり。

          SUBROUTINE OBJFUN(N, X, F, NSTATE, IUSER, RUSER, INFORM)
                            INTEGER N, NSTATE, IUSER(*), INFORM
                            DOUBLE PRECISION X(N), F, RUSER(*) 
    
    それに加えて最小化プロセスの監視用にユーザ提供ルーチン引数 MONIT が必要となる。
          SUBROUTINE MONIT(N, NCALL, XBEST, ICOUNT, NINIT, LIST, NUMPTS,
                            INITPT, NBASKT, XBASKT, BOXL, BOXU, NSTATE, IUSER,
                            RUSER, INFORM)
                            INTEGER N, NCALL, ICOUNT(6), NINIT, NUMPTS(N),
                            INITPT(N), NBASKT, NSTATE, IUSER(*), INFORM
                            DOUBLE PRECISION XBEST(N), LIST(N,NINIT),
                            XBASKT(N,NBASKT), BOXL(N), BOXU(N), RUSER(*) 
    

    すべてのルーチン引数に関する詳細については nAG Fortran Library マニュアル中の E05JBF ルーチン仕様を参照されたい。

  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 e05jbf(int n, String objfun, int ibound, int iinit,
                                double[] bl, double[] bu, int sdlist, double[] list,
                                int[] numpts, int[] initpt, String monit,
                                double[] x, double[] obj, double[] comm, int lcomm,
                                int[] iuser, double[] ruser); 
    

    これは int を応答として返すメソッドを意味する。また ifail 引数を使用しないことから、エラーコードの通知には int 戻り値を用いる。

    Example 3 の場合と同様、サブルーチンの引数を直接JavaからCに引渡すことはできないので、ここでは文字列引数 objfun monit を介してメソッドの名前のみを引渡すことにする。

  5. Javaプログラムのコンパイル
  6. 次に示すのはJavaプログラム GlobalOptimization.java のソースコードである。
    
    public class GlobalOptimization
    {
      // Declaration of C native function, nAG routine e05jbf
      private native int e05jbf(int n, String objfun, int ibound, int iinit,
                                double[] bl, double[] bu, int sdlist, double[] list,
                                int[] numpts, int[] initpt, String monit, 
                                double[] x, double[] obj, double[] comm, int lcomm,
                                int[] iuser, double[] ruser);
    
      // An interface to e05jaf, an initialisation routine for e05jbf
      private native int e05jaf(int n, double[] comm, int lcomm);
    
      static
      {
        System.loadLibrary("nagCJavaInterface");
      }
    
      /* A routine to evaluate the objective function F(x).
         This gets called from nAG routine e05jbf via the Java Native
         Interface */
      private double objfun(int n, double[] x, double f, int nstate,
                            int[] iuser, double[] ruser, int inform)
      {
        double x1, x2;
        
        if (nstate==1)
          {
            /* This is the first call to objfun */
            System.out.println(" (objfun was just called for the first time)");
            System.out.println();
          }
        x1 = x[0];
        x2 = x[1];
        f = 3.0*java.lang.Math.pow((1.0-x1),2.0)*
            java.lang.Math.exp(-java.lang.Math.pow(x1,2.0)-java.lang.Math.pow((x2+1),2.0)) -
            10.0*(x1/5.0-java.lang.Math.pow(x1,3.0)-java.lang.Math.pow(x2,5.0))*
            java.lang.Math.exp(-java.lang.Math.pow(x1,2.0)-java.lang.Math.pow(x2,2.0)) -
            1.0/3.0*java.lang.Math.exp(-java.lang.Math.pow((x1+1.0),2.0)-
            java.lang.Math.pow(x2,2.0));
      
      return f;
      }
    
      /* A routine to monitor the minimization process.
         This gets called from nAG routine e05jbf via the
         Java Native Interface. */
      private int monit(int n, int ncall, double[] xbest, int[] icount, int ninit,
                        double[] list, int[] numpts, int[] initpt, int nbaskt,
                        double[] xbaskt, double[] boxl, double[] boxu, int nstate,
                        int[] iuser, double[] ruser, int inform)
      {
        int i, iplot, j;
    
        inform = 0;
    
        iplot = iuser[0];
    
        if (nstate==0 || nstate==1)
          {
            /* When nstate=1, monit is called for the first time. 
               When nstate=0, monit is called for the first AND last time.
               Display a welcome message */
                
            System.out.println(" *** Begin monitoring information ***");
            System.out.println();
            
            if (iplot==1 && n==2)
              System.out.println(" ");
          }
            
        if (iplot==1 && n==2)
          {
            /* Display the coordinates of the edges of the current 
               search box */
            System.out.println(boxl[0] + " " + boxl[1]);
            System.out.println(boxl[0] + " " + boxu[1]);
            System.out.println();
            System.out.println(boxl[0] + " " + boxl[1]);
            System.out.println(boxu[0] + " " + boxl[1]);
            System.out.println();
            System.out.println(boxl[0] + " " + boxu[1]);
            System.out.println(boxu[0] + " " + boxu[1]);
            System.out.println();
            System.out.println(boxu[0] + " " + boxl[1]);
            System.out.println(boxu[0] + " " + boxu[1]);
          }
        if (nstate<=0)
          {
            /* monit is called for the last time */
            if (iplot==1 && n==2)
              System.out.println(" ");
            
            System.out.println(" Total sub-boxes = " + icount[0]);
            System.out.println(" Total function evaluations = " + ncall);
            System.out.println(" Total function evaluations in local search = " + icount[1]);
            System.out.println(" Total points used in local search = " + icount[2]);
            System.out.println(" Total sweeps through levels = " + icount[3]);
            System.out.println(" Total splits by init. list = " + icount[4]);
            System.out.println(" Lowest level with nonsplit boxes = " + icount[5]);
            System.out.println(" Number of candidate minima in 'shopping basket' = " + nbaskt);
            System.out.println(" Shopping backet:");
            for(i=0;inAG Library routine e05jbf via the Java Native Interface
            ifail = e05jbf(n,objfunction,ibound,iinit,bl,bu,sdlist,list,numpts,
                           initpt,monitrt,x,obj,comm,lcomm,iuser,ruser);
            
            // Output some results
            System.out.println();
            System.out.println(" Results returned by nAG global optimization routine e05jbf");
            System.out.println(" -----------------------------------------------------------");
            System.out.println(" Fail code ifail = " + ifail);
            if (ifail == 0)
              {
                System.out.println(" Final objective function value = " + obj[0]);
                System.out.print(" Global optimum x: ");
                for (i = 0; i < n; i++)
                  System.out.print(x[i] + " ");
                System.out.println();
              }
          }
      }
    }
    

    E05JBF のルーチン OBJFUN and MONIT に対応する形で2つのJavaメソッドが提供されている。ここでは nAG Fortran Library マニュアル中の E05JBF の用例プログラムで規定される最適化問題を解くことにする。

    Fortran Library ルーチン E05JAF に対する JNI インタフェースも宣言するが、それは E05JBF の初期化に使用できる。Javaプログラムにはこのルーチンに対するコールが含まれている。

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

      % javac GlobalOptimization.java 
    

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

      % javah -jni GlobalOptimization 
    
    生成されたヘッダファイル GlobalOptimization.h には2つの JNI 関数に対応する次の2つの関数プロトタイプが含まれている。
      JNIEXPORT jint JNICALL Java_GlobalOptimization_e05jbf
      (JNIEnv *, jobject, jint, jstring, jint, jint, jdoubleArray, jdoubleArray,
      jint, jdoubleArray, jintArray, jintArray, jstring, jdoubleArray, jdoubleArray,
      jdoubleArray, jint, jintArray, jdoubleArray);
     
      JNIEXPORT jint JNICALL Java_GlobalOptimization_e05jaf
        (JNIEnv *, jobject, jint, jdoubleArray, jint); 
     

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

    5.1 Cインタフェースライブラリ用ソースコード

    次はファイル GlobalOptimizationImp.c の内容を記したものである。

    #include "GlobalOptimization.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 globalObjfunID;
    jmethodID globalMonitID;
     
    /* This routine has the interface required by nAG routine e05jbf for
       argument objfun. It makes calls back to the Java version of objfun */
    void objfunFun(int *n, double x[], double *f, int *nstate,
                   int iuser[], double ruser[], int *inform)
    {
      int i;
     
      /* First create Java arrays to pass to objfun */
      jdoubleArray jx = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, *n);
      jintArray jiuser = (*globalJavaEnv)->NewIntArray(globalJavaEnv, 1);
      jdoubleArray jruser = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, 1);
     
      /* Copy input array x to Java array jx */
      double *jxpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jx, 0);
      int *jiuserpt = (*globalJavaEnv)->GetIntArrayElements(globalJavaEnv, jiuser, 0);
      double *jruserpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jruser, 0);
      for (i = 0; i < *n; i++)
        jxpt[i] = x[i];
      for (i = 0; i < 1; i++)
        jiuserpt[i] = iuser[i];
      for (i = 0; i < 1; i++)
        jruserpt[i] = ruser[i];
     
      /* Release array elements back to Java (this puts the values into jx, jiuser, jruser) */
      (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jx, jxpt, 0);
      (*globalJavaEnv)->ReleaseIntArrayElements(globalJavaEnv, jiuser, jiuserpt, 0);
      (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jruser, jruserpt, 0);
     
      /* We need to set inform to a non-negative value */
      *inform = 0;
     
      /* Call Java objfun which returns objf */
      *f = (*globalJavaEnv)->CallDoubleMethod(globalJavaEnv, globaljavaobject,
                                              globalObjfunID, (jint) (*n), jx,
                                              (jdouble) (*f), (jint) (*nstate),
                                              jiuser, jruser, (jint) (*inform));
    }
     
    /* This routine has the interface required by nAG routine e05jbf for
       argument monit. It makes calls back to the Java version of monit */
    void monitFun(int *n, int *ncall, double xbest[], int icount[],
                  int *ninit, double list[], int numpts[], int initpt[],
                  int *nbaskt, double xbaskt[], double boxl[],
                  double boxu[], int *nstate, int iuser[], double ruser[],
                  int *inform)
    {
      int i,j;
      int *jicountpt, *jnumptspt, *jinitptpt, *jiuserpt;
      double *jxbestpt, *jlistpt, *jxbasktpt, *jboxlpt, *jboxupt, *jruserpt;
     
      /* First create some Java arrays to pass to monit */
      jintArray jicount = (*globalJavaEnv)->NewIntArray(globalJavaEnv, 6);
      jintArray jnumpts = (*globalJavaEnv)->NewIntArray(globalJavaEnv, *n);
      jintArray jinitpt = (*globalJavaEnv)->NewIntArray(globalJavaEnv, *n);
      jintArray jiuser = (*globalJavaEnv)->NewIntArray(globalJavaEnv, 1);
      jdoubleArray jxbest = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, *n);
      jdoubleArray jlist = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, ((*n)*(*ninit)));
      jdoubleArray jxbaskt = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, ((*n)*(*nbaskt)));
      jdoubleArray jboxl = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, *n);
      jdoubleArray jboxu = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, *n);
      jdoubleArray jruser = (*globalJavaEnv)->NewDoubleArray(globalJavaEnv, 1);
     
      /* Copy input arguments to Java arrays*/
      jicountpt = (*globalJavaEnv)->GetIntArrayElements(globalJavaEnv, jicount, 0);
      jnumptspt = (*globalJavaEnv)->GetIntArrayElements(globalJavaEnv, jnumpts, 0);
      jinitptpt = (*globalJavaEnv)->GetIntArrayElements(globalJavaEnv, jinitpt, 0);
      jiuserpt = (*globalJavaEnv)->GetIntArrayElements(globalJavaEnv, jiuser, 0);
      jxbestpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jxbest, 0);
      jlistpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jlist, 0);
      jxbasktpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jxbaskt, 0);
      jboxlpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jboxl, 0);
      jboxupt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jboxu, 0);
      jruserpt = (*globalJavaEnv)->GetDoubleArrayElements(globalJavaEnv, jruser, 0);
      for (i = 0; i < 6; i++)
        jicountpt[i] = icount[i];
      for (i = 0; i < *n; i++)
        jnumptspt[i] = numpts[i];
      for (i = 0; i < *n; i++)
        jinitptpt[i] = initpt[i];
      for (i = 0; i < *n; i++)
        jxbestpt[i] = xbest[i];
      for (i = 0; i < *n; i++)
        {
          for (j = 0; j < *ninit; j++)
            jlistpt[j*(*n) + i] = list[j*(*n) + i];
        }
      for (i = 0; i < *n; i++)
        {
          for (j = 0; j < *nbaskt; j++)
            jxbasktpt[j*(*n) + i] = xbaskt[j*(*n) + i];
        }
      for (i = 0; i < *n; i++)
        jboxlpt[i] = boxl[i];
      for (i = 0; i < *n; i++)
        jboxupt[i] = boxu[i];
      for (i = 0; i < 1; i++)
        jiuserpt[i] = iuser[i];
      for (i = 0; i < 1; i++)
        jruserpt[i] = ruser[i];
      /* Release array elements back to Java (this puts the values
         back into Java arrays) */
      (*globalJavaEnv)->ReleaseIntArrayElements(globalJavaEnv, jicount, jicountpt, 0);
      (*globalJavaEnv)->ReleaseIntArrayElements(globalJavaEnv, jnumpts, jnumptspt, 0);
      (*globalJavaEnv)->ReleaseIntArrayElements(globalJavaEnv, jinitpt, jinitptpt, 0);
      (*globalJavaEnv)->ReleaseIntArrayElements(globalJavaEnv, jiuser, jiuserpt, 0);
      (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jxbest, jxbestpt, 0);
      (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jlist, jlistpt, 0);
      (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jxbaskt, jxbasktpt, 0);
      (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jboxl, jboxlpt, 0);
      (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jboxu, jboxupt, 0);
      (*globalJavaEnv)->ReleaseDoubleArrayElements(globalJavaEnv, jruser, jruserpt, 0);
     
      /* Call the Java method via its method ID */
      *inform = (*globalJavaEnv)->CallIntMethod(globalJavaEnv, globaljavaobject,
                                                globalMonitID, (jint)(*n),
                                                (jint) (*ncall), jxbest, jicount,
                                                (jint)(*ninit), jlist, jnumpts,
                                                jinitpt, (jint)(*nbaskt), jxbaskt,
                                                jboxl, jboxu, (jint)(*nstate),
                                                jiuser, jruser, (jint) (*inform));
    }
     
    JNIEXPORT jint JNICALL Java_GlobalOptimization_e05jbf
    (
     JNIEnv *env,
     jobject object,
     jint n,
     jstring objfun,
     jint ibound,
     jint iinit,
     jdoubleArray bl,
     jdoubleArray bu,
     jint sdlist,
     jdoubleArray list,
     jintArray numpts,
     jintArray initpt,
     jstring monit,
     jdoubleArray x,
     jdoubleArray obj,
     jdoubleArray comm,
     jint lcomm,
     jintArray iuser,
     jdoubleArray ruser
     )
    {
      /* Local variables and arrays */
      int ifail;
     
      jclass cls;
      const char *objfunction;
      const char *monitrt;
     
      jdouble *bl_pt, *bu_pt, *list_pt, *x_pt, *obj_pt,
              *comm_pt, *ruser_pt;
      jint *numpts_pt, *initpt_pt, *iuser_pt;
     
      /* Copy the Java env pointers to global space
         so that monitFun and objfunFun can access them. */
      globalJavaEnv = env;
      globaljavaobject = object;
     
      /* Get hold of the name of the user's Java evaluation functions. */
      objfunction = (*env)->GetStringUTFChars(env, objfun, 0);
      monitrt = (*env)->GetStringUTFChars(env, monit, 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 objfunFun
         and monitFun can use them. Note that the Java function signatures
         must be correct. You can find out the signatures after compiling
         the Java program GlobalOptimization.java by using the command
         % javap -private -s GlobalOptimization
       */
      cls = (*env)->GetObjectClass(env, object);
      globalObjfunID = (*env)->GetMethodID(env, cls, objfunction, "(I[DDI[I[DI)D");
      globalMonitID = (*env)->GetMethodID(env, cls, monitrt, "(II[D[II[D[I[II[D[D[DI[I[DI)I");
     
      /* Free up the Java string argument so we don't leak memory. */
      (*env)->ReleaseStringUTFChars(env, objfun, objfunction);
      (*env)->ReleaseStringUTFChars(env, monit, monitrt);
     
      if (globalObjfunID == 0)
        {
          printf(
          "Cannot find objfun method \"%s\" with signature \"(I[DDI[I[DI)D\"\n",
                 objfunction);
          return -1;
        }
      if (globalMonitID == 0)
        {
          printf(
          "Cannot find monit method \"%s\" with signature \"(II[D[II[D[I[II[D[D[DI[I[DI)I\"\n",
                 monitrt);
          return -1;
        }
     
      /* Extract the arrays from Java */
      bl_pt = (*env)->GetDoubleArrayElements(env, bl, 0);
      bu_pt = (*env)->GetDoubleArrayElements(env, bu, 0);
      list_pt = (*env)->GetDoubleArrayElements(env, list, 0);
      x_pt = (*env)->GetDoubleArrayElements(env, x, 0);
      obj_pt = (*env)->GetDoubleArrayElements(env, obj, 0);
      comm_pt = (*env)->GetDoubleArrayElements(env, comm, 0);
      ruser_pt = (*env)->GetDoubleArrayElements(env, ruser, 0);
      numpts_pt = (*env)->GetIntArrayElements(env, numpts, 0);
      initpt_pt = (*env)->GetIntArrayElements(env, initpt, 0);
      iuser_pt = (*env)->GetIntArrayElements(env, iuser, 0);
     
      /* Call to main nAG Library routine e05jbf */
      ifail = -1;
    #ifdef WINDOWS
        E05JBF
    #else
        e05jbf_
    #endif
          (&n, objfunFun, &ibound, &iinit, bl_pt, bu_pt,
           &sdlist, list_pt, numpts_pt, initpt_pt, monitFun,
           x_pt, obj_pt, comm_pt, &lcomm, iuser_pt, ruser_pt,
           &ifail);
     
      /* Release the array elements back to Java and free memory. */
      (*env)->ReleaseDoubleArrayElements(env, bl, bl_pt, 0);
      (*env)->ReleaseDoubleArrayElements(env, bu, bu_pt, 0);
      (*env)->ReleaseDoubleArrayElements(env, list, list_pt, 0);
      (*env)->ReleaseDoubleArrayElements(env, x, x_pt, 0);
      (*env)->ReleaseDoubleArrayElements(env, obj, obj_pt, 0);
      (*env)->ReleaseDoubleArrayElements(env, comm, comm_pt, 0);
      (*env)->ReleaseDoubleArrayElements(env, ruser, ruser_pt, 0);
      (*env)->ReleaseIntArrayElements(env, numpts, numpts_pt, 0);
      (*env)->ReleaseIntArrayElements(env, initpt, initpt_pt, 0);
      (*env)->ReleaseIntArrayElements(env, iuser, iuser_pt, 0);
     
      return ifail;
     
    }
     
    // Interface to initialization routine e05jaf
    JNIEXPORT jint JNICALL Java_GlobalOptimization_e05jaf
    (JNIEnv *env, jobject object, jint n, jdoubleArray comm, jint lcomm)
    {
      jdouble *comm_pt;
      int ifail;
     
      /* Extract the array from Java */
      comm_pt = (*env)->GetDoubleArrayElements(env, comm, 0);
     
      ifail = -1;
      /* Call the initialization routine */
    #ifdef WINDOWS
      E05JAF(&n,comm_pt,&lcomm,&ifail);
    #else
      e05jaf_(&n,comm_pt,&lcomm,&ifail);
    #endif
     
      /* Release the array elements back to Java and free memory. */
      (*env)->ReleaseDoubleArrayElements(env, comm, comm_pt, 0);
     
      return ifail;
    } 
      

    5.2 Cコードの解説

    Java_GlobalOptimization_e05jbf という名前の関数はJavaで宣言されたメソッド e05jbf のCによる実装である。

    目的関数評価用とプロセス監視用のJavaメソッド objfun, monit を直接 nAG Fortran Library ルーチン E05JBF に引渡すことはできないので、それらをC関数中にラップ(wrap)する必要がある。それらがそれぞれ objfunFun, monitFun という名前のC関数である。

       void objfunFun(int *n, double x[], double *f, int *nstate,
                   int iuser[], double ruser[], int *inform)
     
       void monitFun(int *n, int *ncall, double xbest[], int icount[],
                  int *ninit, double list[], int numpts[], int initpt[],
                  int *nbaskt, double xbaskt[], double boxl[],
                  double boxu[], int *nstate, int iuser[], double ruser[],
                  int *inform) 
    
    これらの関数は nAG Library ルーチン E05JBF によって必要とされる引数の型と応答の型を持つ。n のようなすべてのスカラ引数はFortranで要求されるようにポインタとして引渡される点に注意。また引数 listxbaskt はFortranルーチンによって指定される2次元配列ではなく、1次元配列として宣言されている点にも注意を要する。これは2次元のJava配列がFortranの2次元配列に簡単にマップできないという事情による。

    objfunFun, monitFun 内でやることと言えば、対応するJavaメソッドをコールするだけである。ここでもやはりトリックはJavaに対するコール方法を知ることができるという点にある。objfun に対しては JNI 関数 CallDoubleMethod を(Java中でこのメソッドは倍精度の戻り値を持つと定義してあるため)、monit に対しては CallIntMethod を用いてこれを行う。

    Javaメソッド objfun monit に対しては共に配列引数を引渡す必要がある。これらのメソッドはこの配列要素に対して情報をセットする。Example 3 の場合と同様、これら2メソッドのメソッドIDを求めた後、それらをCコード中のグローバル変数内に格納する。それによってそれらの情報はCで書かれた評価関数内、及びメインの JNI 関数からもアクセスできるようになる。これらのIDは JNI 関数 GetMethodID へのコールに際し、適切な名前と署名(signatures)を引渡すことによって得ることができる。なお、Javaメソッドの署名を求める良い方法はJavaプログラム GlobalOptimization.java をコンパイルした後、コマンド

      % javap -private -s GlobalOptimization 
    
    を用いる方法である。

    問題はC関数 objfunFun, monitFun がJavaメソッドに対しJava配列を引渡す必要があるにもかかわらず、C(またはFortran)スタイルの配列しか持ち合わせていないという点にある。従ってCコードはJava配列を生成した上で、C配列の内容をJava配列中にコピーする必要がある。Javaの倍精度配列を生成するためには、JNI 関数 NewDoubleArray を使用した上で GetDoubleArrayElements をコールし配列要素へのCポインタを求め、さらにJavaメソッドをコールする前に ReleaseDoubleArrayElements を用いてCの内容をJava配列中にコピーする。Javaメソッドから戻ったら再度 GetDoubleArrayElements をコールし結果を得る。整数配列の場合、JNI 関数 NewIntArray, GetIntArrayElements, ReleaseIntArrayElements が適切である。ただしデータを正しい位置に正しいタイミングで得るためには、これらの JNI 関数を正しい順序でコールすることが肝要である。GlobalOptimizationImp.c 中の用例を参照されたい。

    5.3 結果のJavaへの引渡し

    この例においては、すべての結果はネイティブメソッドへのJavaコールから得られる配列引数を介してJavaに返される。ただしエラーコード IFAIL は例外で、それは関数名を介して返される。なお、最適な関数値を含む引数 obj を長さ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 \
            	GlobalOptimizationImp.c
        % ld -G -z defs GlobalOptimizationImp.o -o libnagCJavaInterface.so \
             /opt/nAG/fll6a22df/lib/libnag_nag.so -lm -lc -lpthread
      

      他のUNIX系マシン上ではさらなるライブラリの追加がリンク時に必要となる場合がある(Example 1 の注意参照)。

    • Windows上でのVisual C++による作成

        C:\> cl -DWINDOWS -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32
               /Gz -LD GlobalOptimizationImp.c
               "c:\Program Files\nAG\fldll224zl\lib\FLDLL224Z_nag.lib" -FenagCJavaInterface.dll 
      

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

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

      % java GlobalOptimization 
    
    期待される出力は次のとおり。

     Routine e05jbf has been initialised by e05jaf.
     ----------------------------------------------
     
     Running e05jbf example program from Java
     ----------------------------------------
     Problem:
     
       Find global minimum of the 'peaks' function:
       F(x,y) = 3*(1-x)^2*exp(-x^2-(y+1)^2)-10(x/5-x^3-y^5)*exp(-x^2-y^2)
                -1/3*exp(-(x+1)^2-y^2)
       on the box [-3.0,3.0]x[-3.0,3.0]
     
     (objfun was just called for the first time)
     
     *** Begin monitoring information ***
     
     Total sub-boxes = 228
     Total function evaluations = 196
     Total function evaluations used in local search = 87
     Total points used in local search = 13
     Total sweeps through levels = 12
     Total splits by init. list = 5
     Lowest level with nonsplit boxes = 7
     Number of candidate minima in the 'shopping basket' = 2
     Shopping backet:
     xbaskt[0,0] = -1.347396243542219
     xbaskt[0,1] = 0.2282789212962065
     xbaskt[1,0] = 0.20451885579921375
     xbaskt[1,1] = -1.6255349573219788
     
     *** End monitoring information ***
     
     Results returned by nAG global optimization routine e05jbf
     -----------------------------------------------------------
     Fail code ifail = 0
     Final objective function value = -6.551133332835838
     Global optimum x: 0.2282789212962065 -1.6255349573219788 
    

    (ライブラリが見つけられないという趣旨のエラーメッセージが発行された場合には、Example 1 ヒント を参照)

  15. クイックサマリ
  16. 2つのソースファイル GlobalOptimization.java GlobalOptimizationImp.c が与えられたとして、次のコマンドを発行する。

    • Javaクラスのコンパイル:
        % javac GlobalOptimization.java 
      
    • ヘッダファイルの作成:
        % javah -jni GlobalOptimization 
      
    • インタフェースライブラリのコンパイル:

      • (Linux)
          % gcc -c -fPIC -I/opt/jdk1.6.0_11/include -I/opt/jdk1.6.0_11/include/linux \
              	GlobalOptimizationImp.c
            % ld -G -z defs GlobalOptimizationImp.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 GlobalOptimizationImp.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.libnAG Fortran Library のコピーである。

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

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


関連情報
© 日本ニューメリカルアルゴリズムズグループ株式会社 2024
Privacy Policy  /  Trademarks