github.com/onsi/gomega@v1.32.0/matchers/support/goraph/bipartitegraph/bipartitegraphmatching.go (about) 1 package bipartitegraph 2 3 import ( 4 . "github.com/onsi/gomega/matchers/support/goraph/edge" 5 . "github.com/onsi/gomega/matchers/support/goraph/node" 6 "github.com/onsi/gomega/matchers/support/goraph/util" 7 ) 8 9 // LargestMatching implements the Hopcroft–Karp algorithm taking as input a bipartite graph 10 // and outputting a maximum cardinality matching, i.e. a set of as many edges as possible 11 // with the property that no two edges share an endpoint. 12 func (bg *BipartiteGraph) LargestMatching() (matching EdgeSet) { 13 paths := bg.maximalDisjointSLAPCollection(matching) 14 15 for len(paths) > 0 { 16 for _, path := range paths { 17 matching = matching.SymmetricDifference(path) 18 } 19 paths = bg.maximalDisjointSLAPCollection(matching) 20 } 21 22 return 23 } 24 25 func (bg *BipartiteGraph) maximalDisjointSLAPCollection(matching EdgeSet) (result []EdgeSet) { 26 guideLayers := bg.createSLAPGuideLayers(matching) 27 if len(guideLayers) == 0 { 28 return 29 } 30 31 used := make(map[int]bool) 32 33 for _, u := range guideLayers[len(guideLayers)-1] { 34 slap, found := bg.findDisjointSLAP(u, matching, guideLayers, used) 35 if found { 36 for _, edge := range slap { 37 used[edge.Node1] = true 38 used[edge.Node2] = true 39 } 40 result = append(result, slap) 41 } 42 } 43 44 return 45 } 46 47 func (bg *BipartiteGraph) findDisjointSLAP( 48 start Node, 49 matching EdgeSet, 50 guideLayers []NodeOrderedSet, 51 used map[int]bool, 52 ) ([]Edge, bool) { 53 return bg.findDisjointSLAPHelper(start, EdgeSet{}, len(guideLayers)-1, matching, guideLayers, used) 54 } 55 56 func (bg *BipartiteGraph) findDisjointSLAPHelper( 57 currentNode Node, 58 currentSLAP EdgeSet, 59 currentLevel int, 60 matching EdgeSet, 61 guideLayers []NodeOrderedSet, 62 used map[int]bool, 63 ) (EdgeSet, bool) { 64 used[currentNode.ID] = true 65 66 if currentLevel == 0 { 67 return currentSLAP, true 68 } 69 70 for _, nextNode := range guideLayers[currentLevel-1] { 71 if used[nextNode.ID] { 72 continue 73 } 74 75 edge, found := bg.Edges.FindByNodes(currentNode, nextNode) 76 if !found { 77 continue 78 } 79 80 if matching.Contains(edge) == util.Odd(currentLevel) { 81 continue 82 } 83 84 currentSLAP = append(currentSLAP, edge) 85 slap, found := bg.findDisjointSLAPHelper(nextNode, currentSLAP, currentLevel-1, matching, guideLayers, used) 86 if found { 87 return slap, true 88 } 89 currentSLAP = currentSLAP[:len(currentSLAP)-1] 90 } 91 92 used[currentNode.ID] = false 93 return nil, false 94 } 95 96 func (bg *BipartiteGraph) createSLAPGuideLayers(matching EdgeSet) (guideLayers []NodeOrderedSet) { 97 used := make(map[int]bool) 98 currentLayer := NodeOrderedSet{} 99 100 for _, node := range bg.Left { 101 if matching.Free(node) { 102 used[node.ID] = true 103 currentLayer = append(currentLayer, node) 104 } 105 } 106 107 if len(currentLayer) == 0 { 108 return []NodeOrderedSet{} 109 } 110 guideLayers = append(guideLayers, currentLayer) 111 112 done := false 113 114 for !done { 115 lastLayer := currentLayer 116 currentLayer = NodeOrderedSet{} 117 118 if util.Odd(len(guideLayers)) { 119 for _, leftNode := range lastLayer { 120 for _, rightNode := range bg.Right { 121 if used[rightNode.ID] { 122 continue 123 } 124 125 edge, found := bg.Edges.FindByNodes(leftNode, rightNode) 126 if !found || matching.Contains(edge) { 127 continue 128 } 129 130 currentLayer = append(currentLayer, rightNode) 131 used[rightNode.ID] = true 132 133 if matching.Free(rightNode) { 134 done = true 135 } 136 } 137 } 138 } else { 139 for _, rightNode := range lastLayer { 140 for _, leftNode := range bg.Left { 141 if used[leftNode.ID] { 142 continue 143 } 144 145 edge, found := bg.Edges.FindByNodes(leftNode, rightNode) 146 if !found || !matching.Contains(edge) { 147 continue 148 } 149 150 currentLayer = append(currentLayer, leftNode) 151 used[leftNode.ID] = true 152 } 153 } 154 155 } 156 157 if len(currentLayer) == 0 { 158 return []NodeOrderedSet{} 159 } 160 guideLayers = append(guideLayers, currentLayer) 161 } 162 163 return 164 }