135 lines
4.1 KiB
Go
135 lines
4.1 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package graph
|
|
|
|
import (
|
|
"math/rand"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
rand.Seed(time.Now().UnixNano())
|
|
}
|
|
|
|
// treePermutations represents possible permutations
|
|
// of a tree
|
|
type treePermutations struct {
|
|
combinationUpperBound int // The upper bound of combinations of direct descendants.
|
|
originalRoot *TreeVertex // The root vertex of all sub-trees
|
|
permutations []*TreeVertex // The accumulated permutations
|
|
descendantPermutations map[*TreeVertex][][]*TreeVertex // Defines the combinations of sub-trees based on the threshold of the current vertex
|
|
}
|
|
|
|
// newTreePermutation creates a new treePermutations object with a given root vertex
|
|
func newTreePermutation(root *TreeVertex, combinationUpperBound int) *treePermutations {
|
|
return &treePermutations{
|
|
combinationUpperBound: combinationUpperBound,
|
|
descendantPermutations: make(map[*TreeVertex][][]*TreeVertex),
|
|
originalRoot: root,
|
|
permutations: []*TreeVertex{root},
|
|
}
|
|
}
|
|
|
|
// permute returns Trees that their vertices and edges all exist in the original tree who's vertex
|
|
// is the 'originalRoot' field of the treePermutations
|
|
func (tp *treePermutations) permute() []*Tree {
|
|
tp.computeDescendantPermutations()
|
|
|
|
it := newBFSIterator(tp.originalRoot)
|
|
for {
|
|
v := it.Next()
|
|
if v == nil {
|
|
break
|
|
}
|
|
|
|
if len(v.Descendants) == 0 {
|
|
continue
|
|
}
|
|
|
|
// Iterate over all permutations where v exists
|
|
// and separate them to 2 sets: a indiceSet where it exists and a indiceSet where it doesn't
|
|
var permutationsWhereVexists []*TreeVertex
|
|
var permutationsWhereVdoesntExist []*TreeVertex
|
|
for _, perm := range tp.permutations {
|
|
if perm.Exists(v.Id) {
|
|
permutationsWhereVexists = append(permutationsWhereVexists, perm)
|
|
} else {
|
|
permutationsWhereVdoesntExist = append(permutationsWhereVdoesntExist, perm)
|
|
}
|
|
}
|
|
|
|
// Remove the permutations where v exists from the permutations
|
|
tp.permutations = permutationsWhereVdoesntExist
|
|
|
|
// Next, we replace each occurrence of a permutation where v exists,
|
|
// with multiple occurrences of its descendants permutations
|
|
for _, perm := range permutationsWhereVexists {
|
|
// For each permutation of v's descendants, clone the graph
|
|
// and create a new graph with v replaced with the permutated graph
|
|
// of v connected to the descendant permutation
|
|
for _, permutation := range tp.descendantPermutations[v] {
|
|
subGraph := &TreeVertex{
|
|
Id: v.Id,
|
|
Data: v.Data,
|
|
Descendants: permutation,
|
|
}
|
|
newTree := perm.Clone()
|
|
newTree.replace(v.Id, subGraph)
|
|
// Add the new option to the permutations
|
|
tp.permutations = append(tp.permutations, newTree)
|
|
}
|
|
}
|
|
}
|
|
|
|
res := make([]*Tree, len(tp.permutations))
|
|
for i, perm := range tp.permutations {
|
|
res[i] = perm.ToTree()
|
|
}
|
|
return res
|
|
}
|
|
|
|
// computeDescendantPermutations computes all possible combinations of sub-trees
|
|
// for all vertices, based on the thresholds.
|
|
func (tp *treePermutations) computeDescendantPermutations() {
|
|
it := newBFSIterator(tp.originalRoot)
|
|
for {
|
|
v := it.Next()
|
|
if v == nil {
|
|
return
|
|
}
|
|
|
|
// Skip leaves
|
|
if len(v.Descendants) == 0 {
|
|
continue
|
|
}
|
|
|
|
// Ensure we don't have too much combinations of descendants
|
|
for CombinationsExceed(len(v.Descendants), v.Threshold, tp.combinationUpperBound) {
|
|
// Randomly pick a descendant, and remove it
|
|
victim := rand.Intn(len(v.Descendants))
|
|
v.Descendants = append(v.Descendants[:victim], v.Descendants[victim+1:]...)
|
|
}
|
|
|
|
// Iterate over all options of selecting the threshold out of the descendants
|
|
for _, el := range chooseKoutOfN(len(v.Descendants), v.Threshold) {
|
|
// And for each such option, append it to the current TreeVertex
|
|
tp.descendantPermutations[v] = append(tp.descendantPermutations[v], v.selectDescendants(el.indices))
|
|
}
|
|
}
|
|
}
|
|
|
|
// selectDescendants returns a subset of descendants according to given indices
|
|
func (v *TreeVertex) selectDescendants(indices []int) []*TreeVertex {
|
|
r := make([]*TreeVertex, len(indices))
|
|
i := 0
|
|
for _, index := range indices {
|
|
r[i] = v.Descendants[index]
|
|
i++
|
|
}
|
|
return r
|
|
}
|