4.2. Example 2
連立1次方程式ソルバ、関数 f04arc |
内容
- nAG C Library マニュアルからの関数プロトタイプ
- Javaプログラム中でのネイティブ関数の宣言
- Javaプログラムのコンパイル
- C用のヘッダファイルの生成
- ネイティブ関数のCによる実装
- 共用ライブラリ/DLLの作成
- プログラムの実行
- クイックサマリ
- nAG C Library マニュアルからの関数プロトタイプ
- Javaプログラム中でのネイティブ関数の宣言
- Javaプログラムのコンパイル 次に示すのはJavaプログラム LinearEquations.java のソースコードである。
- 行列 A は2次元であるが、ここではそれを型が double[]
の1次元Java配列上にストアしている。配列添え字の操作が必要となるため、Javaコードはそうでない場合に比べやや読みにくくなっているが、インタフェースライブラリ中に記述する必要のあるCコードははるかに簡単なものとなる。Java側では例えば
a[2][2] = -17.0;
と書く代りにa[2*n + 2] = -17.0;
と書くことになる。
- nAG Library ルーチン f04arc は行列 A を LU 要素によって上書きする。このため A のコピーを配列 copyA 中に取り、それを用いて残差ベクトル r = A x – b の計算を行う。
- C用のヘッダファイルの生成 LinearEquations.java のコンパイルが終わると、javah を使ってCヘッダファイルを作成することができる。
- ネイティブ関数のCによる実装 ヘッダファイル LinearEquations.h が作成できたところで、今度は Java_LinearEquations_f04arc のCコードによる実装に移る。次はファイル LinearEquationsImp.c の内容を記したものである。
- 前の場合と同様、適切なnAG C Library ヘッダファイルをインクルードすると共に、f04arc へのfail引数として引渡す型 NagError の変数 fail も宣言しておかなくてはならない。
- 配列引数の要素 a, b, x
を直接アクセスすることはできない。それらはCスタイルの配列ではなく、型が jdoubleArray
のJavaスタイル配列だからである。配列要素を直接アクセスしようとすると破滅的な事態が生じるので、JNI 関数
GetDoubleArrayElements を用いてCスタイルの倍精度配列に変換しなくてはならない。この関数は JNI
ヘッダファイル jni.h 中で次のように宣言する。
jdouble * (JNICALL *GetDoubleArrayElements) (JNIEnv *env, jdoubleArray array, jboolean *isCopy);
GetDoubleArrayElements は JNIEnv ポインタ *env を介してアクセスされる。型が jdoubleArray の配列が与えられると、それは型が jdouble の配列要素に対するポインタ(Cで安全に操作できる)を応答として返す。出力引数 isCopy はJavaが配列のコピーを作成したか、あるいは単に要素へのポインタを引渡しただけなのかどうかを示す。今回の場合はどちらであっても構わない。上記Cプログラム中ではそれぞれの配列引数ごとに GetDoubleArrayElements を計3回コールする。戻り値であるポインタはそのまま nAG Library 関数 f04arc に引渡される。
- nAG Library から戻った後、Javaに対し配列ポインタを使い終えたことを伝える必要がある。そこで関数 ReleaseDoubleArrayElements
を3回コールするわけであるが、それは
jni.h 中に次のように宣言されている。
void (JNICALL *ReleaseDoubleArrayElements) (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode);
この操作は2つの理由によって必要となる。結果が適切なJava配列に反映されること、それによってJavaのガベージコレクションが適切に行われるようにすることの2点である(これを行わなかった場合にはJavaによるメモリ解放が正しく行われないことが起り得る)。
- 共用ライブラリ/DLLの作成 このステップはOS依存となる。
- Linux上での作成
% gcc -c -fPIC -I/opt/jdk1.6.0_11/include \ -I/opt/jdk1.6.0_11/include/linux \ -I/opt/nAG/cll6a09dgl/include LinearEquationsImp.c % ld -G -z defs LinearEquationsImp.o -o libnagCJavaInterface.so \ /opt/nAG/cll6a09dgl/lib/libnagc_nag.so -lm -lc -lpthread
他のUNIX系マシン上ではさらなるライブラリの追加がリンク時に必要となる場合がある(Example 1 の注意参照)。
- Windows上でのVisual C++による作成
C:\> cl -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32 -I"c:\Program Files\nAG\cldll094zl\include" /Gz -LD LinearEquationsImp.c "c:\Program Files\cldll094zl\lib\CLDLL094Z_nag.lib" -FenagCJavaInterface.dll
- プログラムの実行 これまでの操作がすべて正常に行われたとすると、次のコマンドによってプログラムを実行できる。
- クイックサマリ 2つのソースファイル LinearEquations.java と LinearEquationsImp.c が与えられたとして、次のコマンドを発行する。
- Javaクラスのコンパイル:
% javac LinearEquations.java
- ヘッダファイルの作成:
% javah -jni LinearEquations
- インタフェースライブラリのコンパイル:
- (Linux)
% gcc -c -fPIC -I/opt/jdk1.6.0_11/include \ -I/opt/jdk1.6.0_11/include/linux \ -I/opt/nAG/cll6a09dgl/include LinearEquationsImp.c % ld -G -z defs LinearEquationsImp.o \ -o libnagCJavaInterface.so \ /opt/nAG/cll6a09dgl/lib/libnagc_nag.so -lm -lc -lpthread
ただしディレクトリ名称 /opt/jdk1.6.0_11/include, /opt/jdk1.6.0_11/include/linux, /opt/nAG/cll6a09dgl/include, /opt/nAG/cll6a09dgl/lib はJavaと nAG C Library のインストレーションに合せて適宜調整を要す。 - (Windows/Visual C++)
C:\> cl -Ic:\jdk1.6.0_11\include -Ic:\jdk1.6.0_11\include\win32 -I"c:\Program Files\nAG\cldll094zl\include" /Gz -LD LinearEquationsImp.c "c:\Program Files\cldll094zl\lib\CLDLL094Z_nag.lib" -FenagCJavaInterface.dll
ただしディレクトリ名称 c:\jdk1.6.0_11\include, c:\jdk1.6.0_11\include\win32, "c:\Program Files\nAG\cldll094zl\include", "c:\Program Files\nAG\cldll094zl\lib" はJavaと nAG C Library のインストレーションに合せて適宜調整を要す。
- (Linux)
- Javaプログラムの実行:
% java LinearEquations
C Library マニュアルによれば関数 f04arc に対するプロトタイプは次のとおりである。
#include <nag.h> #include <nagf04.h> void f04arc(Integer n, double a[], Integer tda, double b[], double x[], NagError *fail);この関数は n 個の連立1次方程式 A x = b の解を求める。ここに A は n x n の行列であり、b と x は n 元の列ベクトルである。
引数 tda は nAG Library ルーチンに対し2次元行列 A の展開次元(trailing dimension)を通知するために用いられる。
Example 1 の場合と同様、NagError 構造体の内容をJavaには戻さないものとする。従ってJavaプログラム内では関数を次のように宣言すれば良い。
// Declaration of the Native (C) function private native int f04arc(int n, double[] a, int tda, double[] b, double[] x);これは int を応答として返すメソッドを意味する。ここでは fail 引数は用いず、エラーコードはすべて int 戻り値によって送り返すものとする。
public class LinearEquations { // Declaration of the Native (C) function private native int f04arc(int n, double[] a, int tda, double[] b, double[] x); static { // The runtime system executes a class's static // initializer when it loads the class. System.loadLibrary("nagCJavaInterface"); } // The main program public static void main(String[] args) { double a[], b[], x[], r[], copyA[]; int i, j, n, tda, retCode; // Create an object of class LinearEquations LinearEquations lineq = new LinearEquations(); n = 3; tda = n; a = new double[n*n]; b = new double[n]; x = new double[n]; r = new double[n]; copyA = new double[n*n]; a[0*n + 0] = 33.0; a[0*n + 1] = 16.0; a[0*n + 2] = 72.0; a[1*n + 0] = -24.0; a[1*n + 1] = -10.0; a[1*n + 2] = -57.0; a[2*n + 0] = -8.0; a[2*n + 1] = -4.0; a[2*n + 2] = -17.0; b[0] = -359.0; b[1] = 281.0; b[2] = 85.0; // Copy matrix A for later use (it gets overwritten by f04arc). for (i = 0; i < n * n; i++) copyA[i] = a[i]; System.out.println(); System.out.println("Call of nAG f04arc"); System.out.println(); // Print the input matrix A and vector b System.out.println("Input matrix A:"); for (i = 0; i < n; i++) { for (j = 0; j < n; j++) System.out.print(" " + a[i*n + j]); System.out.println(); } System.out.println(); System.out.println("Input vector b:"); for (i = 0; i < n; i++) System.out.println(" " + b[i]); System.out.println(); // Call method f04arc of object lineq retCode = lineq.f04arc(n, a, tda, b, x); System.out.print("Return code from f04arc = "); System.out.println(retCode); System.out.println(); if (retCode == 0) { // Print the solution vector x System.out.print("Solution vector x:\n"); for (i = 0; i < n; i++) System.out.println(" " + x[i]); System.out.println(); // Calculate and print residual vector for (i = 0; i < n; i++) { r[i] = -b[i]; for (j = 0; j < n; j++) r[i] += copyA[i*n + j] * x[j]; } System.out.print("Residual vector r = A * x - b:\n"); for (i = 0; i < n; i++) System.out.println(" " + r[i]); System.out.println(); } } }このプログラムに関する注意事項:
Javaプログラムをコンパイルするには次のコマンドを使用すれば良い。
% javac LinearEquations.java
% javah -jni LinearEquations生成されたヘッダファイル LinearEquations.h には次の関数プロトタイプが含まれている。
JNIEXPORT jint JNICALL Java_LinearEquations_f04arc (JNIEnv *, jobject, jint, jdoubleArray, jint, jdoubleArray, jdoubleArray);
前の場合と同様、Cの視点からすると、我々の関数はJava環境ポインタとJavaオブジェクトという2つの引数を余計に含んでいる。今回の場合にはCコード中でもこれらの引数が必要となる。
#include <jni.h> /* Java Native Interface headers */ #include "LinearEquations.h" /* Auto-generated by javah -jni */ #include <nag.h> /* nAG C Library headers */ #include <nagf04.h> /* Our C definition of f04arc declared in LinearEquations.java */ JNIEXPORT jint JNICALL Java_LinearEquations_f04arc (JNIEnv *env, jobject obj, jint n, jdoubleArray a, jint tda, jdoubleArray b, jdoubleArray x) { static NagError fail; /* First extract the arrays from Java */ jdouble *apt, *bpt, *xpt; jsize len; int i; apt = (*env)->GetDoubleArrayElements(env, a, 0); bpt = (*env)->GetDoubleArrayElements(env, b, 0); xpt = (*env)->GetDoubleArrayElements(env, x, 0); /* Call f04arc */ fail.print = Nag_FALSE; f04arc(n, apt, tda, bpt, xpt, &fail); /* Release the array elements back to Java */ (*env)->ReleaseDoubleArrayElements(env, a, apt, 0); (*env)->ReleaseDoubleArrayElements(env, b, bpt, 0); (*env)->ReleaseDoubleArrayElements(env, x, xpt, 0); return fail.code; }注意事項:
関連するコンパイラフラグについては Example 1 セクション7 を参照されたい。
% java LinearEquations期待される出力は次のとおり。
Call of nAG linear equation solver routine f04arc Input matrix A: 33.0 16.0 72.0 -24.0 -10.0 -57.0 -8.0 -4.0 -17.0 Input vector b: -359.0 281.0 85.0 Return code from f04arc = 0 Solution vector x: 1.0 -2.0 -5.0 Residual vector r = A * x - b: 0.0 0.0 0.0
(ライブラリが見つけられないという趣旨のエラーメッセージが発行された場合には、Example 1 の ヒント を参照)
Copyright 2009 Numerical Algorithms Group
Page last updated 2009-04-21 14:11:48 mick
[NP3671]