Skip to content

Commit ebadb67

Browse files
timmyb32ronsi
authored andcommittedJul 25, 2024·
issue_765 - fixed bug in Hopcroft-Karp algorithm
1 parent 123a071 commit ebadb67

File tree

2 files changed

+97
-0
lines changed

2 files changed

+97
-0
lines changed
 

‎matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go

+6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
. "github.com/onsi/gomega/matchers/support/goraph/edge"
55
. "github.com/onsi/gomega/matchers/support/goraph/node"
66
"github.com/onsi/gomega/matchers/support/goraph/util"
7+
"slices"
78
)
89

910
// LargestMatching implements the Hopcroft–Karp algorithm taking as input a bipartite graph
@@ -157,6 +158,11 @@ func (bg *BipartiteGraph) createSLAPGuideLayers(matching EdgeSet) (guideLayers [
157158
if len(currentLayer) == 0 {
158159
return []NodeOrderedSet{}
159160
}
161+
if done { // if last layer - into last layer must be only 'free' nodes
162+
currentLayer = slices.DeleteFunc(currentLayer, func(in Node)bool{
163+
return !matching.Free(in)
164+
})
165+
}
160166
guideLayers = append(guideLayers, currentLayer)
161167
}
162168

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package bipartitegraph
2+
3+
import (
4+
"github.com/onsi/gomega/matchers/support/goraph/edge"
5+
"slices"
6+
"testing"
7+
)
8+
9+
func buildEdgesArr(l, r []interface{}, edges edge.EdgeSet) []string {
10+
unpackArr := func(in []interface{}) []string {
11+
result := make([]string, 0, len(in))
12+
for _, el := range in {
13+
result = append(result, el.(string))
14+
}
15+
return result
16+
}
17+
18+
vertexes := unpackArr(append(l, r...))
19+
20+
result := make([]string, 0)
21+
for _, currEdge := range edges {
22+
result = append(result, vertexes[currEdge.Node1]+"-"+vertexes[currEdge.Node2])
23+
}
24+
return result
25+
}
26+
27+
func expectedContains(t *testing.T, expected string, edges []string) {
28+
idx := slices.IndexFunc(edges, func(c string) bool { return c == expected })
29+
if idx == -1 {
30+
t.Fatalf("edges %v not contains expected: %s", edges, expected)
31+
}
32+
}
33+
34+
func TestMaximumCardinalityMatch(t *testing.T) {
35+
edgesFunc := func(l, r interface{}) (bool, error) {
36+
ll := l.(string)
37+
rr := r.(string)
38+
39+
type currEdge struct {
40+
l string
41+
r string
42+
}
43+
knownEdges := []currEdge{
44+
{"1", "A"},
45+
{"1", "B"},
46+
{"1", "C"},
47+
{"1", "D"},
48+
{"1", "E"},
49+
{"2", "A"},
50+
{"2", "D"},
51+
{"3", "B"},
52+
{"3", "D"},
53+
{"4", "B"},
54+
{"4", "D"},
55+
{"4", "E"},
56+
{"5", "A"},
57+
}
58+
59+
for _, el := range knownEdges {
60+
if el.l == ll && el.r == rr {
61+
return true, nil
62+
}
63+
}
64+
return false, nil
65+
}
66+
67+
leftPart := []interface{}{"1", "2", "3", "4", "5"}
68+
rightPart := []interface{}{"A", "B", "C", "D", "E"}
69+
70+
bipartiteGraph, err := NewBipartiteGraph(
71+
leftPart,
72+
rightPart,
73+
edgesFunc,
74+
)
75+
if err != nil {
76+
t.Fatalf("NewBipartiteGraph returned error: %v", err)
77+
}
78+
if err != nil {
79+
t.Fatal(err)
80+
}
81+
edgeSet := bipartiteGraph.LargestMatching()
82+
if len(edgeSet) != 5 {
83+
t.Fatalf("bipartiteGraph.LargestMatching() returned not 5 elements: %v", edgeSet)
84+
}
85+
edges := buildEdgesArr(leftPart, rightPart, edgeSet)
86+
expectedContains(t, "1-C", edges)
87+
expectedContains(t, "2-D", edges)
88+
expectedContains(t, "3-B", edges)
89+
expectedContains(t, "4-E", edges)
90+
expectedContains(t, "5-A", edges)
91+
}

0 commit comments

Comments
 (0)
Please sign in to comment.