From 1a4c12001c884288c7bc38db46f3869d4c16612e Mon Sep 17 00:00:00 2001 From: "lorenzovonmatternhorn@gmx.ch" <36513234+cem-okulmus@users.noreply.github.com> Date: Sun, 5 Apr 2020 16:29:08 +0200 Subject: [PATCH] Integrated hingetree optimization, some fixes to GYO --- algorithms/algorithms.go | 1 + algorithms/balDetKDecomp.go | 4 + algorithms/balKDecomp.go | 4 + algorithms/balsepGlobal.go | 4 + algorithms/balsepLocal.go | 4 + algorithms/detKDecomp.go | 4 + algorithms/divideKDecomp.go | 16 ++++ balanced.go | 41 +++++++-- lib/hinge.go | 165 ++++++++++++++++++++++++++++-------- lib/preprocessing.go | 17 ++-- 10 files changed, 214 insertions(+), 46 deletions(-) diff --git a/algorithms/algorithms.go b/algorithms/algorithms.go index e390176..886c8bd 100644 --- a/algorithms/algorithms.go +++ b/algorithms/algorithms.go @@ -5,6 +5,7 @@ import . "github.com/cem-okulmus/BalancedGo/lib" type Algorithm interface { Name() string FindDecomp(K int) Decomp + FindDecompGraph(G Graph, K int) Decomp } type UpdateAlgorithm interface { diff --git a/algorithms/balDetKDecomp.go b/algorithms/balDetKDecomp.go index 4f4abb7..e075ef8 100644 --- a/algorithms/balDetKDecomp.go +++ b/algorithms/balDetKDecomp.go @@ -212,6 +212,10 @@ func (b BalDetKDecomp) FindDecomp(K int) Decomp { return b.FindGHD(K, b.Graph, []Special{}) } +func (b BalDetKDecomp) FindDecompGraph(G Graph, K int) Decomp { + return b.FindGHD(K, G, []Special{}) +} + func (b BalDetKDecomp) FindDecompUpdate(K int, currentGraph Graph, Sp []Special) Decomp { fmt.Println("Ghost edges", b.Graph.Edges) diff --git a/algorithms/balKDecomp.go b/algorithms/balKDecomp.go index 506fd47..bf162bf 100644 --- a/algorithms/balKDecomp.go +++ b/algorithms/balKDecomp.go @@ -118,6 +118,10 @@ func (b BalKDecomp) FindDecomp(K int) Decomp { return b.findDecompParallelFull(K, b.Graph) } +func (b BalKDecomp) FindDecompGraph(G Graph, K int) Decomp { + return b.findDecompParallelFull(K, G) +} + func (b BalKDecomp) Name() string { return "Akatov" } diff --git a/algorithms/balsepGlobal.go b/algorithms/balsepGlobal.go index fc5dc9a..548e20d 100644 --- a/algorithms/balsepGlobal.go +++ b/algorithms/balsepGlobal.go @@ -475,6 +475,10 @@ func (g BalSepGlobal) FindDecomp(K int) Decomp { return g.findDecompParallelFull(K, g.Graph, []Special{}) } +func (b BalSepGlobal) FindDecompGraph(G Graph, K int) Decomp { + return b.findDecompParallelFull(K, G, []Special{}) +} + func (g BalSepGlobal) Name() string { return "BalSep Global" } diff --git a/algorithms/balsepLocal.go b/algorithms/balsepLocal.go index 9e8a8d1..4283670 100644 --- a/algorithms/balsepLocal.go +++ b/algorithms/balsepLocal.go @@ -440,6 +440,10 @@ func (g BalSepLocal) FindDecomp(K int) Decomp { return g.findDecompParallelFull(K, g.Graph, []Special{}) } +func (g BalSepLocal) FindDecompGraph(G Graph, K int) Decomp { + return g.findDecompParallelFull(K, G, []Special{}) +} + func (g BalSepLocal) Name() string { return "BalSep Local" } diff --git a/algorithms/detKDecomp.go b/algorithms/detKDecomp.go index 1c18061..21d9bb8 100644 --- a/algorithms/detKDecomp.go +++ b/algorithms/detKDecomp.go @@ -326,6 +326,10 @@ func (d DetKDecomp) Name() string { } } +func (d DetKDecomp) FindDecompGraph(G Graph, K int) Decomp { + return d.FindHD(K, G, []Special{}) +} + func (d DetKDecomp) FindDecompUpdate(K int, currentGraph Graph, Sp []Special) Decomp { fmt.Println("Ghost edges", d.Graph.Edges) return d.FindHD(K, d.Graph, Sp) diff --git a/algorithms/divideKDecomp.go b/algorithms/divideKDecomp.go index 3e1d220..48af493 100644 --- a/algorithms/divideKDecomp.go +++ b/algorithms/divideKDecomp.go @@ -544,6 +544,13 @@ func (d DivideKDecomp) FindDecomp(K int) Decomp { return output } +func (d DivideKDecomp) FindDecompGraph(G Graph, K int) Decomp { + output := d.decomp(DivideComp{Edges: G.Edges}) + output.Root = postProcess(output.Root, []int{}) + + return output +} + func (d DivideKDecomp) Name() string { return "DivideK" } @@ -564,6 +571,15 @@ func (d DivideKDecompPar) FindDecomp(K int) Decomp { return output } +func (d DivideKDecompPar) FindDecompGraph(G Graph, K int) Decomp { + div := DivideKDecomp{Graph: d.Graph, K: d.K, BalFactor: d.BalFactor} + + output := div.decompParallel(DivideComp{Edges: G.Edges}) + output.Root = postProcess(output.Root, []int{}) + + return output +} + func (d DivideKDecompPar) Name() string { return "DivideK-Par" } diff --git a/balanced.go b/balanced.go index f625a4d..9cace17 100644 --- a/balanced.go +++ b/balanced.go @@ -35,7 +35,7 @@ var Version string var Date string var Build string -func outputStanza(algorithm string, decomp Decomp, msec float64, parsedGraph Graph, gml string, K int, heuristic float64) { +func outputStanza(algorithm string, decomp Decomp, msec float64, parsedGraph Graph, gml string, K int, heuristic float64, hinge float64) { decomp.RestoreSubedges() fmt.Println("Used algorithm: " + algorithm + " @" + Version) @@ -51,6 +51,9 @@ func outputStanza(algorithm string, decomp Decomp, msec float64, parsedGraph Gra fmt.Print("(", msec/60000.0, "min )") } } + if hinge > 0.0 { + fmt.Println("\nHingeTree: ", hinge, " ms") + } fmt.Println("\nWidth: ", decomp.CheckWidth()) correct := decomp.Correct(parsedGraph) @@ -84,7 +87,7 @@ func main() { useHeuristic := flagSet.Int("heuristic", 0, "turn on to activate edge ordering\n\t1 ... Degree Ordering\n\t2 ... Max. Separator Ordering\n\t3 ... MCSO\n\t4 ... Edge Degree Ordering") gyö := flagSet.Bool("g", false, "perform a GYÖ reduct") typeC := flagSet.Bool("t", false, "perform a Type Collapse") - //hingeFlag := flagSet.Bool("hinge", false, "use isHinge Optimization") + hingeFlag := flagSet.Bool("h", false, "use hingeTree Optimization") numCPUs := flagSet.Int("cpu", -1, "Set number of CPUs to use") bench := flagSet.Bool("bench", false, "Benchmark mode, reduces unneeded output (incompatible with -log flag)") akatovTest := flagSet.Bool("akatov", false, "Use Balanced Decomposition algorithm") @@ -251,6 +254,23 @@ func main() { fmt.Println("Graph with subedges \n", parsedGraph) } + var hinget Hingetree + var msecHinge float64 + + if *hingeFlag { + startHinge := time.Now() + + hinget = GetHingeTree(parsedGraph) + + dHinge := time.Now().Sub(startHinge) + msecHinge = dHinge.Seconds() * float64(time.Second/time.Millisecond) + + if !*bench { + fmt.Println("Produced Hingetree: ") + fmt.Println(hinget) + } + } + if *update { var solver UpdateAlgorithm @@ -289,7 +309,7 @@ func main() { } } - outputStanza(solver.Name(), decomp, msec, parsedGraph, *gml, *width, heuristic) + outputStanza(solver.Name(), decomp, msec, parsedGraph, *gml, *width, heuristic, msecHinge) return } @@ -358,13 +378,22 @@ func main() { k := 1 solved := false for !solved { - decomp = solver.FindDecomp(k) + if *hingeFlag { + decomp = hinget.DecompHinge(solver, k, parsedGraph) + } else { + decomp = solver.FindDecomp(k) + } + solved = decomp.Correct(parsedGraph) k++ } *width = k - 1 // for correct output } else { - decomp = solver.FindDecomp(*width) + if *hingeFlag { + decomp = hinget.DecompHinge(solver, *width, parsedGraph) + } else { + decomp = solver.FindDecomp(*width) + } } if *akatovTest { @@ -390,7 +419,7 @@ func main() { } } - outputStanza(solver.Name(), decomp, msec, parsedGraph, *gml, *width, heuristic) + outputStanza(solver.Name(), decomp, msec, parsedGraph, *gml, *width, heuristic, msecHinge) return } diff --git a/lib/hinge.go b/lib/hinge.go index 25a05db..143bda2 100644 --- a/lib/hinge.go +++ b/lib/hinge.go @@ -5,23 +5,35 @@ package lib import ( "bytes" "log" + "reflect" ) type hingeEdge struct { - h hingetree + h Hingetree e Edge } -type hingetree struct { +type Algorithm_h interface { + Name() string + FindDecomp(K int) Decomp + FindDecompGraph(G Graph, K int) Decomp +} + +type Hingetree struct { hinge Graph + decomp Decomp minimal bool children []hingeEdge } -func (h hingetree) stringIdent(i int) string { +func (h Hingetree) stringIdent(i int) string { var buffer bytes.Buffer - buffer.WriteString("\n" + indent(i) + h.hinge.String() + "\n") + if reflect.DeepEqual(h.decomp, Decomp{}) { + buffer.WriteString("\n" + indent(i) + h.hinge.String() + "\n") + } else { + buffer.WriteString("\n" + indent(i) + h.decomp.String() + "\n") + } if len(h.children) > 0 { buffer.WriteString(indent(i) + "Children:\n" + indent(i) + "[") @@ -35,13 +47,13 @@ func (h hingetree) stringIdent(i int) string { return buffer.String() } -func (h hingetree) String() string { +func (h Hingetree) String() string { return h.stringIdent(0) } -func getHingeTree(g Graph) hingetree { +func GetHingeTree(g Graph) Hingetree { - initialTree := hingetree{hinge: g} + initialTree := Hingetree{hinge: g} isUsed := make(map[int]bool) @@ -49,19 +61,19 @@ func getHingeTree(g Graph) hingetree { isUsed[e.Name] = false } - // // fmt.Println("Initial Hingetree \n") - // // fmt.Println(initialTree.String()) + //fmt.Println("Initial Hingetree \n") + //fmt.Println(initialTree.String()) output := initialTree.expandHingeTree(isUsed, -1) - // fmt.Println("Proudced Hingetree \n") - // fmt.Println(output.String()) + //fmt.Println("Proudced Hingetree \n") + //fmt.Println(output.String()) return output } // Following Gyssens, 1994, implemented re -func (h hingetree) expandHingeTree(isUsed map[int]bool, parentE int) hingetree { +func (h Hingetree) expandHingeTree(isUsed map[int]bool, parentE int) Hingetree { // keep expanding the current node until no more new children can be generated for !h.minimal { @@ -84,61 +96,61 @@ func (h hingetree) expandHingeTree(isUsed map[int]bool, parentE int) hingetree { if e == nil { h.minimal = true - // fmt.Println("Setting hinge ", h.hinge, " to minimal") + //fmt.Println("Setting hinge ", h.hinge, " to minimal") continue } if parentE != -1 { - // fmt.Println("Next unused edge", *e, "with parent Edge: ", m[parentE]) + //fmt.Println("Next unused edge", *e, "with parent Edge: ", m[parentE]) } else { - // fmt.Println("Next unused edge", *e, " at root") + //fmt.Println("Next unused edge", *e, " at root") } sepEdge := NewEdges([]Edge{*e}) hinges, _, gamma := h.hinge.GetComponents(sepEdge, []Special{}) - // fmt.Printf("Hinges of Sep: %v\n", hinges) + //fmt.Printf("Hinges of Sep: %v\n", hinges) // Skip reordering step if only single component - if len(hinges) == 1 { - // fmt.Println("skipping sepEdge since no progress") + if len(hinges) <= 1 { + //fmt.Println("skipping sepEdge since no progress") continue } - // hinges into hingetrees, preserving order - var htrees []hingetree + // hinges into Hingetrees, preserving order + var htrees []Hingetree for i := range hinges { edges := hinges[i].Edges.Slice() extendedHinge := Graph{Edges: NewEdges(append(edges, *e))} - htrees = append(htrees, hingetree{hinge: extendedHinge}) + htrees = append(htrees, Hingetree{hinge: extendedHinge}) } - // then assign the children to each hingetree + // then assign the children to each Hingetree for _, hingedge := range h.children { i := gamma[hingedge.e.Name] htrees[i].children = append(htrees[i].children, hingedge) } - // finally add hingetrees above to newchildren + // finally add Hingetrees above to newchildren if parentE == -1 { h = htrees[0] } else { h = htrees[gamma[parentE]] - found := false + // found := false - for _, e := range h.hinge.Edges.Slice() { - if e.Name == parentE { - found = true - } - } + // for _, e := range h.hinge.Edges.Slice() { + // if e.Name == parentE { + // found = true + // } + // } - if !found { - log.Panic(m[parentE], "does not occur in", htrees[gamma[parentE]].hinge) - } + // if !found { + // log.Panic(m[parentE], "does not occur in", htrees[gamma[parentE]].hinge) + // } } @@ -149,12 +161,12 @@ func (h hingetree) expandHingeTree(isUsed map[int]bool, parentE int) hingetree { h.children = append(h.children, hingeEdge{e: *e, h: htrees[i]}) } - // fmt.Println("Current Hingetree \n") - // fmt.Println(h.String()) + //fmt.Println("Current Hingetree \n") + //fmt.Println(h.String()) } - // fmt.Println("going over children") + //fmt.Println("going over children") // recursively repeat procedure over all children of h @@ -164,3 +176,86 @@ func (h hingetree) expandHingeTree(isUsed map[int]bool, parentE int) hingetree { return h } + +func (n Node) containsSubset(subset []int) bool { + // every node contains itself + if Subset(subset, n.Bag) { + return true + } + // Check recursively if contained in children + for _, child := range n.Children { + if child.containsSubset(subset) { + return true + } + } + + return false +} + +func (n Node) parentSubset(subset []int) Node { + // Check recursively if subset covered in children + for i := range n.Children { + if Subset(subset, n.Children[i].Bag) { + return n + } else if n.Children[i].containsSubset(subset) { + return n.Children[i].parentSubset(subset) + } + + } + + return n +} + +// reroot G at node covering edge, producing an isomorphic graph +func (n Node) RerootEdge(edge []int) Node { + + if !n.containsSubset(edge) { + log.Panicf("Can't reRoot: no node covering %+v in node %+v!\n", PrintVertices(edge), n) + } + if Subset(edge, n.Bag) { + return n + } + p := n.parentSubset(edge) + p = n.Reroot(p) + var child Node + + // remove node containing edge from children of parent + var newparentchildren []Node + for _, c := range p.Children { + if Subset(edge, c.Bag) { + child = c + continue + } + newparentchildren = append(newparentchildren, c) + } + p.Children = newparentchildren + newchildren := append(child.Children, p) + + return Node{Bag: child.Bag, Cover: child.Cover, Children: newchildren} +} + +func (h Hingetree) DecompHinge(alg Algorithm_h, K int, g Graph) Decomp { + + h.decomp = alg.FindDecompGraph(h.hinge, K) + + if reflect.DeepEqual(h.decomp, Decomp{}) { + return Decomp{} + } + + // go recursively over children + + for i := range h.children { + out := h.children[i].h.DecompHinge(alg, K, g) + if reflect.DeepEqual(out, Decomp{}) { // reject if subtree cannot be merged to GHD + return Decomp{} + } + //reroot child and parent to a connecting node: + out.Root = out.Root.RerootEdge(h.children[i].e.Vertices) + h.decomp.Root = h.decomp.Root.RerootEdge(h.children[i].e.Vertices) + + h.decomp.Root.Children = append(h.decomp.Root.Children, out.Root) + } + + h.decomp.Graph = g + return h.decomp +} diff --git a/lib/preprocessing.go b/lib/preprocessing.go index 926f482..80a27b4 100644 --- a/lib/preprocessing.go +++ b/lib/preprocessing.go @@ -25,7 +25,7 @@ func (_ edgeOp) isGYÖ() {} func (e edgeOp) String() string { mutex.RLock() defer mutex.RUnlock() - return fmt.Sprintf("(%v ⊆ %v)", m[e.subedge.Name], m[e.parent.Name]) + return fmt.Sprintf("(%v ⊆ %v)", e.subedge, e.parent) } type vertOp struct { @@ -38,7 +38,7 @@ func (_ vertOp) isGYÖ() {} func (v vertOp) String() string { mutex.RLock() defer mutex.RUnlock() - return fmt.Sprintf("(%v ∈ %v)", m[v.vertex], m[v.edge.Name]) + return fmt.Sprintf("(%v ∈ %v)", m[v.vertex], v.edge) } //TODO fix this to run in linear time @@ -70,16 +70,22 @@ func (g Graph) removeVertices() (Graph, []GYÖReduct) { for _, e1 := range g.Edges.Slice() { var vertices []int - // fmt.Println("Working on edge ", e1) + var remVertices []int + // fmt.Println("Working on edge ", e1) INNER: for _, v := range e1.Vertices { - // fmt.Printf("Degree of %v is %v\n", m[v], getDegree(g.Edges, v)) + // fmt.Printf("Degree of %v is %v\n", m[v], getDegree(g.Edges, v)) if getDegree(g.Edges, v) == 1 { - ops = append(ops, vertOp{vertex: v, edge: Edge{Name: e1.Name, Vertices: vertices}}) + remVertices = append(remVertices, v) continue INNER } vertices = append(vertices, v) } + + for _, remV := range remVertices { + ops = append(ops, vertOp{vertex: remV, edge: Edge{Name: e1.Name, Vertices: vertices}}) + } + if len(vertices) > 0 { edges = append(edges, Edge{Name: e1.Name, Vertices: vertices}) } @@ -226,6 +232,7 @@ func (n Node) RestoreGYÖ(reducts []GYÖReduct) (Node, bool) { output, result = output.restoreEdgeOp(v) } if !result { + fmt.Println("Failed at restoring ", r) return output, false } }