¿Cómo llamar a una función java desde python/numpy?

tengo claro cómo extender Python con C++, pero ¿qué pasa si quiero escribir una función en Java para usarla con 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 recopilo el resultado?

Gracias por cualquier ayuda!

preguntado el 22 de mayo de 12 a las 19:05

bueno, realmente no estoy buscando un clon, ya que tengo bastante código numpy y encuentro numpy muy bueno. Es una pena que no haya una forma directa de usar numpy con Java... -

¿La base del código Java es lo suficientemente significativa en tamaño como para impedirle simplemente volver a escribir las secciones sensibles al rendimiento en Cython y usar numpy/python para el resto? -

es más una decisión estratégica, no tengo mucho código Java en este momento. Mucha gente usa C++ para computación científica, pero tengo la sensación de que Java podría reemplazar a C++ para esto en el futuro. Entonces, idealmente, python/numpy debería poder usar las clases C ++ y Java (como matlab). Si la recolección de basura es realmente un problema, uno extendería python con C ++; de lo contrario, Java sería mejor:

No he visto ninguna tendencia hacia la adopción generalizada de Java en la comunidad científica, y dado que parece que no tienes mucho código para convertir, probablemente sea mejor usar herramientas establecidas como Cython, en lugar de meterse en aguas inexploradas y sin soporte con Java. La decisión estratégica es más probable que sea pragmática para usar las tecnologías que están disponibles hoy. -

3 Respuestas

Dediqué un tiempo a 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 se volverá más relevante en la computación científica (por ejemplo, vea el paquete WEKA para la minería de datos) debido a la mejora del rendimiento y otras buenas funciones de desarrollo de software de Java.


En general, resulta que usando las herramientas adecuadas es mucho más fácil extender Python con Java que con C/C++.


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

  • http://pypi.python.org/pypi/JCC: debido a la falta de documentación adecuada, esta herramienta es inútil.

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

  • JPype: aunque el desarrollo parece ser la muerte, funciona bien y hay muchos ejemplos en la web (por ejemplo, ver http://kogs-www.informatik.uni-hamburg.de/~meine/weka-python/ para usar bibliotecas de minería de datos escritas en Java). Por lo tanto Decidí centrarme en esta herramienta..

Instalación de JPype en Fedora 16

Estoy usando Fedora 16, ya que hay algunos problemas al instalar JPype en Linux, describo mi enfoque. Descargar JPype, luego modifique configuración.py script proporcionando la ruta JDK, en la línea 48:

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

entonces corre:

sudo python setup.py install

Después de una instalación exitosa, verifique este archivo:

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

y eliminar o cambiar el nombre del método getDefaultJVMPath() dentro getDefaultJVMPath_old(), luego agregue el siguiente método:

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

Enfoque alternativo: no realice ningún cambio en el archivo anterior _linux.py, pero nunca use el método getDefaultJVMPath() (o métodos que llamen a este método). En el lugar de uso getDefaultJVMPath() proporcionar directamente la ruta a la JVM. Tenga en cuenta que hay varias rutas, por ejemplo, en mi sistema también tengo las siguientes rutas, que se refieren a diferentes versiones de la JVM (no me queda claro si la JVM del cliente o del servidor es más adecuada):

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

Finalmente, agregue la siguiente línea a ~ / .bashrc (o ejecútelo cada vez antes de abrir un intérprete de python):

export JAVA_HOME='/usr/java/default'

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

Tenga en cuenta que todas las pruebas en el directorio donde se ha descargado JPype, es decir JPype-0.5.4.2/prueba/pruebasuite.py fallará (así que no te preocupes por ellos).

Para ver si funciona, prueba 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() 

Llamar a clases de Java desde Java también usando Numpy

Comencemos a implementar una clase de Java que contiene algunas funciones a las que quiero aplicar matrices numpy. Como no existe el concepto de estado, utilizo funciones estáticas para no necesitar 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 buscamos calcular el promedio de un vector numpy (usando el método computar_promedio), sino también multiplicar un vector numpy por un escalar (método multiplicar), y finalmente, la multiplicación de matrices (método mult_mat).

Después de compilar la clase de Java anterior, ahora podemos ejecutar el siguiente script 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() 

Respondido el 03 de junio de 12 a las 19:06

Considero que Jython es una de las mejores opciones, lo que facilita el uso de objetos Java en Python. De hecho, integré weka con mis programas de python, y fue muy fácil. Simplemente importe las clases weka y llámelas como lo haría en java dentro del código python.

http://www.jython.org/

Respondido el 13 de Septiembre de 12 a las 21:09

sí, lo sé. Pero el problema con jython es que, desafortunadamente, no admite numpy tan bien como muchas otras bibliotecas de python para computación científica. - maldito

No estoy seguro acerca del soporte numpy, pero lo siguiente podría ser útil:

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

contestado el 22 de mayo de 12 a las 19:05

podría ser útil, pero esperaba algunas instrucciones paso a paso usando algún contenedor de nivel superior. En teoría, uno debería 1) llamar a java desde C++ y luego 2) llamar a C++ desde python. - maldito

además, la documentación de JCC es horrible (¿o debería decir inexistente?), no obtuve ninguna pista sobre cómo usarlo... - maldito

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.