The easiest way to understand how this all fits together is by building a simple example.
To start, let's implement a simple graph to add two numbers together—basically, this would be: c = a + b:
- First, let's import some libraries—most importantly, Gorgonia, as follows:
package main
import (
"fmt"
"log"
. "gorgonia.org/gorgonia"
)
- Then, let's start our main function, like so:
func main() {
g := NewGraph()
}
- To that, let's add our scalars, as shown here:
a = NewScalar(g, Float64, WithName("a"))
b = NewScalar(g, Float64, WithName("b"))
- Then, very importantly, let's define our operation node, as follows:
c, err = Add(a, b)
if err != nil {
log.Fatal(err)
}
Note that c will not actually have a value now; we've just defined a new node of our computation graph, so we need to execute it before it will have a value.
- To execute it, we need to create a virtual machine object for it to run in, as follows:
machine := NewTapeMachine(g)
- Then, set the initial values of a and b, and proceed to get the machine to execute our graph, as shown here:
Let(a, 1.0)
Let(b, 2.0)
if machine.RunAll() != nil {
log.Fatal(err)
}
The complete code is as follows:
package main
import (
"fmt"
"log"
. "gorgonia.org/gorgonia"
)
func main() {
g := NewGraph()
var a, b, c *Node
var err error
// define the expression
a = NewScalar(g, Float64, WithName("a"))
b = NewScalar(g, Float64, WithName("b"))
c, err = Add(a, b)
if err != nil {
log.Fatal(err)
}
// create a VM to run the program on
machine := NewTapeMachine(g)
// set initial values then run
Let(a, 1.0)
Let(b, 2.0)
if machine.RunAll() != nil {
log.Fatal(err)
}
fmt.Printf("%v", c.Value())
// Output: 3.0
}
Now, we have built our first computation graph in Gorgonia and executed it!