Files
gonum/concrete/dense.go
2014-03-22 22:26:49 -07:00

149 lines
3.9 KiB
Go

package concrete
import (
"github.com/gonum/graph"
"math"
)
// A dense graph is a graph such that all IDs are in a contiguous block from 0 to
// TheNumberOfNodes-1. It uses an adjacency matrix and should be relatively fast for both access
// and writing.
//
// This graph implements the CrunchGraph, but since it's naturally dense this is superfluous.
type DenseGraph struct {
adjacencyMatrix []float64
numNodes int
}
// Creates a dense graph with the proper number of nodes. If passable is true all nodes will have
// an edge with cost 1.0, otherwise every node will start unconnected (cost of +Inf.)
func NewDenseGraph(numNodes int, passable bool) *DenseGraph {
dg := &DenseGraph{adjacencyMatrix: make([]float64, numNodes*numNodes), numNodes: numNodes}
if passable {
for i := range dg.adjacencyMatrix {
dg.adjacencyMatrix[i] = 1.0
}
} else {
for i := range dg.adjacencyMatrix {
dg.adjacencyMatrix[i] = math.Inf(1)
}
}
return dg
}
func (dg *DenseGraph) NodeExists(node graph.Node) bool {
return node.ID() < dg.numNodes
}
func (dg *DenseGraph) Degree(node graph.Node) int {
deg := 0
for i := 0; i < dg.numNodes; i++ {
if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) {
deg++
}
if dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
deg++
}
}
return deg
}
func (dg *DenseGraph) NodeList() []graph.Node {
nodes := make([]graph.Node, dg.numNodes)
for i := 0; i < dg.numNodes; i++ {
nodes[i] = Node(i)
}
return nodes
}
func (dg *DenseGraph) DirectedEdgeList() []graph.Edge {
edges := make([]graph.Edge, 0, len(dg.adjacencyMatrix))
for i := 0; i < dg.numNodes; i++ {
for j := 0; j < dg.numNodes; j++ {
if dg.adjacencyMatrix[i*dg.numNodes+j] != math.Inf(1) {
edges = append(edges, Edge{Node(i), Node(j)})
}
}
}
return edges
}
func (dg *DenseGraph) Neighbors(node graph.Node) []graph.Node {
neighbors := make([]graph.Node, 0)
for i := 0; i < dg.numNodes; i++ {
if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) ||
dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
neighbors = append(neighbors, Node(i))
}
}
return neighbors
}
func (dg *DenseGraph) EdgeBetween(node, neighbor graph.Node) graph.Edge {
if dg.adjacencyMatrix[neighbor.ID()*dg.numNodes+node.ID()] != math.Inf(1) ||
dg.adjacencyMatrix[node.ID()*dg.numNodes+neighbor.ID()] != math.Inf(1) {
return Edge{node, neighbor}
}
return nil
}
func (dg *DenseGraph) Successors(node graph.Node) []graph.Node {
neighbors := make([]graph.Node, 0)
for i := 0; i < dg.numNodes; i++ {
if dg.adjacencyMatrix[node.ID()*dg.numNodes+i] != math.Inf(1) {
neighbors = append(neighbors, Node(i))
}
}
return neighbors
}
func (dg *DenseGraph) EdgeTo(node, succ graph.Node) graph.Edge {
if dg.adjacencyMatrix[node.ID()*dg.numNodes+succ.ID()] != math.Inf(1) {
return Edge{node, succ}
}
return nil
}
func (dg *DenseGraph) Predecessors(node graph.Node) []graph.Node {
neighbors := make([]graph.Node, 0)
for i := 0; i < dg.numNodes; i++ {
if dg.adjacencyMatrix[i*dg.numNodes+node.ID()] != math.Inf(1) {
neighbors = append(neighbors, Node(i))
}
}
return neighbors
}
// DenseGraph is naturally dense, we don't need to do anything
func (dg *DenseGraph) Crunch() {
}
func (dg *DenseGraph) Cost(e graph.Edge) float64 {
return dg.adjacencyMatrix[e.Head().ID()*dg.numNodes+e.Tail().ID()]
}
// Sets the cost of an edge. If the cost is +Inf, it will remove the edge,
// if directed is true, it will only remove the edge one way. If it's false it will change the cost
// of the edge from succ to node as well.
func (dg *DenseGraph) SetEdgeCost(e graph.Edge, cost float64, directed bool) {
dg.adjacencyMatrix[e.Head().ID()*dg.numNodes+e.Tail().ID()] = cost
if !directed {
dg.adjacencyMatrix[e.Tail().ID()*dg.numNodes+e.Head().ID()] = cost
}
}
// Equivalent to SetEdgeCost(edge, math.Inf(1), directed)
func (dg *DenseGraph) RemoveEdge(e graph.Edge, directed bool) {
dg.SetEdgeCost(e, math.Inf(1), directed)
}