Example

This is an illustrative example of a OptiGraph program. To run this code, create a file named 'HelloGraph.scala' forge/apps/OptiGraph/src and then compile it using the instructions on our getting started page.

import optigraph.compiler._
import optigraph.library._
import optigraph.shared._

// This object lets us run the Delite version of the code
object HelloGraphCompiler extends OptiGraphApplicationCompiler with HelloGraph

// This object lets us run the Scala library version of the code
object HelloGraphInterpreter extends OptiGraphApplicationInterpreter 
  with HelloGraph

trait HelloGraph extends OptiGraphApplication {
  def main() = {

    val srcNodes = NodeData.fromFunction(10,i => i)
    val dstNodes = NodeData.fromFunction(10,i => 9-i)
    val edgeList = srcNodes.pack(dstNodes)

    val dg = directedGraphFromEdgeList(edgeList)

    val dt = sumOverNodes(dg.nodes){ n =>
        sumOverNeighbors(dg.inNeighbors(n)){ nbr =>
          if(nbr >= n) 1
          else 0
        }
      }

    println("nbr >= n: " + dt)

    val dupSrcNodes = srcNodes.concat(dstNodes)
    val dupDstNodes = dstNodes.concat(srcNodes)
    val dupEdgeList = dupSrcNodes.pack(dupDstNodes)

    val ug = undirectedGraphFromEdgeList(dupEdgeList)

    val ut = sumOverNodes(ug.nodes){ n =>
        sumOverNeighbors(ug.neighbors(n)){ nbr =>
          if(nbr >= n) 1
          else 0
      }
    }

    println("nbr >= n: " + ut)

  }
}

This code is mostly boilerplate, and is needed to setup the application. First, we import the DSL code. Then, we create both the HelloGraphInterpreter and HelloGraphCompiler objects. HelloGraphInterpreter produces an object that can be run in interpreter mode. HelloGraphCompiler will produce our high performance Delite code. Both objects extend the trait HelloGraph which will hold our application example application. This is an idiom commonly used in Forge code.

import optigraph.compiler._
import optigraph.library._
import optigraph.shared._

// This object lets us run the Delite version of the code
object HelloGraphCompiler extends OptiGraphApplicationCompiler with HelloGraph

// This object lets us run the Scala library version of the code
object HelloGraphInterpreter extends OptiGraphApplicationInterpreter 
 with HelloGraph

trait HelloGraph extends OptiGraphApplication {
  def main() = {

This is where we create the variables used in the problem. In this case we declare two NodeData variables srcNodes and dstNodes. The NodeData trait is akin to a Vector object for graphs. We pack these NodeData objects together to create an edgeList of integer tuples. The edgeList variable holds pairs of src-dst nodes to create our graph.

    val srcNodes = NodeData.fromFunction(10,i => i)
    val dstNodes = NodeData.fromFunction(10,i => 9-i)
    val edgeList = srcNodes.pack(dstNodes)

Here the call to directedGraphFromEdgeList does exactly what it suggests. We take an edge list and create a DSL graph object from it. We just created our first graph! Typically graphs are created from .txt files that are laid out as an adjacency list or an edge list. See the OptiGraph code documentation for specialized file parsers for OptiGraph.

    val dg = directedGraphFromEdgeList(edgeList)

This code introduces us to our first parallel graph operations sumOverNodes and sumOverNodes. I'd like to think the names are descriptive enough. Effectively what we accomplish here is traversing all of the nodes in the graph and counting the number of neighbors of that node on incoming edges that have an id larger than the current node id. These sum over methods boil down to a parallel mapreduce under the covers.

    val dt = sumOverNodes(dg.nodes){ n =>
        sumOverNeighbors(dg.inNeighbors(n)){ nbr =>
          if(nbr >= n) 1
          else 0
        }
      }

The next chunk of code is very similar but you will notice some slight differences. Namely we are operating on an undirected graph now. First we do some manipulation on the NodeData objects in order to prepare an edgelist with undirected edges. Namely we have to make sure we have duplicate edges! After creating an undirected graph you should notice that the operations on the undirected graph are very similar to the directed graph. Incoming edges and outgoing edges are really the same thing here so we just access neighbors now. You should notice that our final count doubles. We have twice the number of edges now!

    val dupSrcNodes = srcNodes.concat(dstNodes)
    val dupDstNodes = dstNodes.concat(srcNodes)
    val dupEdgeList = dupSrcNodes.pack(dupDstNodes)

    val ug = undirectedGraphFromEdgeList(dupEdgeList)

    val ut = sumOverNodes(ug.nodes){ n =>
        sumOverNeighbors(ug.neighbors(n)){ nbr =>
          if(nbr >= n) 1
          else 0
      }
    }

Congrats you have build your first OptiGraph application!