Skip to content

Commit e39b162

Browse files
committed
ds/graph: use new ds/matrix
1 parent 776d706 commit e39b162

File tree

8 files changed

+114
-239
lines changed

8 files changed

+114
-239
lines changed

ds/graph/adjacencyMatrix.go

+25-32
Original file line numberDiff line numberDiff line change
@@ -21,62 +21,58 @@ type AdjacencyMatrix struct {
2121
// TODO implement transpose, undirected
2222

2323
// NewAdjacencyMatrix returns an AdjacencyMatrix. Each vertex pair can only
24-
// have one edge. For a multigraph, use [NewMultiAdjacencyMatrix].
24+
// have one edge. For a multigraph, where vertex pairs can store a count of
25+
// multiple edges, use [NewMultiAdjacencyMatrix].
2526
func NewAdjacencyMatrix() AdjacencyMatrix {
2627
return AdjacencyMatrix{
2728
mat: matrix.NewBit(4, 4),
2829
multi: false,
2930
}
3031
}
3132

32-
// NewAdjacencyMatrix returns an AdjacencyMatrix. Each vertex pair can have
33-
// a count of multiple edges. For a simple graph, use [NewAdjacencyMatrix].
33+
// NewMultiAdjacencyMatrix returns an AdjacencyMatrix. Each vertex pair can
34+
// have a count of multiple edges. For a simple graph, where each vertex pair
35+
// can have at most one edge, use [NewAdjacencyMatrix].
3436
func NewMultiAdjacencyMatrix() AdjacencyMatrix {
3537
return AdjacencyMatrix{
3638
mat: matrix.NewGrid[int](4, 4),
3739
multi: true,
3840
}
3941
}
4042

41-
// Matrix returns a pointer to the underlying [matrix.M] (of type int).
43+
// Matrix returns a pointer to the underlying [matrix.M] (of type int). Note
44+
// that if the matrix is resized, this value may become an old reference.
4245
func (m AdjacencyMatrix) Matrix() matrix.M[int] {
4346
return m.mat
4447
}
4548

4649
// Get returns the number of directed edges from a source vertex to a target
4750
// vertex (including self-loops, if any).
4851
func (m AdjacencyMatrix) Get(source, target VertexIndex) int {
49-
if int(max(source, target)) > m.Width() { return 0 }
52+
if int(max(source, target)) >= m.mat.Length('x') { return 0 }
5053
idx := m.mat.Index(int(source), int(target))
5154
return m.mat.Get(idx)
5255
}
5356

5457
// Set stores the number of directed edges from a source vertex to a target
5558
// vertex (including self-loops, if any).
56-
func (m AdjacencyMatrix) Set(source, target VertexIndex, count int) {
59+
//
60+
// THe matrix is automatically resized, if necessary.
61+
func (m *AdjacencyMatrix) Set(source, target VertexIndex, count int) {
5762
largest := max(source, target)
58-
m.Resize(int(largest))
63+
m.Resize(int(largest) + 1)
5964
idx := m.mat.Index(int(source), int(target))
6065
m.mat.Set(idx, count)
6166
}
6267

63-
// Width returns the width of the adjacency matrix. Note that if VertexIndexes
64-
// are sparsely distributed, width may be greater the number of vertexes
65-
// produced by iteration.
66-
func (m AdjacencyMatrix) Width() int {
67-
return m.mat.Length(0)
68-
}
69-
7068
// CountEdges returns the total number of edges in the adjacency matrix.
7169
func (m AdjacencyMatrix) CountEdges() int {
70+
// walk the matrix sparsely
7271
sum := 0
73-
74-
// optimisation: walk the matrix sparsely
7572
for idx, ok := -1, true; ok; idx, ok = m.mat.Next(idx) {
7673
if idx < 0 { continue }
7774
sum += m.mat.Get(idx)
7875
}
79-
8076
return sum
8177
}
8278

@@ -86,19 +82,16 @@ func (m AdjacencyMatrix) Clear() {
8682

8783
// Resize updates the adjacency matrix, if necessary, so that it has at least
8884
// capacity for width elements in each dimension.
89-
func (m AdjacencyMatrix) Resize(width int) {
90-
if m.Width() <= width { return }
85+
func (m *AdjacencyMatrix) Resize(width int) {
86+
if m.mat.Length('x') > width { return }
9187
width = int(integer.AlignPowTwo(uint(width)))
9288

93-
// TODO move this into matrix itself
94-
95-
var mcons matrix.M[int]
89+
var dest matrix.M[int]
9690
if m.multi {
97-
mcons = matrix.NewGrid[int](width, width)
91+
dest = matrix.NewGrid[int](width, width)
9892
} else {
99-
mcons = matrix.NewBit[int](width, width)
93+
dest = matrix.NewBit(width, width)
10094
}
101-
dest := mcons
10295
matrix.Copy(dest, m.mat)
10396
m.mat = dest
10497
}
@@ -110,7 +103,7 @@ func (m AdjacencyMatrix) Resize(width int) {
110103
// constant time.
111104
func (m AdjacencyMatrix) Indegree(target VertexIndex) int {
112105
var sum = 0
113-
for i := 0; i < m.Width(); i++ {
106+
for i := 0; i < m.mat.Length('x'); i++ {
114107
idx := m.mat.Index(i, int(target))
115108
sum += m.mat.Get(idx)
116109
}
@@ -124,7 +117,7 @@ func (m AdjacencyMatrix) Indegree(target VertexIndex) int {
124117
// constant time.
125118
func (m AdjacencyMatrix) Outdegree(source VertexIndex) int {
126119
var sum = 0
127-
for i := 0; i < m.Width(); i++ {
120+
for i := 0; i < m.mat.Length('x'); i++ {
128121
idx := m.mat.Index(int(source), i)
129122
sum += m.mat.Get(idx)
130123
}
@@ -140,10 +133,10 @@ func (m AdjacencyMatrix) Degree(source VertexIndex) int {
140133
return m.Indegree(source) + m.Outdegree(source)
141134
}
142135

143-
// Vertexes implements the graph [Iterator] Vertexes method. Every vertex will
144-
// have at least one edge (in either direction).
136+
// Vertexes implements the graph [Iterator] Vertexes method. An AdjacencyMatrix
137+
// is a graph of every vertex that has at least one edge (in either direction).
145138
func (m AdjacencyMatrix) Vertexes() func() (VertexIndex, bool) {
146-
i, width := 0, m.Width()
139+
i, width := 0, m.mat.Length('x')
147140
return func() (_ VertexIndex, _ bool) {
148141
for {
149142
if i >= width { return }
@@ -157,7 +150,7 @@ func (m AdjacencyMatrix) Vertexes() func() (VertexIndex, bool) {
157150

158151
// Edges implements the graph [Iterator] Edges method.
159152
func (m AdjacencyMatrix) Edges(source VertexIndex) func() (VertexIndex, int, bool) {
160-
i, width := 0, m.Width()
153+
i, width := 0 ,m.mat.Length('x')
161154
return func() (_ VertexIndex, _ int, _ bool) {
162155
for {
163156
if i >= width { return }
@@ -186,7 +179,7 @@ func (m AdjacencyMatrix) Weight(source, target VertexIndex) Weight {
186179
// Each vertex index in the adjacency matrix corresponds to a matching index
187180
// in graph g. Once an adjacency matrix has been constructed, it is not
188181
// affected by future changes to graph g.
189-
func (m AdjacencyMatrix) Calculate(g Iterator) {
182+
func (m *AdjacencyMatrix) Calculate(g Iterator) {
190183
width := int(vertexIndexLimit(g.Vertexes))
191184
m.Resize(width)
192185
m.Clear()

ds/graph/bfs.go

+22-27
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package graph
22

33
import (
4+
"math"
5+
46
"github.com/tawesoft/golib/v2/ks"
5-
"github.com/tawesoft/golib/v2/operator/checked"
67
)
78

8-
type vertexBFS[W Weight] struct {
9+
type vertexBFS struct {
910
// predecessor is the vertex immediately previous to this one along one
1011
// possible shortest path. Unreachable and root verticies have this
1112
// set to -1.
@@ -19,44 +20,38 @@ type vertexBFS[W Weight] struct {
1920
// graph, or a graph where every edge has unit weight, this is the same
2021
// cumulative number of edges travelled from source to the destination
2122
// along the shortest path.
22-
distance W
23+
distance Weight
2324
}
2425

2526
// BfsTree represents a (unweighted) breadth-first tree of the reachable
2627
// graph from a given start vertex, taking the shortest number of edges.
2728
//
2829
// A BfsTree is itself a graph (the predecessor subgraph of the graph it
2930
// was constructed from), and implements the [Iterator] interface.
30-
type BfsTree[W Weight] struct {
31-
vertexes []vertexBFS[W]
31+
type BfsTree struct {
32+
vertexes []vertexBFS
3233
start VertexIndex
3334
queue []VertexIndex
3435
}
3536

3637
// NewBfsTree returns a new (empty) breadth-first search tree object for
37-
// storing results. Distances are typed as integers.
38-
func NewBfsTree() *BfsTree[int] {
39-
return NewWeightedBfsTree[int]()
40-
}
41-
42-
// NewWeightedBfsTree returns a new (empty) breadth-first search tree object
43-
// for storing results, with a specified Weight type.
44-
func NewWeightedBfsTree[W Weight]() *BfsTree[W] {
45-
return &BfsTree[W]{
46-
vertexes: make([]vertexBFS[W], 0),
38+
// storing results.
39+
func NewBfsTree() *BfsTree {
40+
return &BfsTree{
41+
vertexes: make([]vertexBFS, 0),
4742
queue: make([]VertexIndex, 0),
4843
}
4944
}
5045

5146
// Resize updates the BfsTree, if necessary, so that it has at least capacity
5247
// for n vertexes. It reuses underlying memory from the existing tree where
5348
// possible. Note that this will clear the tree.
54-
func (t *BfsTree[W]) Resize(n int) {
49+
func (t *BfsTree) Resize(n int) {
5550
t.vertexes = ks.SetLength(t.vertexes, n)
5651
}
5752

58-
func (t *BfsTree[W]) Clear() {
59-
maximum := checked.GetLimits[W]().Max
53+
func (t *BfsTree) Clear() {
54+
maximum := Weight(math.MaxInt)
6055
t.start = 0
6156
clear(t.vertexes)
6257
clear(t.queue)
@@ -70,7 +65,7 @@ func (t *BfsTree[W]) Clear() {
7065

7166
// Reachable returns true if the given vertex is reachable from the root of
7267
// the BfsTree.
73-
func (t BfsTree[W]) Reachable(vertex VertexIndex) bool {
68+
func (t BfsTree) Reachable(vertex VertexIndex) bool {
7469
if (vertex < 0) || (int(vertex) >= len(t.vertexes)) { return false }
7570
// Every reachable vertexBFS has a predecessor, except the root.
7671
return (t.vertexes[vertex].predecessor >= 0) || (vertex == t.start)
@@ -80,7 +75,7 @@ func (t BfsTree[W]) Reachable(vertex VertexIndex) bool {
8075
// Predecessor returns the predecessor of the vertex in the search tree.
8176
// If the vertex is not reachable, or if the vertex is the search start
8277
// vertex, the boolean return value is false.
83-
func (t BfsTree[W]) Predecessor(vertex VertexIndex) (VertexIndex, bool) {
78+
func (t BfsTree) Predecessor(vertex VertexIndex) (VertexIndex, bool) {
8479
if !t.Reachable(vertex) { return 0, false }
8580
predecessor := t.vertexes[vertex].predecessor
8681
if predecessor < 0 { return 0, false }
@@ -90,13 +85,13 @@ func (t BfsTree[W]) Predecessor(vertex VertexIndex) (VertexIndex, bool) {
9085
// Distance returns the cumulative number of edges crossed from the search
9186
// start vertex to the given target. If the vertex is not reachable, the
9287
// boolean return value is false.
93-
func (t BfsTree[W]) Distance(vertex VertexIndex) (W, bool) {
88+
func (t BfsTree) Distance(vertex VertexIndex) (Weight, bool) {
9489
if !t.Reachable(vertex) { return 0, false }
9590
return t.vertexes[vertex].distance, true
9691
}
9792

9893
// Vertexes implements the graph [Iterator] Vertexes method.
99-
func (t BfsTree[W]) Vertexes() VertexIterator {
94+
func (t BfsTree) Vertexes() VertexIterator {
10095
i, width := 0, len(t.vertexes)
10196
return func() (_ VertexIndex, _ bool) {
10297
for {
@@ -113,7 +108,7 @@ func (t BfsTree[W]) Vertexes() VertexIterator {
113108
//
114109
// Each vertex has exactly one directed edge, to its predecessor, except the
115110
// root vertex, which has none.
116-
func (t BfsTree[W]) Edges(source VertexIndex) EdgeIterator {
111+
func (t BfsTree) Edges(source VertexIndex) EdgeIterator {
117112
return func() (_ VertexIndex, _ int, _ bool) {
118113
target, ok := t.Predecessor(source)
119114
if (!ok) { return }
@@ -132,7 +127,7 @@ func (t BfsTree[W]) Edges(source VertexIndex) EdgeIterator {
132127
// There may be many possible breadth-first trees of an input graph. This
133128
// procedure always visits vertexes in the order given by a graph's
134129
// [EdgeIterator].
135-
func (t *BfsTree[W]) CalculateUnweighted(graph Iterator, start VertexIndex) {
130+
func (t *BfsTree) CalculateUnweighted(graph Iterator, start VertexIndex) {
136131
t.Resize(int(vertexIndexLimit(graph.Vertexes)))
137132
t.Clear()
138133

@@ -178,15 +173,15 @@ func (t *BfsTree[W]) CalculateUnweighted(graph Iterator, start VertexIndex) {
178173
// There may be many possible breadth-first trees of an input graph. The order
179174
// taken by this procedure, when two paths have the same weight, is arbitrary
180175
// and subject to change.
181-
func (t *BfsTree[W]) CalculateWeightedGeneral(
176+
func (t *BfsTree) CalculateWeightedGeneral(
182177
graph Iterator,
183178
start VertexIndex,
184-
weight WeightFunc[W],
179+
weight WeightFunc,
185180
) {
186181
// Bellman-Ford algorithm
187182

188183
limit := int(vertexIndexLimit(graph.Vertexes))
189-
maxDistance := checked.GetLimits[W]().Max
184+
maxDistance := Weight(math.MaxInt)
190185
t.Resize(limit)
191186
t.Clear()
192187

ds/graph/bfs_test.go

+6-6
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import (
1010
type TestGraph struct {
1111
// mapping maps Source Vertices to a Set of Target Vertices with a
1212
// given weight.
13-
mapping map[graph.VertexIndex](map[graph.VertexIndex]int)
13+
mapping map[graph.VertexIndex](map[graph.VertexIndex]graph.Weight)
1414
// values maps vertexes to a string
1515
values map[graph.VertexIndex]string
1616
count int
1717
}
1818

1919
func NewTestGraph() *TestGraph {
2020
return &TestGraph{
21-
mapping: make(map[graph.VertexIndex](map[graph.VertexIndex]int)),
21+
mapping: make(map[graph.VertexIndex](map[graph.VertexIndex]graph.Weight)),
2222
values: make(map[graph.VertexIndex]string),
2323
count: 0,
2424
}
@@ -43,15 +43,15 @@ func (g *TestGraph) Vertex(s string) graph.VertexIndex {
4343
value := graph.VertexIndex(g.count)
4444
g.count++
4545
g.values[value] = s
46-
g.mapping[value] = make(map[graph.VertexIndex]int)
46+
g.mapping[value] = make(map[graph.VertexIndex]graph.Weight)
4747
return value
4848
}
4949

50-
func (g *TestGraph) Edge(from, to graph.VertexIndex, weight int) {
50+
func (g *TestGraph) Edge(from, to graph.VertexIndex, weight graph.Weight) {
5151
g.mapping[from][to] = weight
5252
}
5353

54-
func (g *TestGraph) Weight(from, to graph.VertexIndex) int {
54+
func (g *TestGraph) Weight(from, to graph.VertexIndex) graph.Weight {
5555
return g.mapping[from][to]
5656
}
5757

@@ -98,7 +98,7 @@ func TestBfsTree_CalculateUnweighted(t *testing.T) {
9898
type row struct{
9999
vertex graph.VertexIndex
100100
predecessor graph.VertexIndex
101-
distance int
101+
distance graph.Weight
102102
reachable bool
103103
}
104104
stat := func(v graph.VertexIndex) row {

0 commit comments

Comments
 (0)