package hierarchyimport ()// The routines below implement the algorithm described in `Fig. 1`, and section// `4 Extending Sifting to k-Layered Directed Graphs`, of// "Using Sifting for k-Layer Straightline Crossing Minimization" by Matuszewski et al.// This iterative algorithm tries to find the optimal position for a given node// initialize a queue of nodes in descending order// for each node in the queue// swap with its siblings all the way to right// swap all the way the left// swap to the position with best crossings or length (this is the optimal position)// swapping with neighbors is a local operation, so it should fast to compute new crossings// however, as we handle containers, swapping container can lead to major changes and then we// can't take full advantage of the local swaps// main changes from the algorithm described in the paper:// - they don't handle edge length// - they reverse the queue if there's no improvement// - my udnerstanding is that the algorithm always process the whole queue, so it doesn't make sense to reverse it// - if there's no improvement, allow changes if crossings or length stay the same (escape local minima)// - if there's still no improvement, stopfunc globalSifting( map[int][]*placementNode) error { := nodesInDescendingDegreeOrder() := buildNodeToSiblings(, )varcrossingScratch := false// 10 is just a killswitch (there's no suggested value in the paper)for := 0; < 10; ++ { := make([]*placementNode, len())copy(, ) := falseforlen() > 0 { := [0] = [1:] := .rankif := sifting(, [], , , &); != nil {return }if .rank != { = true } }if ! {if {// no improvement even considering reordering for the same crossings countbreak } = true } else { = false } }returnnil}func nodesInDescendingDegreeOrder( map[int][]*placementNode) []*placementNode {var []*placementNodefor := 0; < len(); ++ { = append(, allDescendants([], true)...) }slices.SortStableFunc(, func(, *placementNode) int {if := cmp.Compare(.degree(), .degree()); != 0 {return }// If nodes have the same degree, sort by order from top-left.if := cmp.Compare(.level, .level); != 0 {return }returncmp.Compare(.rank, .rank) })return}func buildNodeToSiblings( []*placementNode, map[int][]*placementNode) map[*placementNode][]*placementNode { := make(map[*placementNode][]*placementNode)for , := range {if .container != nil { [] = .container.children } else { [] = [.level] } }return}func sifting( *placementNode, []*placementNode, map[int][]*placementNode, bool, *crossingScratch) error { := .crossLevelSegments(, true, true) := countCrossings() := addLengths() := -1if == 0 || len() == 1 {returnnil } elseif == [len()-1] {// handle edge case where the node is already the right most one = len() - 1 } else {// move all the way to the rightfor := 0; < len()-1; ++ {if == -1 {if [] == { = } else {continue } } [+1], [] = [], [+1]computeLevelRanks([.level]) := .crossLevelSegments(, true, true) := countCrossings() := addLengths()ifimproved(, , , , ) { = = + 1 = } }if [len()-1] != {returnfmt.Errorf("sifting: expected node %s to have moved to the right of siblings slice", .graphNode.DebugID()) }if == -1 {returnfmt.Errorf("sifting: node %s not found", .graphNode.DebugID()) } }// move all the way to the leftfor := len() - 1; > 0; -- { [-1], [] = [], [-1]computeLevelRanks([.level]) := .crossLevelSegments(, true, true) := countCrossings() := addLengths()ifimproved(, , , , ) { = = - 1 = } }if [0] != {returnfmt.Errorf("sifting: expected node %s to have moved to the left of siblings slice", .graphNode.DebugID()) }// swap the node to the best rank and update the rank of all siblingsfor := 0; < len(); ++ {if == {// at this moment, the node was already swapped to bestLocalRankbreak } [+1], [] = [], [+1] }computeLevelRanks([.level])returnnil}func improved(, int64, , float64, bool) bool {if ! {return < || ( == && geo.PrecisionCompare(, , geo.PRECISION) == -1) }return <= || ( == && geo.PrecisionCompare(, , geo.PRECISION) < 1)}
The pages are generated with Goldsv0.8.7. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.