mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-10-19 05:39:51 +00:00
89da94cfd0
Bumps the github-dependencies group with 2 updates: [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) and [github.com/onsi/gomega](https://github.com/onsi/gomega). Updates `github.com/onsi/ginkgo/v2` from 2.20.1 to 2.20.2 - [Release notes](https://github.com/onsi/ginkgo/releases) - [Changelog](https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/ginkgo/compare/v2.20.1...v2.20.2) Updates `github.com/onsi/gomega` from 1.34.1 to 1.34.2 - [Release notes](https://github.com/onsi/gomega/releases) - [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md) - [Commits](https://github.com/onsi/gomega/compare/v1.34.1...v1.34.2) --- updated-dependencies: - dependency-name: github.com/onsi/ginkgo/v2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-dependencies - dependency-name: github.com/onsi/gomega dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-dependencies ... Signed-off-by: dependabot[bot] <support@github.com>
172 lines
3.9 KiB
Go
172 lines
3.9 KiB
Go
package bipartitegraph
|
||
|
||
import (
|
||
"slices"
|
||
|
||
. "github.com/onsi/gomega/matchers/support/goraph/edge"
|
||
. "github.com/onsi/gomega/matchers/support/goraph/node"
|
||
"github.com/onsi/gomega/matchers/support/goraph/util"
|
||
)
|
||
|
||
// LargestMatching implements the Hopcroft–Karp algorithm taking as input a bipartite graph
|
||
// and outputting a maximum cardinality matching, i.e. a set of as many edges as possible
|
||
// with the property that no two edges share an endpoint.
|
||
func (bg *BipartiteGraph) LargestMatching() (matching EdgeSet) {
|
||
paths := bg.maximalDisjointSLAPCollection(matching)
|
||
|
||
for len(paths) > 0 {
|
||
for _, path := range paths {
|
||
matching = matching.SymmetricDifference(path)
|
||
}
|
||
paths = bg.maximalDisjointSLAPCollection(matching)
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
func (bg *BipartiteGraph) maximalDisjointSLAPCollection(matching EdgeSet) (result []EdgeSet) {
|
||
guideLayers := bg.createSLAPGuideLayers(matching)
|
||
if len(guideLayers) == 0 {
|
||
return
|
||
}
|
||
|
||
used := make(map[int]bool)
|
||
|
||
for _, u := range guideLayers[len(guideLayers)-1] {
|
||
slap, found := bg.findDisjointSLAP(u, matching, guideLayers, used)
|
||
if found {
|
||
for _, edge := range slap {
|
||
used[edge.Node1] = true
|
||
used[edge.Node2] = true
|
||
}
|
||
result = append(result, slap)
|
||
}
|
||
}
|
||
|
||
return
|
||
}
|
||
|
||
func (bg *BipartiteGraph) findDisjointSLAP(
|
||
start Node,
|
||
matching EdgeSet,
|
||
guideLayers []NodeOrderedSet,
|
||
used map[int]bool,
|
||
) ([]Edge, bool) {
|
||
return bg.findDisjointSLAPHelper(start, EdgeSet{}, len(guideLayers)-1, matching, guideLayers, used)
|
||
}
|
||
|
||
func (bg *BipartiteGraph) findDisjointSLAPHelper(
|
||
currentNode Node,
|
||
currentSLAP EdgeSet,
|
||
currentLevel int,
|
||
matching EdgeSet,
|
||
guideLayers []NodeOrderedSet,
|
||
used map[int]bool,
|
||
) (EdgeSet, bool) {
|
||
used[currentNode.ID] = true
|
||
|
||
if currentLevel == 0 {
|
||
return currentSLAP, true
|
||
}
|
||
|
||
for _, nextNode := range guideLayers[currentLevel-1] {
|
||
if used[nextNode.ID] {
|
||
continue
|
||
}
|
||
|
||
edge, found := bg.Edges.FindByNodes(currentNode, nextNode)
|
||
if !found {
|
||
continue
|
||
}
|
||
|
||
if matching.Contains(edge) == util.Odd(currentLevel) {
|
||
continue
|
||
}
|
||
|
||
currentSLAP = append(currentSLAP, edge)
|
||
slap, found := bg.findDisjointSLAPHelper(nextNode, currentSLAP, currentLevel-1, matching, guideLayers, used)
|
||
if found {
|
||
return slap, true
|
||
}
|
||
currentSLAP = currentSLAP[:len(currentSLAP)-1]
|
||
}
|
||
|
||
used[currentNode.ID] = false
|
||
return nil, false
|
||
}
|
||
|
||
func (bg *BipartiteGraph) createSLAPGuideLayers(matching EdgeSet) (guideLayers []NodeOrderedSet) {
|
||
used := make(map[int]bool)
|
||
currentLayer := NodeOrderedSet{}
|
||
|
||
for _, node := range bg.Left {
|
||
if matching.Free(node) {
|
||
used[node.ID] = true
|
||
currentLayer = append(currentLayer, node)
|
||
}
|
||
}
|
||
|
||
if len(currentLayer) == 0 {
|
||
return []NodeOrderedSet{}
|
||
}
|
||
guideLayers = append(guideLayers, currentLayer)
|
||
|
||
done := false
|
||
|
||
for !done {
|
||
lastLayer := currentLayer
|
||
currentLayer = NodeOrderedSet{}
|
||
|
||
if util.Odd(len(guideLayers)) {
|
||
for _, leftNode := range lastLayer {
|
||
for _, rightNode := range bg.Right {
|
||
if used[rightNode.ID] {
|
||
continue
|
||
}
|
||
|
||
edge, found := bg.Edges.FindByNodes(leftNode, rightNode)
|
||
if !found || matching.Contains(edge) {
|
||
continue
|
||
}
|
||
|
||
currentLayer = append(currentLayer, rightNode)
|
||
used[rightNode.ID] = true
|
||
|
||
if matching.Free(rightNode) {
|
||
done = true
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
for _, rightNode := range lastLayer {
|
||
for _, leftNode := range bg.Left {
|
||
if used[leftNode.ID] {
|
||
continue
|
||
}
|
||
|
||
edge, found := bg.Edges.FindByNodes(leftNode, rightNode)
|
||
if !found || !matching.Contains(edge) {
|
||
continue
|
||
}
|
||
|
||
currentLayer = append(currentLayer, leftNode)
|
||
used[leftNode.ID] = true
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
if len(currentLayer) == 0 {
|
||
return []NodeOrderedSet{}
|
||
}
|
||
if done { // if last layer - into last layer must be only 'free' nodes
|
||
currentLayer = slices.DeleteFunc(currentLayer, func(in Node) bool {
|
||
return !matching.Free(in)
|
||
})
|
||
}
|
||
guideLayers = append(guideLayers, currentLayer)
|
||
}
|
||
|
||
return
|
||
}
|