Python を C++ で拡張する方法は明らかですが、numpy で使用する関数を Java で記述したい場合はどうすればよいでしょうか?
これは簡単なシナリオです: Java クラスを使用して numpy 配列の平均を計算したいと考えています。numpy ベクトルを Java クラスに渡し、結果を収集するにはどうすればよいですか?
助けてくれてありがとう!
私は自分の質問に時間を費やしましたが、 stackoverflowに関するこのトピックに関する情報があまりないと感じたので、回答を共有したいと思います。また、パフォーマンスの向上や Java のその他の優れたソフトウェア開発機能により、Java は科学計算 (たとえば、データ マイニング用の WEKA パッケージを参照) でより関連性が高くなると思います。
一般に、適切なツールを使用すると、Python を Java で拡張する方が C/C++ で拡張するよりもはるかに簡単であることがわかります。
http://pypi.python.org/pypi/JCC : 適切なドキュメントがないため、このツールは役に立ちません。
Py4J: Python を使用する前に Java プロセスを開始する必要があります。他の人が指摘したように、これは失敗の可能性のあるポイントです。さらに、多くの使用例が文書化されていません。
JPype : 開発は死んだように見えますが、うまく機能し、Web 上に多くの例があります (たとえば、使用方法についてはhttp://kogs-www.informatik.uni-hamburg.de/~meine/weka-python/を参照してください)。 Java で記述されたデータ マイニング ライブラリ) 。したがって、このツールに注目することにしました。
LinuxにJPypeをインストールするときにいくつかの問題があるため、Fedora 16を使用しています。私のアプローチについて説明します。JPypeをダウンロードし、48 行目に JDK パスを指定してsetup.pyスクリプトを変更します。
self.javaHome = '/usr/java/default'
次に実行します:
sudo python setup.py install
インストールが正常に完了したら、次のファイルを確認します。
/usr/lib64/python2.7/site-packages/jpype/_linux.py
メソッドgetDefaultJVMPath()を削除するか名前を変更してgetDefaultJVMPath_old()にし、次のメソッドを追加します。
def getDefaultJVMPath():
return "/usr/java/default/jre/lib/amd64/server/libjvm.so"
別のアプローチ: 上記のファイル_linux.pyを変更しないでください。ただし、メソッド getDefaultJVMPath() (またはこのメソッドを呼び出すメソッド) は使用しないでください。getDefaultJVMPath()を使用する場所で、JVM へのパスを直接指定します。いくつかのパスがあることに注意してください。たとえば、私のシステムには、さまざまなバージョンの JVM を参照する次のパスもあります (クライアント JVM とサーバー JVM のどちらが適しているかはわかりません)。
最後に、次の行を~/.bashrcに追加します(または、Python インタープリターを開く前に毎回実行します)。
export JAVA_HOME='/usr/java/default'
(上記のディレクトリは、実際には/usr/java/jdk1.7.0_04にある最後のバージョンの JDK への単なるシンボリック リンクです)。
JPype がダウンロードされたディレクトリ、つまりJPype-0.5.4.2/test/testsuite.py内のすべてのテストは失敗することに注意してください (そのため気にしないでください)。
動作するかどうかを確認するには、このスクリプトを Python でテストします。
import jpype
jvmPath = jpype.getDefaultJVMPath()
jpype.startJVM(jvmPath)
# print a random text using a Java class
jpype.java.lang.System.out.println ('Berlusconi likes women')
jpype.shutdownJVM()
numpy arraysに適用したいいくつかの関数を含む Java クラスの実装を始めましょう。状態の概念がないため、Java オブジェクトを作成する必要がないように静的関数を使用します (Java オブジェクトを作成しても何も変わりません)。
/**
* Cookbook to pass numpy arrays to Java via Jpype
* @author Mannaggia
*/
package test.java;
public class Average2 {
public static double compute_average(double[] the_array){
// compute the average
double result=0;
int i;
for (i=0;i<the_array.length;i++){
result=result+the_array[i];
}
return result/the_array.length;
}
// multiplies array by a scalar
public static double[] multiply(double[] the_array, double factor) {
int i;
double[] the_result= new double[the_array.length];
for (i=0;i<the_array.length;i++) {
the_result[i]=the_array[i]*factor;
}
return the_result;
}
/**
* Matrix multiplication.
*/
public static double[][] mult_mat(double[][] mat1, double[][] mat2){
// find sizes
int n1=mat1.length;
int n2=mat2.length;
int m1=mat1[0].length;
int m2=mat2[0].length;
// check that we can multiply
if (n2 !=m1) {
//System.err.println("Error: The number of columns of the first argument must equal the number of rows of the second");
//return null;
throw new IllegalArgumentException("Error: The number of columns of the first argument must equal the number of rows of the second");
}
// if we can, then multiply
double[][] the_results=new double[n1][m2];
int i,j,k;
for (i=0;i<n1;i++){
for (j=0;j<m2;j++){
// initialize
the_results[i][j]=0;
for (k=0;k<m1;k++) {
the_results[i][j]=the_results[i][j]+mat1[i][k]*mat2[k][j];
}
}
}
return the_results;
}
/**
* @param args
*/
public static void main(String[] args) {
// test case
double an_array[]={1.0, 2.0,3.0,4.0};
double res=Average2.compute_average(an_array);
System.out.println("Average is =" + res);
}
}
クラスの名前は少し誤解を招きます。numpy ベクトルの平均を計算すること (メソッドcompute_averageを使用) だけでなく、numpy ベクトルをスカラーで乗算すること (メソッドmultiply )、そして最後に行列乗算 (メソッドmult_mat )。
上記の Java クラスをコンパイルすると、次の Python スクリプトを実行できるようになります。
import numpy as np
import jpype
jvmPath = jpype.getDefaultJVMPath()
# we to specify the classpath used by the JVM
classpath='/home/mannaggia/workspace/TestJava/bin'
jpype.startJVM(jvmPath,'-Djava.class.path=%s' % classpath)
# numpy array
the_array=np.array([1.1, 2.3, 4, 6,7])
# build a JArray, not that we need to specify the Java double type using the jpype.JDouble wrapper
the_jarray2=jpype.JArray(jpype.JDouble, the_array.ndim)(the_array.tolist())
Class_average2=testPkg.Average2
res2=Class_average2.compute_average(the_jarray2)
np.abs(np.average(the_array)-res2) # ok perfect match!
# now try to multiply an array
res3=Class_average2.multiply(the_jarray2,jpype.JDouble(3))
# convert to numpy array
res4=np.array(res3) #ok
# matrix multiplication
the_mat1=np.array([[1,2,3], [4,5,6], [7,8,9]],dtype=float)
#the_mat2=np.array([[1,0,0], [0,1,0], [0,0,1]],dtype=float)
the_mat2=np.array([[1], [1], [1]],dtype=float)
the_mat3=np.array([[1, 2, 3]],dtype=float)
the_jmat1=jpype.JArray(jpype.JDouble, the_mat1.ndim)(the_mat1.tolist())
the_jmat2=jpype.JArray(jpype.JDouble, the_mat2.ndim)(the_mat2.tolist())
res5=Class_average2.mult_mat(the_jmat1,the_jmat2)
res6=np.array(res5) #ok
# other test
the_jmat3=jpype.JArray(jpype.JDouble, the_mat3.ndim)(the_mat3.tolist())
res7=Class_average2.mult_mat(the_jmat3,the_jmat2)
res8=np.array(res7)
res9=Class_average2.mult_mat(the_jmat2,the_jmat3)
res10=np.array(res9)
# test error due to invalid matrix multiplication
the_mat4=np.array([[1], [2]],dtype=float)
the_jmat4=jpype.JArray(jpype.JDouble, the_mat4.ndim)(the_mat4.tolist())
res11=Class_average2.mult_mat(the_jmat1,the_jmat4)
jpype.java.lang.System.out.println ('Goodbye!')
jpype.shutdownJVM()
私は、Jython が最良のオプションの 1 つであると考えています。これにより、Python で Java オブジェクトをシームレスに使用できるようになります。実際に weka を Python プログラムに統合しましたが、とても簡単でした。weka クラスをインポートして、Python コード内で Java と同じように呼び出すだけです。
numpy のサポートについてはよくわかりませんが、以下が役立つかもしれません。