PGX 2.3.1
Documentation
Note: This feature is only available in the following software packages: PGX on the Oracle Tech Network

PGX 2.3.1 limitation

Dynamic compilation of Green-Marl code is currently only possible on Linux x86 platforms (refer to system requirements). If you run on a different platform, you can still use PGX by disabling the dynamic compilation feature, setting the enable_gm_compiler field to false in the PGX configuration.

Compile and Run Your Own Green-Marl Program

A key benefit of PGX is that you may implement algorithms with no limit. You can easily add your own algorithms using Green-Marl, a domain-specific language designed for graph algorithms. Green-Marl has a C-like syntax. Refer to the Built-in Algorithms section for a number of code example. For a full reference, see the Green-Marl language specification.

'Hello World' program

To start with, we will compile and run a Green-Marl 'Hello World' program.

/*
* Copyright (C) 2013 - 2016 Oracle and/or its affiliates. All rights reserved.
*/
procedure hello_world() {
    println("Hello World");
}

PGX 2.3.1 limitations

The current version of PGX does not support the print statement in remote mode. Further, it does not support Green-Marl algorithms that use edge properties on undirected graphs.

This program can be found in the examples/gm directory of the distribution.

Creating a session

Let's begin by creating a session:

cd $PGX_HOME
./bin/pgx
// starting the shell will create an implicit session
import oracle.pgx.api.*;

...

PgxSession session = Pgx.createSession("my-session");

POST /core/session HTTP/1.1
Content-Type: application/json

{"source":"test","idleTimeout":null,"requestTimeout":null,"timeUnitName":null}

Compiling the code

Next, let's compile the 'Hello World' program:

pgx> p = session.compileProgram("examples/gm/hello_world.gm")
==> Compiled Program hello_world
CompiledProgram p = session.compileProgram("examples/gm/hello_world.gm")
POST /core/session/<sessionId>/analysis HTTP/1.1
Content-Type: application/json

{"code":"procedure hello_world() {println("Hello World");}"}

Run 'Hello World'

pgx> p.run()
Hello World
==> {
  "success" : true,
  "canceled" : false,
  "executionTimeMs" : 0,
  "exception" : null
}
AnalysisResult<Void> r = p.run();
POST /core/session/<sessionId>/analysis/hello_world HTTP/1.1
Content-Type: application/json

{"args":[],"targetPool":"ANALYSIS_POOL"}

Graph Analysis Example

As a more advanced example, we will compile the following algorithm, which computes the maximum degree of a given graph:

/*
* Copyright (C) 2013 - 2016 Oracle and/or its affiliates. All rights reserved.
*/

/*
* Computes the maximum out degree of a graph
*
* Input-Parameters:
*   G:      arbitrary graph.
* 
* Returns: maximum out degree
*
*/
procedure max_degree(G: graph): int {
    return max(n: G.nodes) { n.degree() };
}

You can find this program in the examples/gm directory of the distribution.

Compiling the code

Compile our max_degree procedure:

pgx> p = session.compileProgram("examples/gm/max_degree.gm")
==> Compiled Program max_degree
CompiledProgram p = session.compileProgram("examples/gm/max_degree.gm")
POST /core/session/<sessionId>/analysis HTTP/1.1
Content-Type: application/json

{"code":"procedure max_degree(G: graph): int { return max(n: G.vertices) { n.degree() }; }"}

In PGX, every algorithm is uniquely identified by an analysis name, which equals the procedure name of the given Green-Marl program. If an algorithm with the same name already exists for this session, it will be overwritten. Note: Algorithms are session-bound, meaning session A cannot overwrite any algorithm from session B. Likewise, session A cannot execute algorithms compiled by session B.

Loading the graph data into memory

Our procedure uses a graph as an input argument. In order to run our algorithm, we first need to load a graph into memory. In this tutorial, we will load the sample example graph included in the distribution.

pgx> g = session.readGraphWithProperties("examples/graphs/connections.edge_list.json")
PgxGraph g = session.readGraphWithProperties("examples/graphs/connections.edge_list.json");
POST /core/session/<sessionId>/graph HTTP/1.1
Content-Type: application/json

{"graphName":"G","graphConfig":{"uri":"connections.edge_list","format":"edge_list","vertex_id_type":"integer","vertex_props":[
    {"name":"religion","type":"string"},{"name":"type","type":"string"},{"name":"company","type":"string"},
    {"name":"show","type":"string"},{"name":"music_genre","type":"string"},{"name":"role","type":"string"},
    {"name":"name","type":"string"},{"name":"country","type":"string"},{"name":"genre","type":"string"},
    {"name":"team","type":"string"},{"name":"occupation","type":"string"}],"edge_props":[
    {"name":"weight","type":"float"}],"edge_label":true,"loading":{"load_edge_label":true}}}

Running the algorithm

Next, let's execute our compiled algorithm with our loaded graph as input:

pgx> p.run(g)
==> {
  "success" : true,
  "canceled" : false,
  "returnValue" : 2,
  "executionTimeMs" : 0,
  "exception" : null
}
AnalysisResult<Integer> r = p.run(g);
System.out.println("max_degree = " + r.getReturnValue());
POST /core/session/<sessionId>/analysis/max_degree HTTP/1.1
Content-Type: application/json

{"args":[{"type":"GRAPH","name":"G"}],"targetPool":"ANALYSIS_POOL","expectedReturnType":"int"}

Congratulations, you have successfully executed a custom Green-Marl algorithm.

Here is the complete Java class:

import oracle.pgx.api.CompiledProgram;
import oracle.pgx.api.Pgx;
import oracle.pgx.api.PgxGraph;
import oracle.pgx.api.PgxSession;
import oracle.pgx.api.internal.AnalysisResult;

public class CompilationExample {

  public static void main(String[] args) throws Exception {
    PgxSession session = Pgx.createSession("my-session");
    CompiledProgram maxDegree = session.compileProgram("examples/gm/max_degree.gm");
    PgxGraph graph = session.readGraphWithProperties("examples/graphs/connections.edge_list.json");
    AnalysisResult<Integer> result = maxDegree.run(graph);
    System.out.println("max_degree = " + result.getReturnValue() + " (took " + result.getExecutionTimeMs() + "ms)");
  }
}

To compile, run

cd $PGX_HOME
mkdir classes
javac -cp lib/embedded/*:lib/common/*:third-party/* -d classes examples/java/CompilationExample.java

To run it on your local machine, do

java -cp lib/embedded/*:lib/common/*:third-party/*:classes:conf CompilationExample

Persist a compiled algorithm into a JAR file

If you are using PGX in embedded mode, you can write a compiled algorithm into a .jar file to persist it across PGX instances by using the following APIs:

pgx> import oracle.pgx.compilers.Compilers
pgx> import oracle.pgx.compilers.GmCompilation
pgx> import oracle.pgx.compilers.Language
pgx> gmComp = Compilers.findCompiler(Language.GM)
==> oracle.pgx.compilers.gm.GmCompiler@388b401d
pgx> maxDegree = gmComp.compile("examples/gm/max_degree.gm", "max_degree")
==> oracle.pgx.compilers.gm.GmCompilationImpl@6573d2f7
pgx> maxDegree.writeAsJar(new FileOutputStream("/tmp/max-degree.jar"))
import oracle.pgx.compilers.Compilers;
import oracle.pgx.compilers.GmCompilation;
import oracle.pgx.compilers.Language;
import oracle.pgx.compilers.PgxCompiler;

...

PgxCompiler<GmCompilation> gmComp = Compilers.findCompiler(Language.GM);
GmCompilation maxDegree = gmComp.compile("examples/gm/max_degree.gm", "max_degree");
maxDegree.writeAsJar(new FileOutputStream(tmpFile));

In above example, we store the compilation artifact of the code in examples/gm/max_degree.gm into a .jar file at /tmp/max-degree.jar. We also have to provide an ID for the algorithm, so we can reference it later. In this example we use max_degree as ID.

To make the stored algorithm visible to PGX, add the generated .jar file to the classpath PGX is using. In the case of PGX shell, you can add the .jar to the classpath by setting

export CLASSPATH=/tmp/max-degree.jar
./bin/pgx

You can then retrieve the compiled algorithm by looking it up via its ID:

maxDegree = session.getCompiledProgram("max_degree")