/
graph.go
340 lines (281 loc) · 9.51 KB
/
graph.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
// Copyright(c) 2016 Ethan Zhuang <zhuangwj@gmail.com>.
package goraph
import (
"fmt"
"math"
)
// ID uniquely identify a vertex.
type ID interface{}
// Vertex interface represents a vertex with edges connected to it.
type Vertex interface {
// ID get the unique id of the vertex.
ID() ID
// Edges get all the edges connected to the vertex
Edges() []Edge
}
// Edge interface represents an edge connecting two vertices.
type Edge interface {
// Get returns the edge's inbound vertex, outbound vertex and weight.
Get() (from ID, to ID, weight float64)
}
// Graph is made up of vertices and edges.
// Vertices in the graph must have an unique id.
// Each edges in the graph connects two vertices directed with a weight.
type Graph struct {
vertices map[ID]*vertex
egress map[ID]map[ID]*edge
ingress map[ID]map[ID]*edge
}
type vertex struct {
self interface{}
enable bool
}
type edge struct {
self interface{}
weight float64
enable bool
changed bool
}
func (edge *edge) getWeight() float64 {
return edge.weight
}
// NewGraph creates a new empty graph.
func NewGraph() *Graph {
graph := new(Graph)
graph.vertices = make(map[ID]*vertex)
graph.egress = make(map[ID]map[ID]*edge)
graph.ingress = make(map[ID]map[ID]*edge)
return graph
}
// GetVertex get a vertex by input id.
// Try to get a vertex not in the graph will get an error.
func (graph *Graph) GetVertex(id ID) (vertex interface{}, err error) {
if v, exists := graph.vertices[id]; exists {
vertex = v.self
return
}
err = fmt.Errorf("Vertex %v is not found", id)
return
}
// GetEdge gets the edge between the two vertices by input ids.
// Try to get the edge from or to a vertex not in the graph will get an error.
// Try to get the edge between two disconnected vertices will get an error.
func (graph *Graph) GetEdge(from ID, to ID) (interface{}, error) {
if _, exists := graph.vertices[from]; !exists {
return nil, fmt.Errorf("Vertex(from) %v is not found", from)
}
if _, exists := graph.vertices[to]; !exists {
return nil, fmt.Errorf("Vertex(to) %v is not found", to)
}
if edge, exists := graph.egress[from][to]; exists {
return edge.self, nil
}
return nil, fmt.Errorf("Edge from %v to %v is not found", from, to)
}
// GetEdgeWeight gets the weight of the edge between the two vertices by input ids.
// Try to get the weight of the edge from or to a vertex not in the graph will get an error.
// Try to get the weight of the edge between two disconnected vertices will get +Inf.
func (graph *Graph) GetEdgeWeight(from ID, to ID) (float64, error) {
if _, exists := graph.vertices[from]; !exists {
return math.Inf(1), fmt.Errorf("Vertex(from) %v is not found", from)
}
if _, exists := graph.vertices[to]; !exists {
return math.Inf(1), fmt.Errorf("Vertex(to) %v is not found", to)
}
if edge, exists := graph.egress[from][to]; exists {
return edge.weight, nil
}
return math.Inf(1), nil
}
// AddVertex adds a new vertex into the graph.
// Try to add a duplicate vertex will get an error.
func (graph *Graph) AddVertex(id ID, v interface{}) error {
if _, exists := graph.vertices[id]; exists {
return fmt.Errorf("Vertex %v is duplicate", id)
}
graph.vertices[id] = &vertex{v, true}
graph.egress[id] = make(map[ID]*edge)
graph.ingress[id] = make(map[ID]*edge)
return nil
}
// AddEdge adds a new edge between the vertices by the input ids.
// Try to add an edge with -Inf weight will get an error.
// Try to add an edge from or to a vertex not in the graph will get an error.
// Try to add a duplicate edge will get an error.
func (graph *Graph) AddEdge(from ID, to ID, weight float64, e interface{}) error {
if weight == math.Inf(-1) {
return fmt.Errorf("-inf weight is reserved for internal usage")
}
if _, exists := graph.vertices[from]; !exists {
return fmt.Errorf("Vertex(from) %v is not found", from)
}
if _, exists := graph.vertices[to]; !exists {
return fmt.Errorf("Vertex(to) %v is not found", to)
}
if _, exists := graph.egress[from][to]; exists {
return fmt.Errorf("Edge from %v to %v is duplicate", from, to)
}
graph.egress[from][to] = &edge{e, weight, true, false}
graph.ingress[to][from] = graph.egress[from][to]
return nil
}
// UpdateEdgeWeight updates the weight of the edge between vertices by the input ids.
// Try to update an edge with -Inf weight will get an error.
// Try to update an edge from or to a vertex not in the graph will get an error.
// Try to update an edge between disconnected vertices will get an error.
func (graph *Graph) UpdateEdgeWeight(from ID, to ID, weight float64) error {
if weight == math.Inf(-1) {
return fmt.Errorf("-inf weight is reserved for internal usage")
}
if _, exists := graph.vertices[from]; !exists {
return fmt.Errorf("Vertex(from) %v is not found", from)
}
if _, exists := graph.vertices[to]; !exists {
return fmt.Errorf("Vertex(to) %v is not found", to)
}
if edge, exists := graph.egress[from][to]; exists {
edge.weight = weight
return nil
}
return fmt.Errorf("Edge from %v to %v is not found", from, to)
}
// DeleteVertex deletes a vertex from the graph and gets the value of the vertex.
// Try to delete a vertex not in the graph will get an nil.
func (graph *Graph) DeleteVertex(id ID) interface{} {
if vertex, exists := graph.vertices[id]; exists {
for to := range graph.egress[id] {
delete(graph.ingress[to], id)
}
for from := range graph.ingress[id] {
delete(graph.egress[from], id)
}
delete(graph.egress, id)
delete(graph.ingress, id)
delete(graph.vertices, id)
return vertex.self
}
return nil
}
// DeleteEdge deletes the edge between the vertices by the input id from the graph and gets the value of edge.
// Try to delete an edge from or to a vertex not in the graph will get an error.
// Try to delete an edge between disconnected vertices will get a nil.
func (graph *Graph) DeleteEdge(from ID, to ID) interface{} {
if _, exists := graph.vertices[from]; !exists {
return nil
}
if _, exists := graph.vertices[to]; !exists {
return nil
}
if edge, exists := graph.egress[from][to]; exists {
delete(graph.egress[from], to)
delete(graph.ingress[to], from)
return edge.self
}
return nil
}
// AddVertexWithEdges adds a vertex value which implements Vertex interface.
// AddVertexWithEdges adds edges connected to the vertex at the same time, due to the Vertex interface can get the Edges.
func (graph *Graph) AddVertexWithEdges(v Vertex) error {
if _, exists := graph.vertices[v.ID()]; exists {
return fmt.Errorf("Vertex %v is duplicate", v.ID())
}
graph.vertices[v.ID()] = &vertex{v, true}
graph.egress[v.ID()] = make(map[ID]*edge)
graph.ingress[v.ID()] = make(map[ID]*edge)
for _, eachEdge := range v.Edges() {
from, to, weight := eachEdge.Get()
if weight == math.Inf(-1) {
return fmt.Errorf("-inf weight is reserved for internal usage")
}
if from != v.ID() && to != v.ID() {
return fmt.Errorf("Edge from %v to %v is unrelated to the vertex %v", from, to, v.ID())
}
if _, exists := graph.egress[to]; !exists {
graph.egress[to] = make(map[ID]*edge)
}
if _, exists := graph.egress[from]; !exists {
graph.egress[from] = make(map[ID]*edge)
}
if _, exists := graph.ingress[from]; !exists {
graph.ingress[from] = make(map[ID]*edge)
}
if _, exists := graph.ingress[to]; !exists {
graph.ingress[to] = make(map[ID]*edge)
}
graph.egress[from][to] = &edge{eachEdge, weight, true, false}
graph.ingress[to][from] = graph.egress[from][to]
}
return nil
}
// CheckIntegrity checks if any edge connects to or from unknown vertex.
// If the graph is integrate, nil is returned. Otherwise an error is returned.
func (graph *Graph) CheckIntegrity() error {
for from, out := range graph.egress {
if _, exists := graph.vertices[from]; !exists {
return fmt.Errorf("Vertex %v is not found", from)
}
for to := range out {
if _, exists := graph.vertices[to]; !exists {
return fmt.Errorf("Vertex %v is not found", to)
}
}
}
for to, in := range graph.ingress {
if _, exists := graph.vertices[to]; !exists {
return fmt.Errorf("Vertex %v is not found", to)
}
for from := range in {
if _, exists := graph.vertices[from]; !exists {
return fmt.Errorf("Vertex %v is not found", from)
}
}
}
return nil
}
// GetPathWeight gets the total weight along the path by input ids.
// It will get -Inf if the input path is nil or empty.
// It will get -Inf if the path contains vertex not in the graph.
// It will get +Inf if the path contains vertices not connected.
func (graph *Graph) GetPathWeight(path []ID) (totalWeight float64) {
if len(path) == 0 {
return math.Inf(-1)
}
if _, exists := graph.vertices[path[0]]; !exists {
return math.Inf(-1)
}
for i := 0; i < len(path)-1; i++ {
if _, exists := graph.vertices[path[i+1]]; !exists {
return math.Inf(-1)
}
if edge, exists := graph.egress[path[i]][path[i+1]]; exists {
totalWeight += edge.getWeight()
} else {
return math.Inf(1)
}
}
return totalWeight
}
// DisableEdge disables the edge for further calculation.
func (graph *Graph) DisableEdge(from, to ID) {
graph.egress[from][to].enable = false
}
// DisableVertex disables the vertex for further calculation.
func (graph *Graph) DisableVertex(vertex ID) {
for _, edge := range graph.egress[vertex] {
edge.enable = false
}
}
// DisablePath disables all the vertices in the path for further calculation.
func (graph *Graph) DisablePath(path []ID) {
for _, vertex := range path {
graph.DisableVertex(vertex)
}
}
// Reset enables all vertices and edges for further calculation.
func (graph *Graph) Reset() {
for _, out := range graph.egress {
for _, edge := range out {
edge.enable = true
}
}
}