github.com/yacovm/fabric@v2.0.0-alpha.0.20191128145320-c5d4087dc723+incompatible/common/graph/perm.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package graph
     8  
     9  import (
    10  	"math/rand"
    11  	"time"
    12  )
    13  
    14  func init() {
    15  	rand.Seed(time.Now().UnixNano())
    16  }
    17  
    18  // treePermutations represents possible permutations
    19  // of a tree
    20  type treePermutations struct {
    21  	combinationUpperBound  int                             // The upper bound of combinations of direct descendants.
    22  	originalRoot           *TreeVertex                     // The root vertex of all sub-trees
    23  	permutations           []*TreeVertex                   // The accumulated permutations
    24  	descendantPermutations map[*TreeVertex][][]*TreeVertex // Defines the combinations of sub-trees based on the threshold of the current vertex
    25  }
    26  
    27  // newTreePermutation creates a new treePermutations object with a given root vertex
    28  func newTreePermutation(root *TreeVertex, combinationUpperBound int) *treePermutations {
    29  	return &treePermutations{
    30  		combinationUpperBound:  combinationUpperBound,
    31  		descendantPermutations: make(map[*TreeVertex][][]*TreeVertex),
    32  		originalRoot:           root,
    33  		permutations:           []*TreeVertex{root},
    34  	}
    35  }
    36  
    37  // permute returns Trees that their vertices and edges all exist in the original tree who's vertex
    38  // is the 'originalRoot' field of the treePermutations
    39  func (tp *treePermutations) permute() []*Tree {
    40  	tp.computeDescendantPermutations()
    41  
    42  	it := newBFSIterator(tp.originalRoot)
    43  	for {
    44  		v := it.Next()
    45  		if v == nil {
    46  			break
    47  		}
    48  
    49  		if len(v.Descendants) == 0 {
    50  			continue
    51  		}
    52  
    53  		// Iterate over all permutations where v exists
    54  		// and separate them to 2 sets: a indiceSet where it exists and a indiceSet where it doesn't
    55  		var permutationsWhereVexists []*TreeVertex
    56  		var permutationsWhereVdoesntExist []*TreeVertex
    57  		for _, perm := range tp.permutations {
    58  			if perm.Exists(v.Id) {
    59  				permutationsWhereVexists = append(permutationsWhereVexists, perm)
    60  			} else {
    61  				permutationsWhereVdoesntExist = append(permutationsWhereVdoesntExist, perm)
    62  			}
    63  		}
    64  
    65  		// Remove the permutations where v exists from the permutations
    66  		tp.permutations = permutationsWhereVdoesntExist
    67  
    68  		// Next, we replace each occurrence of a permutation where v exists,
    69  		// with multiple occurrences of its descendants permutations
    70  		for _, perm := range permutationsWhereVexists {
    71  			// For each permutation of v's descendants, clone the graph
    72  			// and create a new graph with v replaced with the permutated graph
    73  			// of v connected to the descendant permutation
    74  			for _, permutation := range tp.descendantPermutations[v] {
    75  				subGraph := &TreeVertex{
    76  					Id:          v.Id,
    77  					Data:        v.Data,
    78  					Descendants: permutation,
    79  				}
    80  				newTree := perm.Clone()
    81  				newTree.replace(v.Id, subGraph)
    82  				// Add the new option to the permutations
    83  				tp.permutations = append(tp.permutations, newTree)
    84  			}
    85  		}
    86  	}
    87  
    88  	res := make([]*Tree, len(tp.permutations))
    89  	for i, perm := range tp.permutations {
    90  		res[i] = perm.ToTree()
    91  	}
    92  	return res
    93  }
    94  
    95  // computeDescendantPermutations computes all possible combinations of sub-trees
    96  // for all vertices, based on the thresholds.
    97  func (tp *treePermutations) computeDescendantPermutations() {
    98  	it := newBFSIterator(tp.originalRoot)
    99  	for {
   100  		v := it.Next()
   101  		if v == nil {
   102  			return
   103  		}
   104  
   105  		// Skip leaves
   106  		if len(v.Descendants) == 0 {
   107  			continue
   108  		}
   109  
   110  		// Ensure we don't have too much combinations of descendants
   111  		for CombinationsExceed(len(v.Descendants), v.Threshold, tp.combinationUpperBound) {
   112  			// Randomly pick a descendant, and remove it
   113  			victim := rand.Intn(len(v.Descendants))
   114  			v.Descendants = append(v.Descendants[:victim], v.Descendants[victim+1:]...)
   115  		}
   116  
   117  		// Iterate over all options of selecting the threshold out of the descendants
   118  		for _, el := range chooseKoutOfN(len(v.Descendants), v.Threshold) {
   119  			// And for each such option, append it to the current TreeVertex
   120  			tp.descendantPermutations[v] = append(tp.descendantPermutations[v], v.selectDescendants(el.indices))
   121  		}
   122  	}
   123  }
   124  
   125  // selectDescendants returns a subset of descendants according to given indices
   126  func (v *TreeVertex) selectDescendants(indices []int) []*TreeVertex {
   127  	r := make([]*TreeVertex, len(indices))
   128  	i := 0
   129  	for _, index := range indices {
   130  		r[i] = v.Descendants[index]
   131  		i++
   132  	}
   133  	return r
   134  }