Skip to content
Snippets Groups Projects
Unverified Commit e66dad18 authored by Annika Wickert's avatar Annika Wickert Committed by GitHub
Browse files

Merge pull request #117 from bio-routing/dijkstra

Adding Dijkstra's algorithm
parents e9e5c326 c9c5864c
No related branches found
No related tags found
No related merge requests found
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["dijkstra.go"],
importpath = "github.com/bio-routing/bio-rd/util/dijkstra",
visibility = ["//visibility:public"],
)
go_test(
name = "go_default_test",
srcs = ["dijkstra_test.go"],
embed = [":go_default_library"],
deps = ["//vendor/github.com/stretchr/testify/assert:go_default_library"],
)
package dijkstra
// Topology represents a network topology
type Topology struct {
nodes map[Node]int64
edges map[Node]map[Node]int64
}
// Node represents a node in a graph
type Node struct {
Name string
}
// Edge represents a directed edge in a graph
type Edge struct {
NodeA Node
NodeB Node
Distance int64
}
// SPT represents a shortest path tree
type SPT map[Node]Path
// Path represents a path through a graph
type Path struct {
Edges []Edge
Distance int64
}
// NewTopology creates a new topology
func NewTopology(nodes []Node, edges []Edge) *Topology {
t := &Topology{
nodes: make(map[Node]int64),
edges: make(map[Node]map[Node]int64),
}
for _, n := range nodes {
t.nodes[n] = -1
}
for _, e := range edges {
if _, ok := t.edges[e.NodeA]; !ok {
t.edges[e.NodeA] = make(map[Node]int64)
}
t.edges[e.NodeA][e.NodeB] = e.Distance
}
return t
}
func (t *Topology) newSPT() SPT {
spt := make(SPT)
for n := range t.nodes {
spt[n] = Path{
Edges: make([]Edge, 0),
Distance: -1,
}
}
return spt
}
// SPT calculates the shortest path tree
func (t *Topology) SPT(from Node) SPT {
spt := t.newSPT()
tmp := spt[from]
tmp.Distance = 0
spt[from] = tmp
unmarked := make(map[Node]struct{})
for n := range t.nodes {
if n == from {
continue
}
unmarked[n] = struct{}{}
}
for len(unmarked) > 0 {
for neighbor, distance := range t.edges[from] {
if spt[neighbor].Distance == -1 {
tmp := spt[neighbor]
tmp.Distance = spt[from].Distance + distance
tmp.Edges = make([]Edge, len(spt[from].Edges)+1)
copy(tmp.Edges, spt[from].Edges)
tmp.Edges[len(spt[from].Edges)] = Edge{
NodeA: from,
NodeB: neighbor,
Distance: distance,
}
spt[neighbor] = tmp
continue
}
if spt[from].Distance+distance < spt[neighbor].Distance {
tmp := spt[neighbor]
tmp.Distance = spt[from].Distance + distance
tmp.Edges = make([]Edge, len(spt[from].Edges)+1)
copy(tmp.Edges, spt[from].Edges)
tmp.Edges[len(spt[from].Edges)] = Edge{
NodeA: from,
NodeB: neighbor,
Distance: distance,
}
spt[neighbor] = tmp
continue
}
}
var next *Node
nextDistance := int64(0)
for candidate := range unmarked {
if spt[candidate].Distance == -1 {
continue
}
if next == nil {
tmp := candidate
next = &tmp
nextDistance = spt[candidate].Distance
continue
}
if spt[candidate].Distance < nextDistance {
tmp := candidate
next = &tmp
nextDistance = spt[candidate].Distance
continue
}
}
from = *next
delete(unmarked, from)
}
return spt
}
package dijkstra
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSPT(t *testing.T) {
tests := []struct {
name string
nodes []Node
edges []Edge
expected SPT
}{
{
name: "Test #1",
nodes: []Node{
{
Name: "A",
},
{
Name: "B",
},
{
Name: "C",
},
{
Name: "D",
},
},
edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "A"},
Distance: 1,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "C"},
Distance: 5,
},
{
NodeA: Node{Name: "C"},
NodeB: Node{Name: "B"},
Distance: 5,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "D"},
Distance: 1,
},
{
NodeA: Node{Name: "D"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "D"},
Distance: 5,
},
{
NodeA: Node{Name: "D"},
NodeB: Node{Name: "A"},
Distance: 5,
},
{
NodeA: Node{Name: "D"},
NodeB: Node{Name: "C"},
Distance: 1,
},
{
NodeA: Node{Name: "C"},
NodeB: Node{Name: "D"},
Distance: 1,
},
},
expected: SPT{
Node{Name: "A"}: Path{
Edges: []Edge{},
Distance: 0,
},
Node{Name: "B"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
},
Distance: 1,
},
Node{Name: "C"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "D"},
Distance: 1,
},
{
NodeA: Node{Name: "D"},
NodeB: Node{Name: "C"},
Distance: 1,
},
},
Distance: 3,
},
Node{Name: "D"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "D"},
Distance: 1,
},
},
Distance: 2,
},
},
},
{
name: "Test #2",
nodes: []Node{
{
Name: "A",
},
{
Name: "B",
},
{
Name: "C",
},
{
Name: "D",
},
{
Name: "E",
},
},
edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "D"},
Distance: 2,
},
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "E"},
Distance: 3,
},
{
NodeA: Node{Name: "B"},
NodeB: Node{Name: "C"},
Distance: 10,
},
{
NodeA: Node{Name: "E"},
NodeB: Node{Name: "C"},
Distance: 5,
},
},
expected: SPT{
Node{Name: "A"}: Path{
Edges: []Edge{},
Distance: 0,
},
Node{Name: "B"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "B"},
Distance: 1,
},
},
Distance: 1,
},
Node{Name: "C"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "E"},
Distance: 3,
},
{
NodeA: Node{Name: "E"},
NodeB: Node{Name: "C"},
Distance: 5,
},
},
Distance: 8,
},
Node{Name: "D"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "D"},
Distance: 2,
},
},
Distance: 2,
},
Node{Name: "E"}: Path{
Edges: []Edge{
{
NodeA: Node{Name: "A"},
NodeB: Node{Name: "E"},
Distance: 3,
},
},
Distance: 3,
},
},
},
}
for _, test := range tests {
top := NewTopology(test.nodes, test.edges)
spt := top.SPT(Node{Name: "A"})
assert.Equalf(t, test.expected, spt, "Test %q", test.name)
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment