【问题标题】:How to call a java function from python/numpy?如何从 python/numpy 调用 java 函数?
【发布时间】:2012-05-22 18:14:08
【问题描述】:

我很清楚如何用 C++ 扩展 Python,但是如果我想用 Java 编写一个函数来与 numpy 一起使用呢?

这是一个简单的场景:我想使用 Java 类计算 numpy 数组的平均值。如何将 numpy 向量传递给 Java 类并收集结果?

感谢您的帮助!

【问题讨论】:

  • 好吧,我并不是真的在寻找克隆,因为我有一些 numpy 代码,而且我发现 numpy 非常好。可惜没有直接的方法可以将 numpy 与 Java 一起使用...
  • Java 代码库的大小是否足够重要,以至于您无法仅在 Cython 中重写性能敏感部分,而其余部分使用 numpy/python?
  • 这更像是一个战略决策,我现在没有很多Java代码。很多人使用 C++ 进行科学计算,但我感觉 Java 可能会在未来取代 C++。所以理想情况下,python/numpy 应该能够同时使用 C++ 和 Java 类(如 matlab)。如果垃圾收集真的是一个问题,可以用 C++ 扩展 python,否则 Java 会更好
  • 我还没有看到任何在科学界广泛采用 Java 的趋势,而且听起来你没有很多代码要转换,最好使用已建立的像 Cython 这样的工具,而不是使用 Java 涉足未知和不受支持的水域。战略决策更可能是使用当今可用技术的务实决策。

标签: java numpy


【解决方案1】:

我花了一些时间在我自己的问题上,并想分享我的答案,因为我觉得 stackoverflow 上没有太多关于这个主题的信息。我还认为,由于 Java 的性能和其他良好的软件开发特性的改进,Java 将在科学计算中变得更加相关(例如,参见 WEKA 数据挖掘包)。


总的来说,事实证明,使用正确的工具使用 Java 扩展 Python 比使用 C/C++ 更容易!


从 Python 调用 Java 的工具概述和评估

  • http://pypi.python.org/pypi/JCC: 因为没有合适的 文档这个工具没用。

  • Py4J:需要在使用 python 之前启动 Java 进程。作为 其他人指出,这是一个可能的故障点。此外,记录的使用示例并不多。

  • JPype: 发展虽然看似死亡,但运作良好,有 网上有很多关于它的示例(例如,请参阅http://kogs-www.informatik.uni-hamburg.de/~meine/weka-python/ 以了解使用 Java 编写的数据挖掘库)。因此我决定专注于 在这个工具上

在 Fedora 16 上安装 JPype

我正在使用 Fedora 16,因为在 Linux 上安装 JPype 时存在一些问题,我描述了我的方法。 下载JPype,然后通过提供JDK路径来修改setup.py脚本,在第48行:

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 更适合):

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

最后,在~/.bashrc中加入下面一行(或者每次打开python解释器前运行):

export JAVA_HOME='/usr/java/default'

(上面的目录实际上只是我上一个版本的JDK的符号链接,它位于/usr/java/jdk1.7.0_04)。

请注意,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 从 Java 调用 Java 类

让我们开始实现一个Java 类,其中包含一些我想应用于numpy 数组 的函数。由于没有状态的概念,我使用静态函数,这样我就不需要创建任何 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() 

【讨论】:

    【解决方案2】:

    我认为 Jython 是最好的选择之一——这使得在 python 中使用 java 对象变得无缝。我实际上将 weka 与我的 python 程序集成在一起,而且非常简单。只需导入 weka 类并在 python 代码中调用它们,就像在 java 中一样。

    http://www.jython.org/

    【讨论】:

    • 是的,我知道。但是 jython 的问题在于它不支持 numpy 以及许多其他用于科学计算的 python 库。
    【解决方案3】:

    我不确定 numpy 是否支持,但以下内容可能会有所帮助:

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

    【讨论】:

    • 这可能会有所帮助,但我希望在一些分步说明中使用一些更高级别的包装器。理论上应该1)从C++调用java,然后2)从python调用C++。
    • 另外,JCC 的文档很糟糕(或者我应该说没有?),我没有任何关于如何使用它的线索......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 2010-10-23
    • 2019-10-24
    • 1970-01-01
    • 2016-12-23
    相关资源
    最近更新 更多