mirror of
https://github.com/gonum/gonum.git
synced 2025-10-09 09:00:38 +08:00
265 lines
6.5 KiB
Go
265 lines
6.5 KiB
Go
package discrete
|
|
|
|
import (
|
|
"sort"
|
|
)
|
|
|
|
// A GonumGraph is a very generalized graph that can handle an arbitrary number of vertices and edges -- as well as act as either directed or undirected.
|
|
//
|
|
// Internally, it uses a map of successors AND predecessors, to speed up some operations (such as getting all successors/predecessors). It also speeds up thing like adding edges (assuming both edges exist).
|
|
//
|
|
// However, its generality is also its weakness (and partially a flaw in needing to satisfy MutableGraph). For most purposes, creating your own graph is probably better. For instance, see discrete.TileGraph for an example
|
|
// of an immutable 2D grid of tiles that also implements the Graph interface, but would be more suitable if all you needed was a simple undirected 2D grid.
|
|
type GonumGraph struct {
|
|
successors map[int]map[int]float64
|
|
predecessors map[int]map[int]float64
|
|
directed bool
|
|
}
|
|
|
|
func NewGonumGraph(directed bool) *GonumGraph {
|
|
return &GonumGraph{
|
|
successors: make(map[int]map[int]float64),
|
|
predecessors: make(map[int]map[int]float64),
|
|
directed: directed,
|
|
}
|
|
}
|
|
|
|
func NewPreAllocatedGonumGraph(directed bool, numVertices int) *GonumGraph {
|
|
return &GonumGraph{
|
|
successors: make(map[int]map[int]float64, numVertices),
|
|
predecessors: make(map[int]map[int]float64, numVertices),
|
|
directed: directed,
|
|
}
|
|
}
|
|
|
|
/* Mutable Graph implementation */
|
|
|
|
func (graph *GonumGraph) NewNode(successors []int) (id int) {
|
|
nodes := sort.IntSlice(graph.NodeList())
|
|
sort.Sort(&nodes)
|
|
for i, node := range nodes {
|
|
if i != node {
|
|
graph.AddNode(i, successors)
|
|
return i
|
|
}
|
|
}
|
|
|
|
newID := len(nodes)
|
|
graph.AddNode(newID, successors)
|
|
return newID
|
|
}
|
|
|
|
func (graph *GonumGraph) AddNode(id int, successors []int) {
|
|
if _, ok := graph.successors[id]; ok {
|
|
return
|
|
}
|
|
|
|
graph.successors[id] = make(map[int]float64, len(successors))
|
|
if !graph.directed {
|
|
graph.predecessors[id] = make(map[int]float64, len(successors))
|
|
} else {
|
|
graph.predecessors[id] = make(map[int]float64)
|
|
}
|
|
for _, succ := range successors {
|
|
graph.successors[id][succ] = 1.0
|
|
|
|
// Always add the reciprocal node to the graph
|
|
if _, ok := graph.successors[succ]; !ok {
|
|
graph.predecessors[succ] = make(map[int]float64)
|
|
graph.successors[succ] = make(map[int]float64)
|
|
}
|
|
|
|
graph.predecessors[succ][id] = 1.0
|
|
|
|
// But only add the reciprocal edge if we're undirected
|
|
if !graph.directed {
|
|
graph.successors[succ][id] = 1.0
|
|
graph.predecessors[id][succ] = 1.0
|
|
}
|
|
}
|
|
}
|
|
|
|
func (graph *GonumGraph) AddEdge(id, successor int) {
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return
|
|
}
|
|
|
|
if _, ok := graph.successors[successor]; !ok {
|
|
graph.successors[successor] = make(map[int]float64)
|
|
graph.predecessors[successor] = make(map[int]float64)
|
|
}
|
|
|
|
graph.successors[id][successor] = 1.0
|
|
graph.predecessors[successor][id] = 1.0
|
|
|
|
if !graph.directed {
|
|
graph.successors[successor][id] = 1.0
|
|
graph.predecessors[id][successor] = 1.0
|
|
}
|
|
}
|
|
|
|
func (graph *GonumGraph) SetEdgeCost(id, successor int, cost float64) {
|
|
// Normally I'd use graph.vertices.Contains(id) as above, but this is equivalent and a bit easier to read here
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return
|
|
} else if _, ok := graph.successors[id][successor]; !ok {
|
|
return
|
|
}
|
|
graph.successors[id][successor] = cost
|
|
graph.predecessors[successor][id] = cost
|
|
|
|
// By the spec, only the empty graph will be toggled between directed and undirected. Therefore we can be sure the reciprocal edge exists
|
|
if !graph.directed {
|
|
graph.successors[successor][id] = cost
|
|
graph.predecessors[id][successor] = cost
|
|
}
|
|
}
|
|
|
|
func (graph *GonumGraph) RemoveNode(id int) {
|
|
if _, ok := graph.successors[id]; ok {
|
|
return
|
|
}
|
|
|
|
for succ, _ := range graph.successors[id] {
|
|
delete(graph.predecessors[succ], id)
|
|
}
|
|
delete(graph.successors, id)
|
|
|
|
for pred, _ := range graph.predecessors[id] {
|
|
delete(graph.successors[pred], id)
|
|
}
|
|
delete(graph.predecessors, id)
|
|
|
|
}
|
|
|
|
func (graph *GonumGraph) RemoveEdge(id, succ int) {
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return
|
|
} else if _, ok := graph.successors[succ]; !ok {
|
|
return
|
|
}
|
|
|
|
delete(graph.successors[id], succ)
|
|
delete(graph.predecessors[succ], id)
|
|
if !graph.directed {
|
|
delete(graph.predecessors[id], succ)
|
|
delete(graph.successors[succ], id)
|
|
}
|
|
}
|
|
|
|
func (graph *GonumGraph) EmptyGraph() {
|
|
if len(graph.successors) == 0 {
|
|
return
|
|
}
|
|
graph.successors = make(map[int]map[int]float64)
|
|
graph.predecessors = make(map[int]map[int]float64)
|
|
}
|
|
|
|
func (graph *GonumGraph) SetDirected(directed bool) {
|
|
if len(graph.successors) > 0 {
|
|
return
|
|
}
|
|
graph.directed = directed
|
|
}
|
|
|
|
/* Graph implementation */
|
|
|
|
func (graph *GonumGraph) Successors(id int) []int {
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return nil
|
|
}
|
|
|
|
successors := make([]int, len(graph.successors[id]))
|
|
for succ, _ := range graph.successors[id] {
|
|
successors = append(successors, succ)
|
|
}
|
|
|
|
return successors
|
|
}
|
|
|
|
func (graph *GonumGraph) IsSuccessor(id, succ int) bool {
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return false
|
|
}
|
|
|
|
_, ok := graph.successors[id][succ]
|
|
|
|
return ok
|
|
}
|
|
|
|
func (graph *GonumGraph) Predecessors(id int) []int {
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return nil
|
|
}
|
|
|
|
predecessors := make([]int, len(graph.predecessors[id]))
|
|
for pred, _ := range graph.predecessors[id] {
|
|
predecessors = append(predecessors, pred)
|
|
}
|
|
|
|
return predecessors
|
|
}
|
|
|
|
func (graph *GonumGraph) IsPredecessor(id, pred int) bool {
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return false
|
|
}
|
|
|
|
_, ok := graph.predecessors[id][pred]
|
|
|
|
return ok
|
|
}
|
|
|
|
func (graph *GonumGraph) IsAdjacent(id, neighbor int) bool {
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return false
|
|
}
|
|
|
|
_, succ := graph.predecessors[id][neighbor]
|
|
_, pred := graph.predecessors[id][neighbor]
|
|
|
|
return succ || pred
|
|
}
|
|
|
|
func (graph *GonumGraph) NodeExists(id int) bool {
|
|
_, ok := graph.successors[id]
|
|
|
|
return ok
|
|
}
|
|
|
|
func (graph *GonumGraph) Degree(id int) int {
|
|
if _, ok := graph.successors[id]; !ok {
|
|
return 0
|
|
}
|
|
|
|
return len(graph.successors[id]) + len(graph.predecessors[id])
|
|
}
|
|
|
|
func (graph *GonumGraph) EdgeList() [][2]int {
|
|
eList := make([][2]int, 0, len(graph.successors))
|
|
for id, succMap := range graph.successors {
|
|
for succ, _ := range succMap {
|
|
eList = append(eList, [2]int{id, succ})
|
|
}
|
|
}
|
|
|
|
return eList
|
|
}
|
|
|
|
func (graph *GonumGraph) NodeList() []int {
|
|
nodes := make([]int, 0, len(graph.successors))
|
|
for node, _ := range graph.successors {
|
|
nodes = append(nodes, node)
|
|
}
|
|
|
|
return nodes
|
|
}
|
|
|
|
func (graph *GonumGraph) IsDirected() bool {
|
|
return graph.directed
|
|
}
|
|
|
|
func (graph *GonumGraph) Cost(id, succ int) float64 {
|
|
return graph.successors[id][succ]
|
|
}
|