2012-05-22 15 views
6

es claro para mí cómo extender Python con C++, pero ¿qué ocurre si quiero escribir una función en Java para usar con numpy?¿Cómo llamar a una función java de python/numpy?

Aquí hay un escenario simple: quiero calcular el promedio de una matriz numpy usando una clase Java. ¿Cómo paso el vector numpy a la clase Java y reúno el resultado?

¡Gracias por cualquier ayuda!

+0

Tenga una mirada en [es-no-un-buen-numpy-clon-de-jython] (http://stackoverflow.com/q/316410/776084) – RanRag

+0

así, no estoy Realmente estoy buscando un clon, ya que tengo bastante código numpy y me parece muy bueno. Es una pena que no haya una forma directa de usar numpy con Java ... – Mannaggia

+0

¿La base de código Java es lo suficientemente significativa como para evitar que simplemente vuelva a escribir las secciones sensibles al rendimiento en Cython y use numpy/python para el ¿descanso? – JoshAdel

Respuesta

12

pasé algún tiempo en mi propia pregunta y me gustaría compartir mi respuesta ya que siento que no hay mucha información sobre este tema en stackoverflow. También creo que Java será más relevante en informática científica (por ejemplo, consulte el paquete WEKA para minería de datos) debido a la mejora del rendimiento y otras buenas características de desarrollo de software de Java.


En general, resulta que el uso de las herramientas adecuadas es mucho más fácil de extender con Java Python que con C/C++!


Descripción y evaluación de las herramientas para llamar a Java desde Python

  • http://pypi.python.org/pypi/JCC: por falta de documentación apropiada esta herramienta no sirve para nada.

  • Py4J: requiere iniciar el proceso de Java antes de usar python. Como señalado por otros, este es un posible punto de falla. Además, no se documentan muchos ejemplos de uso.

  • JPype: si bien el desarrollo parece ser la muerte, funciona bien y hay muchos ejemplos en él en la web (por ejemplo, ver http://kogs-www.informatik.uni-hamburg.de/~meine/weka-python/ para el uso de las bibliotecas de minería de datos escritos en Java). Por lo tanto, decidí enfocar en esta herramienta.

JPype la instalación en Fedora 16

estoy usando Fedora 16, ya que hay algunos problemas al instalar JPype en Linux, describo mi método. Descargar JPype, a continuación, modificar setup.py guión, proporcionando la ruta del JDK, en la línea 48:

self.javaHome = '/usr/java/default' 

continuación, ejecute:

sudo python setup.py install 

Afters instalación correcta, compruebe este archivo:

/usr/lib64/python2.7/site-packages/jpype/_linux.py

y eliminar o cambiar el nombre del método getDefaultJVMPath() en getDefaultJVMPath_old(), a continuación, añadir el siguiente método:

def getDefaultJVMPath(): 
    return "/usr/java/default/jre/lib/amd64/server/libjvm.so" 

enfoque alternativo: no hacer ningún cambio en el anterior archivo _linux.py, pero nunca use el método getDefaultJVMPath() (o métodos que llaman a este método). En el lugar donde se usa getDefaultJVMPath(), proporcione directamente la ruta a la JVM. Tenga en cuenta que hay varios caminos, por ejemplo en mi sistema también tengo las siguientes rutas, en referencia a las diferentes versiones de la JVM (no me queda claro si la JVM de cliente o servidor se adapta mejor):

  • /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

Por último, agregue la siguiente línea al ~/.bashrc (o ejecutarlo cada vez que antes de abrir un intérprete de Python):

export JAVA_HOME='/usr/java/default' 

(El directorio anterior es en realidad sólo un enlace simbólico a la última versión del JDK, que se encuentra en /usr/java/jdk1.7.0_04).

Tenga en cuenta que todas las pruebas en el directorio donde JPype ha sido descargado, es decir, JPype-0.5.4.2/test/testsuite.py fallará (así que no se preocupan por ellos).

Para ver si funciona, probar este script en 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() 

llamadas clases Java de Java también utilizando Numpy

Vamos a empezar la implementación de una clase Java que contiene algunas funciones que quiero aplicar a matrices numpy. Como no existe un concepto de estado, utilizo funciones estáticas para no tener que crear ningún objeto Java (la creación de objetos Java no cambiaría nada).

/** 
* 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); 
} 
} 

El nombre de la clase es un poco engañoso, ya que no solo objetivo es calcular el promedio de un vector numpy (usando el método calcular_promedio), sino también multiplicar un vector numpy por un escalar (método multiplicar), y finalmente, la multiplicación de la matriz (método mult_mat).

Después de compilar la clase Java anterior que ahora puede ejecutar la siguiente secuencia de comandos de 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

no estoy seguro sobre el apoyo numpy, pero lo siguiente podría ser útil:

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

+0

podría ser útil, pero esperaba algunas instrucciones paso a paso utilizando un contenedor de nivel superior. En teoría, uno debería 1) llamar a Java desde C++, y luego 2) llamar a C++ desde Python. – Mannaggia

+0

además, la documentación de JCC es horrible (¿o debería decir ausente?), No obtuve ninguna pista sobre cómo usarlo ... – Mannaggia

2

considero Jython para ser una de las mejores opciones - que hace que sea transparente a utilizar objetos Java en Python.De hecho, integé Weka con mis programas Python, y fue muy fácil. Solo importa las clases weka y llámalas como lo harías en Java dentro del código python.

http://www.jython.org/

+1

sí, lo sé. Pero el problema con jython es que, desafortunadamente, no es compatible con Numpy, así como con muchas otras bibliotecas de Python para la informática científica. – Mannaggia

Cuestiones relacionadas