Introducing the Wolfram Client Library for Java

Todd Gayley
Wolfram Research

Join the Conversation #WolframTechConf

ABSTRACT

The new Wolfram Client Library for Java provides a very simple, high-level way to call the Wolfram Engine, whether it is running in the cloud or locally. You can call Wolfram Language functions or Wolfram Cloud APIs using an easy interface and get back results in a variety of convenient formats. Your Java programs can target a local installation of the Wolfram Engine (i.e. Mathematica or Wolfram Desktop), the Wolfram Cloud, an Enterprise Private Cloud, or a local or remote LinkServer, all with essentially the exact same Java code. This means that Java programmers can create programs that use the capabilities of the Wolfram Engine and distribute them to users who do not have Mathematica, Wolfram Desktop, or any other Wolfram products installed. If you are familiar with J/Link, the Wolfram Client Library sits on top, relying internally on J/Link for some of its capabilities, but you don’t need to know anything about J/Link to use it. This library is a supported product of Wolfram Research, but is open source and will soon be hosted on GitHub, so users can examine the code and contribute to its development.

Relation to WolframScript

Relation to J/Link

The Wolfram Client Library is in no way a replacement for J/Link

It sits on top of J/Link, relying on J/Link for much of its functionality when calling kernels over WSTP (e.g., a local kernel)

It integrates fully with J/Link in that if you use J/Link to get a link to a kernel, you can use that link as a target for calls made by the Wolfram Client Library

For users who don’t need the advanced features of J/Link, the Wolfram Client Library is easier to use

The Wolfram Client Library provides many features that are not in J/Link, like calling kernels in the cloud, either for APIs or direct computations

An Open Source Project

To be made available on GitHub

We hope that others will contribute to the library

Client Libraries for Multiple Languages

Python and Java are under development at the moment

Others will come in the future

We hope that the developer community will contribute libraries for other languages

General Design Principles

Target independence

Make it easy to send and receive native Java types

Encapsulate result, errors, and metadata

Don’t throw checked exceptions

Synchronous and asynchronous execution

Small and simple API

The Three Main Classes

WolframCall

used to evaluate arbitrary code, expressions, or functions

WolframAPICall

used to evaluate Cloud APIs

WolframSession

represents a persistent kernel session; used to create calls of the above two types

WolframCall

Evaluating Arbitrary Wolfram Language Code and Functions

import com.wolfram.client.*;

public class SampleWolframCall {

    public static void main(String[] args) {

        WolframCall call = new WolframCall().
                               setInput(“15!”);
                               
        WolframCallResult callResult = call.perform();
        
        if (callResult.isSuccess()) {
            String res = (String) callResult.getResult();
            System.out.println(res);
        } else {
            System.out.println(“error in call: “ + callResult.getException());
        }
    }
}

WolframCall

Changing Targets

WolframCall call = new WolframCall().
                         setInput(“15!”).
                         setTarget("https://www.wolframcloud.com");
                               
  WolframCallResult callResult = call.perform();
        
  if (callResult.isSuccess()) {
      String res = (String) callResult.getResult();
      System.out.println(res);
  } else {
      System.out.println(“Wolfram call error: “ + callResult.getException());
  }

WolframCall

Some Possible Targets

path to a local kernel

J/Link KernelLink object

Cloud base URL to the Wolfram Cloud or a private cloud

WolframAPICall

Calling Cloud APIs

Targets

Public Wolfram Cloud

Wolfram Enterprise Private Cloud

Future products...

Calling Cloud APIs

api = CloudDeploy[
           APIFunction[{"x"->Integer}, #x^2&], Permissions->"Public"]

WolframAPICall call = new WolframAPICall().
                            setUUID("45bd63c5-a64a-409b-afe3-2ea3df73aedb").
                            addArgument("x", 42);
                                  
  WolframCallResult callResult = call.perform();
        
  if (callResult.isSuccess()) {
     String res = (String) callResult.getResult();
     System.out.println(res);
  } else {
     System.out.println(“Wolfram API call error: “ + callResult.getException());
  }

Calling Cloud APIs

Argument Type Flexibility

api = CloudDeploy[
APIFunction[{"w"->Integer,"x"->Integer, "y"->Integer, "z"->Integer}, #w #x #y #z&], "myapis/argtypes", Permissions->"Public"]

WolframAPICall call =
      new WolframAPICall().
              setEndpoint("objects/tgayley/myapis/argtypes").
              addArgument("w", 42).
              addArgument("x", "43").
              addArgument("y", new Integer(44)).
              addArgument("z", new Expr(45)).
              setResultType(ResultType.Integer);
                                  
  WolframCallResult callResult = call.perform();
        
  if (callResult.isSuccess()) {
      Integer res = (Integer) callResult.getResult();
      System.out.println(res);
  } else {
      System.out.println("Wolfram API call error:" + callResult.getException());
  }

Calling Cloud APIs

ResultType Specification

public enum ResultType {
        Void,           // Not interested in reading the output, might be none
        Expr,
        String,
        Binary,         // byte[]
        Image,          // java.awt.image.BufferedImage 
        Integer,        // int
        Long,           // long
        Double,         // double
        IntegerArray1,  // int[]
        DoubleArray1,   // double[]
        IntegerArray2,  // int[][]
        DoubleArray2    // double[][]
    }

Calling Cloud APIs

ResultType Specification

api = CloudDeploy[
APIFunction[{"n"->Integer}, Plot[Sin[#n x], {x,0,2Pi}]&, "JPEG"], "myapis/plot",Permissions->"Public"]

WolframAPICall call =
      new WolframAPICall().
              setEndpoint("objects/tgayley/myapis/plot").
              addArgument("n", 2).
              setResultType(ResultType.Image);
                                  
  WolframCallResult callResult = call.perform();
        
  if (callResult.isSuccess()) {
      Image res = (Image) callResult.getResult();
      // Display the image, save to a file, etc.
  } else {
      System.out.println(“Wolfram API call error:“ + callResult.getException());
  }

Calling Cloud APIs

ExportForm Specification

CloudDeploy[
APIFunction[{"n"->Integer}, Plot[Sin[#n x], {x,0,2Pi}]&, AllowedCloudExtraParameters -> {"_exportform"}], "myapis/plot2",Permissions->"Public"]

WolframAPICall call = new WolframAPICall().
                            setEndpoint("objects/tgayley/myapis/plot2").
                            addArgument("n", 2).
                            setExportForm("JPEG").
                            setResultType(ResultType.Image);
                                  
  WolframCallResult callResult = call.perform();
        
  if (callResult.isSuccess()) {
      Image res = (Image) callResult.getResult();
  } else {
      System.out.println(“Wolfram API call error:“ + callResult.getException());
  }

Calling Cloud APIs

Asynchronous Execution

CloudDeploy[
APIFunction[{"n"->Integer},TakeALongTime[#n]&], "myapis/slow",Permissions->"Public"]

WolframAPICall call = new WolframAPICall().
                            setEndpoint("objects/tgayley/myapis/slow").
                            addArgument("n", 42);
                                  
  Future<WolframCallResult> callFutureResult = call.performAsync();
        
  ...
        
  if (callFutureResult.isDone()) {
      WolframCallResult callResult = callResultFuture.get();
      if (callResult.isSuccess()) {
           ....
  }

Calling Cloud APIs

Authentication and Permissions

CloudDeploy[APIFunction[{"n"->Integer}, #n^2&], "myapis/private"]

sak=GenerateSecuredAuthenticationKey[]

WolframAPICall call = new WolframAPICall().
                            setEndpoint("objects/tgayley/myapis/private").
                            addArgument("n", 2).
                            setOAuthCredentials("...key...", "...secret...");

Calling Cloud APIs

Authentication and Permissions

CloudDeploy[APIFunction[{"n"->Integer}, #n^2&], "myapis/permkey", Permissions->{PermissionsKey["somekey"]->"Execute"}]

sak=GenerateSecuredAuthenticationKey[]

WolframAPICall call = new WolframAPICall().
                            setEndpoint("objects/tgayley/myapis/permkey").
                            addArgument("n", 2).
                            setPermissionsKey("somekey");

Calling Cloud APIs

Retrieving Evaluation Metadata

CloudDeploy[
APIFunction[{"n"->Integer},N[1/#n]&, AllowedCloudExtraParameters->{"_responseform"}], "myapis/msgs",Permissions->"Public"]

WolframAPICall call = new WolframAPICall().
                            setEndpoint("objects/tgayley/myapis/msgs").
                            addArgument("n", 0).
                            setCollectEvaluationData(true);
                                  
  WolframCallResult callResult = call.perform();
        
  if (callResult.isSuccess()) {
      String res = (String) callResult.getResult();
      String messages = callResult.getMetadata().getMessages();
      String prints = callResult.getMetadata().getPrintOutput();
      double timing = callResult.getMetadata().getTiming();
  } else {
      System.out.println(“Wolfram API call error:“ + callResult.getException());
  }

Demo: Van Gogh-ization of images

CloudDeploy[APIFunction[{"im"->"Image"}, NetModel["CycleGAN Photo-to-Van Gogh Translation"][#im]&, "JPEG"], "myapis/vangogh",Permissions->"Public"]

Demo: Van Gogh-ization of images

Import["/Users/tgayley/Pictures/tgayley_WRI.jpg"]

Image img = ImageIO.read(new File("/Users/tgayley/Picture/tgayley_WRI/jpg"));

  WolframAPICall call = new WolframAPICall().
                            setEndpoint("objects/tgayley/myapis/vangogh").
                            addArgument("im", img).
                            setResultType(ResultType.Image);
                                  
  WolframCallResult callResult = call.perform();
        
  Image imgAfter = callResult.getResult();

  ImageIcon icon = new ImageIcon(imgAfter);
  JFrame frame = new JFrame();
  frame.setSize(imgAfter.getWidth(), imgAfter.getHeight() + 20);
  JLabel label = new JLabel();
  label.setIcon(icon);
  frame.add(label);
  frame.setVisible(true);

Needs["JLink`"];
InstallJava[];
AddToClassPath["/Users/tgayley/Documents/workspace/JavaPrebuilt/eclipsebuild"];
LoadJavaClass["com.wolfram.client.WolframCallResult$ResultType"];
LoadJavaClass["javax.imageio.ImageIO"];

img =ImageIO`read[JavaNew["java.io.File","/Users/tgayley/Pictures/tgayley_WRI.jpg"]];

call=JavaNew["com.wolfram.client.WolframAPICall"]@
              setEndpoint["objects/tgayley/myapis/vangogh"]@
               addArgument["im", img]@
               setResultType[WolframCallResult$ResultType`Image];

wcr=call@perform[];

imgAfter=wcr@getResult[];

icon=JavaNew["javax.swing.ImageIcon", imgAfter];
frame=JavaNew["javax.swing.JFrame"];
frame@setSize[imgAfter@getWidth[],imgAfter@getHeight[]+20 (* make room for title bar *)];
label=JavaNew["javax.swing.JLabel"];
label@setIcon[icon];
frame@add[label];
JavaShow[frame]

Conclusion

Look for it on GitHub:  https://github.com/WolframResearch