The Graph
data structure started out rather normal, as a typical
graph data structure where nodes are any-type (their value can be of
any C++ type). As nodes also have a key
, the data structure was also
an any-type dictionary (as in Python). This turned out so useful that
it is now used equally as dictionary, as graph, or a mix of
both. Further, a node can have a sub-graph (or sub-dictionary) as
value, making the data structure hierarchical and ideal to represent
XML or YAML kind of data. I use this data structure to read XML, YAML,
JSON. The .g
file format is one-to-one with the Graph data structure
and is the only file format used in rai
(for configuration files,
kinematic model descriptions, logic, factor graphs, optimization
problems, etc).
The best way to understand the Graph
class is to first understand the file format, which clarifies what is actually represented. Here is the example.g
from test/Core/graph
, hopefully the comments are self-explanatory:
## a trivial graph (all boolean-typed nodes)
x # a 'vertex': key=x, value=true, parents=none
y # another 'vertex': key=y, value=true, parents=none
(x y) # an 'edge': key=none, value=true, parents=x y
x # key=x, value=true, parents=none
y # key=y, value=true, parents=none
(x y) # key=none, value=true, parents=x y
(-1 -2) # key=none, value=true, parents=the previous and the y-node
## nodes with Graph value: hierarchical
node A { color:blue } # keys=node A, value=<Graph>, parents=none
node B { color:red, value:5 } # keys=node B, value=<Graph>, parents=none
edge C(A B) { width:2 } # keys=edge C, value=<Graph>, parents=A B
hyperedge(A B C) : 5 # keys=hyperedge, value=5, parents=A B C
## standard value types
a:string # rai::String (except for keywords 'true' and 'false' and 'Mod' and 'Include')
b:"STRING" # rai::String
c:'file.txt' # rai::FileToken (with RELATIVE path; creates file pointer on parsing)
d:-0.1234 # double
e:[1 2 3 0.5] # rai::arr
g! # bool (default: true, !means false)
h:true # bool
i:false # bool
j:{ a:0 } # sub-Graph (special: does not require a '=')
## parsing: : {..} (..) , and \n are separators for parsing key-value-pairs
b0:false b1 b2, b3() b4 # 4 boolean nodes with keys 'b0', 'b1 b2', 'b3', 'b4'
k:{ a, b:0.2 x:"hallo" # sub-Graph with 6 nodes
y
z():filename.org x }
## special Node Keys
## editing: after reading all nodes, the Graph takes all Edit nodes, deletes the Edit tag, and calls a edit()
## this example will modify/append the respective attributes of k
Edit k { y:false, z:otherString, b:7, c:newAttribute }
## including
Include : 'example_include.g' # first creates a normal FileToken node then opens and includes the file directly
## any types
trans:<t(10 0 0)> # trans is initially untyped; becomes types when accessed first in the code
## strange notations
a() # key=a, value=true, parents=none
() # key=none, value=true, parents=none
[1 2 3 4] # key=none, value=rai::arr, parents=none
Subgraphs may contain nodes can have parents from the containing graph, or from other subgraphs of the containing graph. Some methods of the @Graph@ class (to find nodes by key or type) allow to specify whether also nodes in subgraphs or parentgraphs are to be searched. This connectivity across (sub)-graphs e.g. allows to represent logic knowledge bases.