Skip to content

Java¤

Since Java byte code is executed in a JVM it doesn't matter whether it was built with a 32- or 64-bit Java Development Kit. The Python interpreter does not load the Java byte code but communicates with the JVM through a local network socket that is managed by Py4J.

Example (.jar)¤

Load the example Java archive, java_lib.jar

>>> from msl.loadlib import LoadLibrary
>>> from msl.examples.loadlib import EXAMPLES_DIR
>>> jar = LoadLibrary(EXAMPLES_DIR / "java_lib.jar")
>>> jar
<LoadLibrary libtype=JVMView path=...java_lib.jar>
>>> jar.gateway
<py4j.java_gateway.JavaGateway object at ...>

The Java archive contains a nz.msl.examples package with two classes, MathUtils and Matrix

>>> MathUtils = jar.lib.nz.msl.examples.MathUtils
>>> Matrix = jar.lib.nz.msl.examples.Matrix

Calculate the square root of a number using the MathUtils class

>>> MathUtils.sqrt(32.4)
5.692099788303...

Solve a linear system of equations, Ax=b, using the Matrix library class and the gateway object to allocate memory for the A and b arrays

>>> A = jar.gateway.new_array(jar.lib.Double, 3, 3)
>>> coefficients = [[3, 2, -1], [7, -2, 4], [-1, 5, 1]]
>>> for i in range(3):
...     for j in range(3):
...         A[i][j] = float(coefficients[i][j])
...
>>> b = jar.gateway.new_array(jar.lib.Double, 3)
>>> b[0] = 4.0
>>> b[1] = 15.0
>>> b[2] = 12.0
>>> x = Matrix.solve(Matrix(A), Matrix(b))
>>> print(x.toString())
+1.000000e+00
+2.000000e+00
+3.000000e+00

Verify that x is the solution

>>> for i in range(3):
...     x_i = 0.0
...     for j in range(3):
...         x_i += coefficients[i][j] * x.getValue(j,0)
...     assert abs(x_i - b[i]) < 1e-12
...

Shutdown the connection to the JVM when finished

>>> jar.gateway.shutdown()

Example (.class)¤

Load the example Java byte code, Trig.class

>>> cls = LoadLibrary(EXAMPLES_DIR / "Trig.class")
>>> cls
<LoadLibrary libtype=JVMView path=...Trig.class>
>>> cls.lib
<py4j.java_gateway.JVMView object at ...>

The Java library contains a Trig class, which calculates various trigonometric quantities

>>> Trig = cls.lib.Trig
>>> Trig
<py4j.java_gateway.JavaClass object at ...>
>>> Trig.cos(1.2)
0.3623577544766...
>>> Trig.asin(0.6)
0.6435011087932...
>>> Trig.tanh(1.3)
0.8617231593133...

Shutdown the connection to the JVM when finished

>>> cls.gateway.shutdown()

Java Source Code¤

java_lib.jar
package nz.msl.examples;

public class MathUtils {

  /** Generate a random number between [0, 1) */
  static public double random() {
    return Math.random();
  }

  /** Calculate the square root of {@code x} */
  static public double sqrt(double x) {
    return Math.sqrt(x);
  }

}
package nz.msl.examples;

import java.util.Random;


public class Matrix {

  /** The matrix, M */
  private double[][] m;

  /** Lower-triangular matrix representation, M=LU, in LU Decomposition */
  private Matrix L;

  /** Upper-triangular matrix representation, M=LU, in LU Decomposition */
  private Matrix U;

  /** A NxM orthogonal matrix representation, M=QR, in QR Decomposition */
  private Matrix Q;

  /** Upper-triangular matrix representation, M=QR, in QR Decomposition */
  private Matrix R;

  /** When calculating the inverse we calculate the LU matrices once */
  static private boolean calculatingInverse = false;

  /*
   * 
   * Define the constructors.
   * 
   *    
   */

  /** Create a Matrix that is a copy of another Matrix. */
  public Matrix(Matrix m) {
    this.m = new double[m.getNumberOfRows()][m.getNumberOfColumns()];
    for (int i=0; i<m.getNumberOfRows(); i++)
      for (int j=0; j<m.getNumberOfColumns(); j++)
        this.m[i][j] = m.getValue(i,j);
  }

  /** Create a {@code n} x {@code n} identity Matrix */
  public Matrix(int n) {
    m = new double[n][n];
    for (int i=0; i<n; i++) 
      m[i][i] = 1.0;
  }

  /** Create a {@code rows} x {@code cols} Matrix filled with zeros. */
  public Matrix(int rows, int cols) {
    m = new double[rows][cols];
  }

  /** Create a {@code rows} x {@code cols} Matrix filled with a value. */
  public Matrix(int rows, int cols, double value) {
    m = new double[rows][cols];
    for (int i=0; i<rows; i++)
      for (int j=0; j<cols; j++)
        m[i][j] = value;
  }

  /**
   * Create a {@code rows} x {@code cols} Matrix that is filled with 
   * uniformly-distributed random values that are within the range 
   * {@code min} to {@code max}.
   */
  public Matrix(int rows, int cols, double min, double max) {
    Random rand = new Random();
    m = new double[rows][cols];    
    for (int i=0; i<rows; i++)
      for (int j=0; j<cols; j++)        
        m[i][j] = (max-min)*rand.nextDouble()+min;
  }

  /** Create a Matrix from {@code m}. */
  public Matrix(Double[][] m) {    
    this.m = new double[m.length][m[0].length];
    for (int i=0; i<m.length; i++)
      for (int j=0; j<m[0].length; j++)        
        this.m[i][j] = m[i][j];
  }

  /** Create a Matrix from a vector. */
  public Matrix(Double[] vector) {
    m = new double[1][vector.length];
    for (int i=0; i<vector.length; i++)
      m[0][i] = vector[i];    
  }  

  /*
   * 
   * The public static methods.
   *
   * 
   */

  /** Returns the product of two Matrices as a new Matrix, C=AB. */
  public static Matrix multiply(Matrix a, Matrix b) {    
    if (a.getNumberOfColumns() != b.getNumberOfRows()) {
      throw new IllegalArgumentException(
          String.format("ERROR! Cannot multiply a %dx%d matrix "
            + "with a %dx%d matrix",
            a.getNumberOfRows(), a.getNumberOfColumns(),
            b.getNumberOfRows(), b.getNumberOfColumns()));
    } else {
      Matrix c = new Matrix(a.getNumberOfRows(), b.getNumberOfColumns());
      double sum = 0.0;
      for (int i = 0; i < a.getNumberOfRows() ; i++) {
        for (int j = 0; j < b.getNumberOfColumns(); j++) {
                 for (int k = 0 ; k < b.getNumberOfRows() ; k++) {
                    sum += a.getValue(i,k)*b.getValue(k,j);
                 }
                 c.setValue(i, j, sum);
                 sum = 0.0;
              }
           }
      return c;
    }
  }

  /** 
   * Solves {@code b = Ax} for {@code x}.
   * 
   * @param A - the coefficient matrix
   * @param b - the expected values
   * @return x - the solution to the system of equations
   */
  public static Matrix solve(Matrix A, Matrix b) {

    // ensure that 'b' is a column vector
    if (b.getNumberOfColumns() > 1)  b = b.transpose();

    // ensure that 'A' and 'b' have the correct dimensions
    if (b.getNumberOfRows() != A.getNumberOfRows()) {
      throw new IllegalArgumentException(
        String.format("ERROR! Dimension mismatch when solving the "
          + "system of equations using b=Ax, b has dimension "
          + " %dx%d and A is %dx%d.", b.getNumberOfRows(), 
          b.getNumberOfColumns(), A.getNumberOfRows(),
          A.getNumberOfColumns()));
    }

    // if A is an under-determined system of equations then use the 
    // matrix-multiplication expression to solve for x
    if (A.getNumberOfRows() < A.getNumberOfColumns()) {
      Matrix At = A.transpose();
      return Matrix.multiply(Matrix.multiply(At, 
          Matrix.multiply(A, At).getInverse() ), b);
    }

    // If A is a square matrix then use LU Decomposition, if it is an 
    // over-determined system of equations then use QR Decomposition
    Double[] x = new Double[A.getNumberOfColumns()];
    if (A.isSquare()) {

      // when using 'solve' to calculate the inverse of a matrix we
      // only need to generate the LU Decomposition matrices once
      if (!calculatingInverse) A.makeLU();

      // solve Ly=b for y using forward substitution
      double[] y = new double[b.getNumberOfRows()];
      y[0] = b.getValue(0,0);
      for (int i=1; i<y.length; i++) {
        y[i] = b.getValue(i,0);
        for (int j=0; j<i; j++)
          y[i] -= A.getL().getValue(i,j)*y[j];
      }

      // solve Ux=y for x using backward substitution
      for (int i=x.length-1; i>-1; i--) {
        x[i] = y[i];
        for (int j=i+1; j<x.length; j++)
          x[i] -= A.getU().getValue(i,j)*x[j];
        x[i] /= A.getU().getValue(i,i);
      }

    } else {

      A.makeQR();
      Matrix d = Matrix.multiply(A.getQ().transpose(), b);

      // solve Rx=d for x using backward substitution
      for (int i=x.length-1; i>-1; i--) {
        x[i] = d.getValue(i, 0);
        for (int j=i+1; j<x.length; j++)
          x[i] -= A.getR().getValue(i,j)*x[j];
        x[i] /= A.getR().getValue(i,i);
      }      
    }    

    return new Matrix(x).transpose();
  }

  /*
   * 
   * The public methods.
   *
   * 
   */

  /** Returns the primitive data of the Matrix. */
  public double[][] primitive() {
    return m;
  }

  /** Convert the Matrix to a string. */
  @Override
  public String toString() {
    StringBuffer sb = new StringBuffer();
    for (int i=0; i<m.length; i++) {
      for (int j=0; j<m[0].length; j++) {
        sb.append(String.format("%+.6e\t", m[i][j]));
      }
      sb.append("\n");
    }
    return sb.toString();
  }

  /** Returns the number of rows in the Matrix. */
  public int getNumberOfRows() {
    return m.length;
  }

  /** Returns the number of columns in the Matrix. */
  public int getNumberOfColumns() {
    try {
      return m[0].length; 
    } catch (ArrayIndexOutOfBoundsException e) {
      return 0;
    }
  }

  /** Returns the value at {@code row} and {@code col}. */
  public double getValue(int row, int col) {
    return m[row][col];
  }

  /** Sets the value at {@code row} and {@code col} to be {@code value}. */
  public void setValue(int row, int col, double value) {
    m[row][col] = value;
  }  

  /** Returns the transpose of the Matrix. */
  public Matrix transpose() {
    Matrix mt = new Matrix(m[0].length, m.length);
    for (int i=0; i<m.length; i++)
      for (int j=0; j<m[0].length; j++)
        mt.setValue(j, i, m[i][j]);
    return mt;
  }

  /** Returns whether the Matrix is a square Matrix. */
  public boolean isSquare() {
    return m.length == m[0].length;
  }

  /** Returns the determinant of the Matrix. */
  public double getDeterminant() {
    if (isSquare()) {
      makeLU();
      double det = 1.0;
      for (int i=0; i<m.length; i++)
        det *= U.getValue(i,i);
      // 's' is the number of row and column exchanges in LU Decomposition
      // but we are currently not using pivoting
      int s = 0;
      return Math.pow(-1.0, s)*det;
    } else {
      return Double.NaN;
    }
  }

  /** Returns the lower-triangular Matrix, L, from a LU Decomposition */
  public Matrix getL() {
    if (L==null) makeLU();
    return L;
  }

  /** Returns the upper-triangular Matrix, U, from a LU Decomposition */
  public Matrix getU() {
    if (U==null) makeLU();
    return U;
  }

  /** Returns the orthogonal Matrix, Q, from a QR Decomposition */
  public Matrix getQ() {
    if (Q==null) makeQR();
    return Q;
  }

  /** Returns the upper-triangular Matrix, R, from a QR Decomposition */
  public Matrix getR() {
    if (R==null) makeQR();
    return R;
  }

  /** Returns the inverse of the Matrix, if it exists. */
  public Matrix getInverse() {
    if (isSquare()) {
      Matrix inv = new Matrix(m.length);
      Matrix bb = new Matrix(m.length);
      for (int i=0; i<m.length; i++) {
        inv.setColumn(i, Matrix.solve(this, bb.getColumn(i)));
        calculatingInverse = true;
      }
      calculatingInverse = false;
      return inv;
    } else {
      throw new IllegalArgumentException(
        String.format("ERROR! Cannot calculate the inverse of a "
          + "%dx%d matrix, it must be a square Matrix", 
          m.length, m[0].length));
    }
  }


  /*
   * 
   * Private methods.
   * 
   * 
   */

  /** 
   * Create the Lower, L, and Upper, U, triangular matrices, such that M=LU.
   * Does not use pivoting. 
   */
  private void makeLU() {
    L = new Matrix(m.length); // create an identity matrix
    U = new Matrix(this); // copy the values of this matrix
    double val;
    for (int k=0; k<m[0].length; k++) {
      for (int i=k+1; i<m.length; i++) {
        val = U.getValue(i,k)/U.getValue(k,k);
        L.setValue(i, k, val);
        for (int j=k; j<m[0].length; j++)
          U.setValue(i, j, U.getValue(i,j)-val*U.getValue(k,j));
      }
    }    
  }

  /** 
   * Computes the QR Factorization matrices using a modified 
   * Gram–Schmidt process.<p>
   * 
   * @see https://people.inf.ethz.ch/gander/papers/qrneu.pdf
   */
  private void makeQR() {

    Q = new Matrix(m.length, m[0].length);
    R = new Matrix(m[0].length, m[0].length);
    Matrix A = new Matrix(this);

    double s;
    for (int k=0; k<m[0].length; k++) {
      s = 0.0;
      for (int j=0; j<m.length; j++)
        s += Math.pow(A.getValue(j, k), 2);
      s = Math.sqrt(s);
      R.setValue(k, k, s);
      for (int j=0; j<m.length; j++)
        Q.setValue(j, k, A.getValue(j, k)/s);
      for (int i=k+1; i<m[0].length; i++) {
        s = 0.0;
        for (int j=0; j<m.length; j++)
          s += A.getValue(j, i)*Q.getValue(j, k);
        R.setValue(k, i, s);
        for (int j=0; j<m.length; j++)
          A.setValue(j, i, A.getValue(j,i)-R.getValue(k,i)*Q.getValue(j,k));
      }
    }
  }

  /** Returns a copy of the specified column. */
  private Matrix getColumn(int column) {
    if (column < m[0].length) {
      Matrix c = new Matrix(m.length, 1);
      for (int i=0; i<m.length; i++)
        c.setValue(i, 0, m[i][column]);
      return c;
    } else {
      throw new IllegalArgumentException(
        String.format("ERROR! Cannot get column %d in the Matrix "
          + "since it is > the number of columns in the "
          + "Matrix, %d.", column, m[0].length));
    }
  }

  /** 
   * Replace the values in the specified column of the matrix to the values in
   * {@code vector}.
   *  
   * The {@code vector} must be a 1D vector, can have dimension 1xN or Nx1.
   */
  private void setColumn(int column, Matrix vector) {

    // make sure that 'vector' is either a 1xN or Nx1 vector and not a NxM Matrix
    if ( (vector.getNumberOfColumns() != 1) && (vector.getNumberOfRows() != 1) ) {
      throw new IllegalArgumentException(
        String.format("ERROR! Require a 1D vector to replace the values "
          + "in a column of a matrix. Got a %dx%d vector.", 
          vector.getNumberOfRows(), vector.getNumberOfColumns()));
    }

    // make sure we have a column vector
    if (vector.getNumberOfColumns() != 1) {
      vector = vector.transpose();
    }

    // make sure the 'vector' has the correct length
    if (vector.getNumberOfRows() != m.length) {
      throw new IllegalArgumentException(
        String.format("ERROR! Cannot replace a Matrix column of length "
          + "%d, with a column vector of length %d.",
          m.length, vector.getNumberOfRows()));
    }

    // make sure the column is valid
    if (column >= m[0].length) {
      throw new IllegalArgumentException(
        String.format("ERROR! Cannot replace column %d in the Matrix "
        + "since it is > the number of columns in the matrix.", column));
    }

    for (int i=0; i<m.length; i++)
      m[i][column] = vector.getValue(i,0);
  }

}
Trig.class
/*
 * Compile with JDK 6 for maximal compatibility with Py4J
 * 
 * javac Trig.java
 *
 */

public class Trig {

  /** Returns the trigonometric cosine of an angle. */
  static public double cos(double x) {
    return Math.cos(x);
  }

  /** Returns the hyperbolic cosine of a value. */
  static public double cosh(double x) {
    return Math.cosh(x);
  }

  /** Returns the arc cosine of a value, [0.0, pi]. */
  static public double acos(double x) {
    return Math.acos(x);
  }

  /** Returns the trigonometric sine of an angle. */
  static public double sin(double x) {
    return Math.sin(x);
  }

  /** Returns the hyperbolic sine of a value. */
  static public double sinh(double x) {
    return Math.sinh(x);
  }

  /** Returns the arc sine of a value, [-pi/2, pi/2]. */
  static public double asin(double x) {
    return Math.asin(x);
  }

  /** Returns the trigonometric tangent of an angle. */
  static public double tan(double x) {
    return Math.tan(x);
  }

  /** Returns the hyperbolic tangent of a value. */
  static public double tanh(double x) {
    return Math.tanh(x);
  }

  /** Returns the arc tangent of a value; [-pi/2, pi/2]. */
  static public double atan(double x) {
    return Math.atan(x);
  }

  /** 
   * Returns the angle theta from the conversion of rectangular coordinates 
   * (x, y) to polar coordinates (r, theta).
   */
  static public double atan2(double y, double x) {
    return Math.atan2(y, x);
  }

}