From c57719f0084f91c97479eac253e6b069ff125e3c Mon Sep 17 00:00:00 2001 From: Levente Pap <levente.pap@iota.org> Date: Thu, 18 Jun 2020 18:22:31 +0200 Subject: [PATCH] graph pkg from autopeering-sim --- packages/graph/graph.go | 149 ++++++++++++++++++++++++++++++++++++++++ packages/graph/list.go | 54 +++++++++++++++ 2 files changed, 203 insertions(+) create mode 100644 packages/graph/graph.go create mode 100644 packages/graph/list.go diff --git a/packages/graph/graph.go b/packages/graph/graph.go new file mode 100644 index 00000000..ed62ad4c --- /dev/null +++ b/packages/graph/graph.go @@ -0,0 +1,149 @@ +package graph + +import ( + "runtime" + "sync" +) + +type nodeId int32 + +type SymbolTable map[string]nodeId + +func (s SymbolTable) getId(name string) nodeId { + id, ok := s[name] + if !ok { + id = nodeId(len(s)) + s[name] = id + } + return id +} + +type Graph struct { + SymbolTable + Nodes +} + +func New(IDs []string) *Graph { + g := &Graph{ + SymbolTable: make(SymbolTable, len(IDs)), + Nodes: make(Nodes, len(IDs)), + } + for index, id := range IDs { + g.Nodes[index].ID = nodeId(index) + g.SymbolTable[id] = nodeId(index) + } + return g +} + +func (g *Graph) AddEdge(a, b string) { + aid := g.SymbolTable.getId(a) + bid := g.SymbolTable.getId(b) + + g.Nodes.AddEdge(aid, bid) +} + +type Node struct { + ID nodeId + + // adjacent edges + Adj []nodeId +} + +func (n *Node) add(adjNode *Node) { + for _, id := range n.Adj { + if id == adjNode.ID { + return + } + } + n.Adj = append(n.Adj, adjNode.ID) +} + +type Nodes []Node + +func (nl Nodes) get(id nodeId) *Node { + return &nl[id] +} + +func (nl Nodes) AddEdge(a, b nodeId) { + an := nl.get(a) + bn := nl.get(b) + + an.add(bn) + bn.add(an) +} + +// diameter is the maximum length of a shortest path in the network +func (nl Nodes) Diameter() int { + + cpus := runtime.NumCPU() + numNodes := len(nl) + nodesPerCpu := numNodes / cpus + + results := make([]int, cpus) + wg := &sync.WaitGroup{} + wg.Add(cpus) + start := 0 + for cpu := 0; cpu < cpus; cpu++ { + end := start + nodesPerCpu + if cpu == cpus-1 { + end = numNodes + } + + go func(cpu int, start, end nodeId) { + defer wg.Done() + var diameter int + q := &list{} + depths := make([]bfsNode, numNodes) + for id := start; id < end; id++ { + // Need to reset the bfsData between runs + for i := range depths { + depths[i] = -1 + } + + df := nl.longestShortestPath(nodeId(id), q, depths) + if df > diameter { + diameter = df + } + } + results[cpu] = diameter + }(cpu, nodeId(start), nodeId(end)) + start += nodesPerCpu + } + + wg.Wait() + + diameter := 0 + for _, result := range results { + if result > diameter { + diameter = result + } + } + return diameter +} + +// bfs tracking data +type bfsNode int16 + +func (nodes Nodes) longestShortestPath(start nodeId, q *list, depths []bfsNode) int { + + n := nodes.get(start) + depths[n.ID] = 0 + q.pushBack(n) + + for { + newN := q.getHead() + if newN == nil { + break + } + n = newN + + for _, id := range n.Adj { + if depths[id] == -1 { + depths[id] = depths[n.ID] + 1 + q.pushBack(nodes.get(id)) + } + } + } + + return int(depths[n.ID]) +} diff --git a/packages/graph/list.go b/packages/graph/list.go new file mode 100644 index 00000000..5e15feff --- /dev/null +++ b/packages/graph/list.go @@ -0,0 +1,54 @@ +package graph + +type listElt struct { + next *listElt + node *Node +} + +type list struct { + head *listElt + tail *listElt + + free *listElt +} + +func (l *list) getHead() *Node { + elt := l.head + if elt == nil { + return nil + } + + // Remove elt from the list + l.head = elt.next + if l.head == nil { + l.tail = nil + } + // Add elt to the free list + elt.next = l.free + l.free = elt + + n := elt.node + elt.node = nil + return n +} + +func (l *list) pushBack(n *Node) { + // Get a free listElt to use to point to this node + elt := l.free + if elt == nil { + elt = &listElt{} + } else { + l.free = elt.next + elt.next = nil + } + + // Add the element to the tail of the list + elt.node = n + if l.tail == nil { + l.tail = elt + l.head = elt + } else { + l.tail.next = elt + l.tail = elt + } +} -- GitLab