2012-05-22 32 views
6

mi è chiaro come estendere Python con C++, ma cosa succede se voglio scrivere una funzione in Java da utilizzare con numpy?Come chiamare una funzione java da python/numpy?

Ecco uno scenario semplice: voglio calcolare la media di una matrice NumPy utilizzando una classe Java. Come posso passare il vettore numerico alla classe Java e raccogliere il risultato?

Grazie per qualsiasi aiuto!

+0

Dai un'occhiata alla [è-ci-a-buon-NumPy-clone-per-Jython] (http://stackoverflow.com/q/316410/776084) – RanRag

+0

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

+0

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

risposta

12

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() 
1

non sono sicuro sul supporto numpy, ma quanto segue potrebbe essere utile:

http://pypi.python.org/pypi/JCC/

+0

potrebbe essere utile, ma speravo in alcune istruzioni passo-passo usando un wrapper di livello superiore. In teoria si dovrebbe 1) chiamare java da C++ e quindi 2) chiamare C++ da python. – Mannaggia

+0

inoltre, la documentazione di JCC è orribile (o dovrei dire assente?), Non ho avuto alcun indizio su come usarlo ... – Mannaggia

2

Ritengo Jython di essere una delle migliori opzioni - che lo rende senza soluzione di continuità per usare oggetti Java in Python.In realtà ho integrato weka con i miei programmi python, ed è stato semplicissimo. Basta importare le classi weka e chiamarle come faresti in Java all'interno del codice Python.

http://www.jython.org/

+1

sì, lo so. Ma il problema con jython è che sfortunatamente non supporta numpy e molte altre librerie python per il calcolo scientifico. – Mannaggia

Problemi correlati