Skip to content

Defining and Populating Graphs

Sameer Singh edited this page Dec 25, 2015 · 1 revision

Defining and Populating Graphs

Declarative specification of the graph is via the definition of the hyper graph, i.e. the user specifies sets of nodes (hyper-nodes) and sets of edges between these node sets (hyper-edges).

Everything below should be surrounded by:

trait MyModel extends DataModel {
   // everything below
}

Nodes

Definition

Defining a hypernode just requires the type of the underlying instances.

val n1 = node[T1]
val n2 = node[T2]

We can also create join nodes, i.e. nodes that contain pairs of instances, using a specific matcher.

val j = join(n1, n2)( (i1,i2) => i1.key == i2.key) // is of type Node[(T1,T2)]

Population

We just send the instances to populate.

val t1s: Iterable[T1] = ...
n1.populate(t1s)
// for join nodes, you can directly, add, or through a child node
j.populate(t1s zip t2s) // works
n2.populate(t2s) // automatically populates j as well

Note: that instances are deduplicated inside populate, i.e. only distinct instances can be stored in nodes.

You can also specify train or test.

n1.populate(t1s, true) // is training data
n1.populate(t1s, false) // is test data

Edges

Definition

Defining the edge requires the source and target nodes.

val e1 = edge(n1, n2)
val e2 = edge(n1, n1) // self-loops okay, but it is still asymmetric (think parents)
// for symmetric edges (think siblings, spouses, etc.)
val e3 = symmEdge(n1,n1) // can be different nodes, but with same base types
// can be given an optional 'name'
val e4 = edge(n2, n1, 'myedge)

Sensors

To define which edges to instantiate within a hyper-edge, we define sensors and matchers to identify the links.

Note: defining the sensors doesn't actually add the instances, till node.populate() is called.

Matchers

Doesn't add new instances to the nodes, just links existing instances

// can be used for any configuration: one-to-one, many-to-many, etc.
e1.addSensor((i1,i2) => i1.id == i2.fid)

Generators

Given one of the instances in the neighboring node, create instances in the other node, and link them.

e1.addSensor(i1 => i1.neighs) // i1.neighs: Iterable[T2]
e1.addSensor(i1 => i1.parent) // i1.parent: T2

Reverse Sensors

Sometimes, for asymmetric edges, it is often easier to specify the sensor from the target node to source node, for example, to specify a many-to-one relation where the neighbors are stored at the destination.

// reminder: e4 = edge(n2, n1)
e4.addReverseSensor(i1 => i1.neighs)
e4.addReverseSensor(i1 => i1.parent)

Population

You cannot directly populate edges, instead populate nodes, which automatically calls the sensors of the edges connected to the nodes.