import graph_tool.all as gt
import numpy as np
from gtnn.network.activation import LogSigmoid, Identity
[docs]class Net(object):
"""This is a wrapper for the graph_tool.Graph object.
One can register vertex/edge properties and use them in algorithm implementation.
It also implements basic forward and backward operations.
"""
def __init__(self, nInput, nOutput, graph=gt.Graph()):
self.g = gt.Graph(graph)
self.nInput = nInput
self.nOutput = nOutput
self.properties = dict()
self.biasProp = self.addVertexProperty("bias", "long double")
self.valueProp = self.addVertexProperty("value", "long double")
self.sumProp = self.addVertexProperty("sum", "long double")
self.activation = self.addVertexProperty(
"activation", "python::object")
self.errorProp = self.addVertexProperty("errorProp", "long double")
self.weightProp = self.addEdgeProperty("weight", "long double")
self.prepare()
[docs] def addVertexProperty(self, name, typeName):
self.properties[name] = self.g.new_vertex_property(typeName)
return self.properties[name]
[docs] def addEdgeProperty(self, name, typeName):
self.properties[name] = self.g.new_edge_property(typeName)
return self.properties[name]
[docs] def prepare(self):
"""
Computes ans saves the topological sort of the graph for future use.
"""
self.order = np.array(gt.topological_sort(self.g)[::-1])
for vIdx in self.order[:self.nInput]:
self.activation[self.g.vertex(vIdx)] = Identity()
[docs] def forward(self, inputVals=[]):
g = self.g
vp = self.valueProp
bp = self.biasProp
wp = self.weightProp
sm = self.sumProp
activation = self.activation
for inpVal, vIdx in zip(inputVals, self.order[:self.nInput]):
vp[g.vertex(vIdx)] = inpVal
for vIdx in self.order[self.nInput:]:
v = g.vertex(vIdx)
inputs = np.array([vp[e.source()] for e in v.in_edges()])
weights = np.array([wp[e] for e in v.in_edges()])
sum = np.sum(inputs * weights) + bp[v]
sm[v] = sum
vp[v] = activation[v].value(sum)
return np.array(
[vp[g.vertex(vIdx)] for vIdx in self.order[-self.nOutput:]]
)
[docs] def backward(self, outputErr=[]):
g = self.g
ep = self.errorProp
wp = self.weightProp
sm = self.sumProp
activation = self.activation
for outErr, vIdx in zip(outputErr,
self.order[-self.nOutput:]):
v = g.vertex(vIdx)
ep[v] = outErr * activation[v].derivative(sm[v])
# TODO: Change the borders
for vIdx in reversed(self.order[:-self.nOutput]):
v = g.vertex(vIdx)
errors = np.array([ep[e.target()] for e in v.out_edges()])
weights = np.array([wp[e] for e in v.out_edges()])
ep[v] = np.sum(errors * weights) * activation[v].derivative(sm[v])
def __str__(self):
# TODO: Change output format
ret = "Net: |V|=" + str(self.g.num_vertices())
ret += " |E|=" + str(self.g.num_edges()) + "\n"
for key, prop in self.properties.items():
ret += key + ": " + str(prop.a) + "\n"
return ret