Ho trascorso qualche tempo da sola domanda e vorrei condividere la mia risposta come mi sento non è molte informazioni su questo argomento sullo stackoverflow . Penso inoltre che Java diventerà più rilevante nel calcolo scientifico (ad esempio vedere il pacchetto WEKA per il data mining) a causa del miglioramento delle prestazioni e di altre buone funzionalità di sviluppo del software di Java.
In generale, si scopre che utilizzando gli strumenti giusti è molto più facile di estendere Python con Java che con C/C++!
Descrizione e valutazione degli strumenti di chiamare Java da Python
http://pypi.python.org/pypi/JCC: a causa di nessun corretta documentazione di questo strumento è inutile.
Py4J: richiede di avviare il processo di Java prima di utilizzare Python. Come rilevato da da altri, questo è un possibile punto di errore. Inoltre, non sono molti gli esempi di utilizzo documentati.
JPype: anche se lo sviluppo sembra essere la morte, funziona bene e ci sono molti esempi su di esso sul web (ad esempio vedi http://kogs-www.informatik.uni-hamburg.de/~meine/weka-python/ per l'utilizzo di librerie di data mining scritti in Java). Pertanto, , ho deciso di mettere a fuoco su questo strumento.
Installazione JPype su Fedora 16
Sto usando Fedora 16, dal momento che ci sono alcuni problemi durante l'installazione JPype su Linux, descrivo il mio approccio. Scarica JPype, quindi modificare setup.py sceneggiatura fornendo il percorso JDK, in linea 48:
self.javaHome = '/usr/java/default'
quindi eseguire:
sudo python setup.py install
Afters corretta installazione, controllare questo file:
/usr/lib64/python2.7/site-packages/jpype/_linux.py
e rimuovere o rinominare il metodo getDefaultJVMPath() in getDefaultJVMPath_old(), quindi aggiungere il seguente metodo:
def getDefaultJVMPath():
return "/usr/java/default/jre/lib/amd64/server/libjvm.so"
approccio alternativo: non apportare alcuna modifica in quanto sopra file _linux.py, ma non utilizzare mai il metodo getDefaultJVMPath() (oi metodi che chiamano questo metodo). Al posto di utilizzare getDefaultJVMPath() fornire direttamente il percorso per la JVM. Si noti che ci sono diversi percorsi, per esempio nel mio sistema ho anche i seguenti percorsi, facendo riferimento a diverse versioni della JVM (non mi è chiaro se il client o server JVM è più adatto):
- /usr/lib/jvm/java-1.5.0-gcj-1.5.0.0/jre/lib/x86_64/client/libjvm.so
- /usr/lib/jvm/java-1.5.0-gcj-1.5. 0.0/jre/lib/x86_64/server/libjvm.so
- /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/amd64/server/libjvm.so
Infine, aggiungere la seguente riga a ~/.bashrc (o eseguire ogni volta prima di aprire un interprete python):
export JAVA_HOME='/usr/java/default'
(La directory sopra è in realtà solo un link simbolico a mia ultima versione del JDK, che è situato in /usr/java/jdk1.7.0_04).
Si noti che tutti i test nella directory in cui è stato scaricato JPype, ad esempio JPype-0.5.4.2/test/testsuite.py non funzioneranno (quindi non preoccuparsene).
Per vedere se funziona, provare questo script in 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()
Calling classi Java da Java utilizzando anche Numpy
Cominciamo l'implementazione di una classe Java che contiene alcune funzioni che voglio applicare a array di numpy. Dal momento che non esiste un concetto di stato, utilizzo le funzioni statiche in modo che non sia necessario creare alcun oggetto Java (la creazione di oggetti Java non cambierebbe nulla).
/**
* 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);
}
}
Il nome della classe è un po 'fuorviante, in quanto non solo proponiamo di calcolare la media di un vettore numpy (utilizzando il metodo compute_average), ma anche moltiplichiamo un vettore NumPy per uno scalare (metodo moltiplicare) e infine la moltiplicazione di matrice (metodo mult_mat).
Dopo aver compilato la classe Java di cui sopra può ora eseguire il seguente script 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()
Dai un'occhiata alla [è-ci-a-buon-NumPy-clone-per-Jython] (http://stackoverflow.com/q/316410/776084) – RanRag
bene, io non sono davvero alla ricerca di un clone, in quanto ho un bel codice numpy e trovo molto bello. È un peccato che non ci sia un modo diretto per usare numpy con Java ... – Mannaggia
Il codice Java ha dimensioni abbastanza significative da impedirti di riscrivere le sezioni sensibili alle prestazioni in Cython e usare numpy/python per riposo? – JoshAdel