2012-05-22 35 views
6

rõ ràng với tôi cách mở rộng Python bằng C++, nhưng nếu tôi muốn viết một hàm trong Java để được sử dụng với numpy thì sao?Làm thế nào để gọi một hàm java từ python/numpy?

Đây là một kịch bản đơn giản: Tôi muốn tính mức trung bình của một mảng có nhiều mảng bằng cách sử dụng một lớp Java. Làm cách nào để chuyển vectơ cứng nhắc đến lớp Java và thu thập kết quả?

Cảm ơn bạn đã trợ giúp!

+0

Có một cái nhìn tại [là-có-một-tốt-NumPy-clone-cho-Jython] (http://stackoverflow.com/q/316410/776084) – RanRag

+0

tốt, tôi không thực sự tìm kiếm một bản sao, vì tôi có khá một số mã gumpy và tôi thấy gumpy rất tốt. Đó là một điều đáng tiếc không có cách trực tiếp để sử dụng gọn gàng với Java ... – Mannaggia

+0

Cơ sở mã Java có đủ kích thước để ngăn cản bạn chỉ viết lại các phần nhạy cảm về hiệu suất trong Cython và sử dụng numpy/python cho nghỉ ngơi? – JoshAdel

Trả lời

12

tôi đã dành một số thời gian trên câu hỏi của riêng tôi và muốn chia sẻ câu trả lời của tôi như là tôi cảm thấy không có nhiều thông tin về chủ đề này trên stackoverflow. Tôi cũng nghĩ Java sẽ trở nên có liên quan hơn trong tính toán khoa học (ví dụ: xem gói WEKA để khai thác dữ liệu) vì cải thiện hiệu suất và các tính năng phát triển phần mềm tốt khác của Java.


Nói chung, nó quay ra rằng việc sử dụng các công cụ thích hợp nó là dễ dàng hơn nhiều để mở rộng Python với Java so với C/C++!


Tổng quan và đánh giá các công cụ để gọi Java từ Python

  • http://pypi.python.org/pypi/JCC: vì không có tài liệu thích hợp công cụ này là vô ích.

  • Py4J: yêu cầu bắt đầu quá trình Java trước khi sử dụng python. Như nhận xét bởi những người khác, đây là một điểm có thể thất bại. Hơn nữa, không có nhiều ví dụ sử dụng được ghi lại.

  • JPype: mặc dù phát triển dường như là cái chết, nó hoạt động tốt và có nhiều ví dụ về nó trên web (ví dụ: xem http://kogs-www.informatik.uni-hamburg.de/~meine/weka-python/ cho việc sử dụng thư viện khai thác dữ liệu viết bằng Java). Do đó Tôi quyết định tập trung trên công cụ này.

JPype Cài đặt trên Fedora 16

Tôi đang sử dụng Fedora 16, vì có một số vấn đề khi cài đặt JPype trên Linux, tôi mô tả cách tiếp cận của tôi. Tải JPype, sau đó sửa đổi setup.py kịch bản bằng cách cung cấp các con đường JDK, phù hợp 48:

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

sau đó chạy:

sudo python setup.py install 

Afters cài đặt thành công, kiểm tra tập tin này:

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

và loại bỏ hoặc đổi tên các phương pháp getDefaultJVMPath() vào getDefaultJVMPath_old(), sau đó thêm các phương pháp sau đây:

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

Alternative tiếp cận: không thực hiện bất kỳ thay đổi ở trên tệp _linux.py, nhưng không bao giờ sử dụng phương thức getDefaultJVMPath() (hoặc phương thức gọi phương thức này). Tại nơi sử dụng getDefaultJVMPath() cung cấp trực tiếp đường dẫn đến JVM. Lưu ý rằng có một số con đường, ví dụ như trong hệ thống của tôi, tôi cũng có các đường dẫn sau đây, đề cập đến các phiên bản khác nhau của JVM (nó không phải là rõ ràng với tôi cho dù khách hàng hoặc máy chủ JVM là phù hợp hơn):

  • /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/máy chủ/libjvm.so
  • /usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0.x86_64/jre/lib/amd64/server/libjvm.so

Cuối cùng, thêm dòng sau vào ~/.bashrc (hoặc chạy nó mỗi lần trước khi mở một thông dịch viên python):

export JAVA_HOME='/usr/java/default' 

(Thư mục trên là trong thực tế chỉ là một liên kết tượng trưng đến phiên bản cuối cùng của tôi về JDK, mà nằm ở /usr/java/jdk1.7.0_04).

Lưu ý rằng tất cả các thử nghiệm trong thư mục mà JPype đã được tải xuống, tức là JPype-0.5.4.2/test/testsuite.py sẽ không thành công (do đó, không quan tâm đến chúng).

Để xem nếu nó hoạt động, kiểm tra kịch bản này trong 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 lớp Java từ Java cũng sử dụng NumPy

Hãy bắt đầu thực hiện một lớp Java có chứa một số chức năng mà tôi muốn áp dụng cho mảng numpy. Vì không có khái niệm trạng thái, tôi sử dụng các hàm tĩnh để không cần phải tạo bất kỳ đối tượng Java nào (việc tạo các đối tượng Java sẽ không thay đổi bất kỳ thứ gì).

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

Tên của lớp là một chút sai lầm, như chúng ta không chỉ nhằm mục đích tính toán trung bình của một vector NumPy (sử dụng phương pháp này compute_average), mà còn nhân lên một vector NumPy bởi một vô hướng (phương pháp nhân) và cuối cùng, nhân ma trận (phương pháp mult_mat).

Sau khi biên dịch lớp Java trên bây giờ chúng tôi có thể chạy các script Python sau:

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

Tôi không chắc chắn về việc hỗ trợ NumPy, nhưng những điều sau đây có thể hữu ích:

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

+0

nó có thể hữu ích, nhưng tôi đã hy vọng trong một số hướng dẫn từng bước bằng cách sử dụng một số wrapper cấp cao hơn. Trong lý thuyết ta nên 1) gọi java từ C++, và sau đó 2) gọi C++ từ python. – Mannaggia

+0

ngoài ra, các tài liệu của JCC là khủng khiếp (hoặc tôi nên nói vắng mặt?), Tôi đã không nhận được bất kỳ đầu mối về cách sử dụng nó ... – Mannaggia

2

tôi xem xét Jython là một trong những lựa chọn tốt nhất - mà làm cho nó liền mạch sử dụng đối tượng java trong python.Tôi thực sự tích hợp weka với các chương trình python của tôi, và nó rất dễ dàng. Chỉ cần nhập các lớp weka và gọi chúng như bạn làm trong java trong mã python.

http://www.jython.org/

+1

vâng, tôi biết. Nhưng vấn đề với jython là tiếc là nó không hỗ trợ numpy cũng như nhiều thư viện python khác cho máy tính khoa học. – Mannaggia

Các vấn đề liên quan