def test_subgroup_family(self): cf = Permutation.read_cycle_form a = cf([[2,3],[4,6],[5,8],[9,11]], 13) b = cf([[1,2,4,7,9,3,5,6,8,10,11,12,13]], 13) fam = SubgroupFamily(PermGroup([a,b])) stack = PartitionStack([0]*13,[-1]*13) stack.extend(0,[1,3,5,7,9,11]) stack.extend(1,[1]) stack.extend(1,[3]) func1 = fam.extension_functions(stack)[1] extend = lambda x:fam.extension_functions(x)[0](x) extend(stack) after = Partition([[4,6,8,10,13],[5,7,9,11],[1],[3],[2,12]]) self.assertEqual(stack[-1],after) stack.extend(1,[5]) extend(stack) extend(stack) extend(stack) extend(stack) extend(stack) extend(stack) self.assertFalse(stack.discrete()) extend(stack) self.assertTrue(stack.discrete()) right_before = PartitionStack.single_partition_stack([[2,4,6,8,10,12,13],[1,5,7,11],[9],[3]]) func1(right_before) right_after = Partition([[2,6,8,12,13],[1,5,7,11],[9],[3],[4,10]]) self.assertEqual(right_before[-1],right_after)
def test_discrete(self): base = PartitionStack([0,0,0,0,0],[-1,-1,-1,-1,-1]) base.extend(0,[3,4,5]) base.extend(1,[4]) base.extend(0,[2]) self.assertFalse(base.discrete()) base.extend(1,[5]) self.assertTrue(base.discrete())
def __init__(self, group_property, refinement_family, size): self.size = size self.refinement_family = refinement_family self.group_property = group_property self.left = PartitionStack([0]*size,[-1]*size) self.right = PartitionStack([0]*size,[-1]*size) self._cur_height = 0 self._cur_position = None self._special_lookup = None self._left_split_index = None self.printing = False self.multi_backtrack = True self.double_coset_check = False
def test_can_extend(self): base = PartitionStack([0,0,0,0,0],[-1,-1,-1,-1,-1]) base.extend(0, [1,3,5]) self.assertEqual(base[-1], Partition([[2,4],[1,3,5]])) self.assertFalse(base.can_extend(0,[2,4])) self.assertFalse(base.can_extend(0,[2,4,1,3])) self.assertFalse(base.can_extend(0,[])) self.assertFalse(base.can_extend(0,[5])) self.assertTrue(base.can_extend(0,[1,2,3])) self.assertTrue(base.can_extend(0,[4]))
def test_pop(self): stack = PartitionStack([6,2,4,7,0,0,3,5,1,4],[1,1,0,2,-1,-1,0,4,0,0]) self.assertEqual(stack[-1], Partition([[5,6],[9],[2],[7],[3,10],[8],[1],[4]])) stack.pop() self.assertEqual(stack[-1], Partition([[5,6],[9],[2,4],[7],[3,10],[8],[1]])) self.assertEqual(stack, PartitionStack([6,2,4,2,0,0,3,5,1,4],[1,1,0,1,-1,-1,0,4,0,0])) stack.pop() self.assertEqual(stack[-1], Partition([[5,6],[1,9],[2,4],[7],[3,10],[8]])) stack.pop() self.assertEqual(stack[-1], Partition([[5,6],[1,9],[2,4],[7],[3,8,10]])) stack.pop() self.assertEqual(stack[-1], Partition([[3,5,6,8,10],[1,9],[2,4],[7]]))
def __init__(self, group_property, refinement_family, size): self.size = size self.refinement_family = refinement_family self.group_property = group_property self.left = PartitionStack([0]*size,[-1]*size) self.right = PartitionStack([0]*size,[-1]*size) self._cur_height = 0 self._cur_position = None self._special_lookup = None self._left_split_index = None
def extension_functions(self,left,*args): #Only works for r_base construction or equivelent procedure. base = left.fix() self._group.change_base(base) orbits = self._group.orbits(len(base), key = self._key_single) orbits.sort(key = self._key_orbit) #should be left.height if we can gaureentee complete part. stack. for i in range(left.degree): for orbit in orbits: if left.can_extend(i, orbit): base_stack = PartitionStack.deep_copy(left) func = lambda stack: self._full_extension(stack,base,i,orbit) func._info = [self.__class__,base, i,orbit] return func, func return None
def extension_functions(self,left): base = left.fix() self._group.change_base(base) orbits = self._group.orbits(len(base), key = self._key_single) orbits.sort(key = self._key_orbit) left_copy = PartitionStack.deep_copy(left) for i in range(left.base_size): for orbit in orbits: before = len(stack) self.full_extention(left, right,base_stack,i,orbit) if len(stack) > before: func = lambda l=None, r=None : self.full_extention(l,r,base_stack,i,orbit) func._info = [self.__class__,base, i,orbit ] return left, right, func return left, right, None
def test_non_trivial_modifiers(self): cf = lambda x: Permutation.read_cycle_form(x,13) a = cf([[2,3],[4,6],[5,8],[9,11]]) b = cf([[1,2,4,7,9,3,5,6,8,10,11,12,13]]) G = PermGroup([a,b]) part_stab = Partition([[3,2],[6,4],[5,1,7,8,9,10,11,12,13]]) fam = RefinementUnion([PartitionStabaliserFamily(part_stab), SubgroupFamily(G)]) prop = CosetPropertyUnion([PartitionStabaliserProperty(part_stab), SubgroupProperty(G)]) log = LeonLoggerUnion([LeonCounter()]) cons = ModifierUnion([PartitionStackConstraint()]) union = ModifierUnion([fam,prop,log,cons]) left = PartitionStack([0]*13,[-1]*13) right = PartitionStack.deep_copy(left) funcs = union.extension_functions(left) self.assertFalse(funcs is None) funcs[0](left) funcs[1](right) index = union.exclude_backtrack_index(left,right, None, 1) self.assertEqual(index,None) index = union.exclude_backtrack_index(left,right, None, 2) self.assertEqual(index,1) self.assertTrue(union.property_check(a)) self.assertFalse(union.property_check(b)) pos_leaf = union.leaf_fail_backtrack_index(a,b,None) neg_leaf = union.leaf_pass_backtrack_index(a,b,None) self.assertEqual(pos_leaf, None) self.assertEqual(neg_leaf, None)
class PartitionBacktrackWorkhorse(): def __init__(self, group_property, refinement_family, size): self.size = size self.refinement_family = refinement_family self.group_property = group_property self.left = PartitionStack([0]*size,[-1]*size) self.right = PartitionStack([0]*size,[-1]*size) self._cur_height = 0 self._cur_position = None self._special_lookup = None self._left_split_index = None self.printing = False self.multi_backtrack = True self.double_coset_check = False def find_partition_backtrack_subgroup(self): #Assume the refinement families are symmetric. generators = [] r_base = self._r_base() count = 0 leaf_count = 0 if self.printing: print("-----------------------------------") print("Traversing tree:") while self._cur_height > -1: count += 1 if self.printing: a = self._cur_position b = self.right[self._cur_height] c = self.left[self._cur_height] print("\n{}: {}\n{} -> {}".format(self._cur_height,a,c,b)) #Alternatice 1: premature rule out. if self.alt1(): if self.printing: print("Alternative 1: partition violation") #print("alt_1") self.backtrack(self._cur_height - 1) #Alternative 2: discrete check. elif self.right.discrete(): leaf_count += 1 #print("alt_2") perm = Permutation.read_partitions(self.left[self._cur_height], self.right[self._cur_height]) if self.double_coset_check and self.printing: G=PermGroup(generators) Dco = G*perm*G perm_key = ordering.ordering_to_perm_key(self.left.fix()) if Dco.mid_minimal(key = perm_key): print("Actually minimal in own double coset: {}".format(Dco._list_elements(Perm_key))) added = False if not perm.trivial() and self.group_property.check(perm): generators.append(perm) added = True #self.backtrack() if self.printing: print("Alternative 2 found permutation: {} ({})".format(perm, added)) if self.multi_backtrack and added: self.backtrack(self._cur_position.min_changed_index()) else: self.backtrack() #Alternative 3: not a special level elif r_base[self._cur_height] is not None: #print("alt_3") r_base[self._cur_height](None, self.right) if self.printing: print("Alternative 3 applying function: {}".format(r_base[self._cur_height]._info)) self._cur_height += 1 #Alternative 4: is a special level else: if self.printing: print("Alternative 4 special level") #print("alt_4") self.extend_right() if self.printing: print("\nFinished tree traversal.") print("Visited {} vertices ({} of which were terminal).".format(count, leaf_count)) return generators def _r_base(self): if self.printing: print("Constructing r-base:") r_base = [] special_cell_sizes = [] special_lookup = dict() height = 0 while height < self.size -1: if self.printing: print("\n{}".format(self.left[-1])) _,_,func = self.refinement_family.extend(self.left, None) r_base.append(func) if func is not None: if self.printing: print(func._info) special_cell_sizes.append(0) else: if self.printing: print("Special level:") cell, cell_size, point = self._split_left_cell() special_cell_sizes.append(cell_size) special_lookup[height] = (cell, point) height += 1 if self.printing: print("\n{}".format(self.left[-1])) self._cur_position = PositionTracker(special_cell_sizes) self._special_lookup = special_lookup if self.printing: print("\nFinished R-base construction.") _temp_levels = sorted([level for level in special_lookup]) print("Special levels: {}".format(_temp_levels)) special_cells = [special_lookup[level][0] for level in _temp_levels] special_vals = [special_lookup[level][1] for level in _temp_levels] print("Basis of group: {}".format(special_vals)) print("Ordering from rbase: {}".format(self.left.fix())) return r_base def _split_left_cell(self): #Overwrite this with a clever function that queries the refinements for #clues as to which cell and element to split. top = self.left[-1] if self._left_split_index is None or len(top[self._left_split_index]) < 2: _, self._left_split_index = min([(len(cell),index) for index, cell in enumerate(top) if len(cell) > 1]) cell = top[self._left_split_index] point = cell[0] cell_size = len(cell) #Turn checks off. self.left.extend(self._left_split_index, [point]) return self._left_split_index, cell_size, point def find_partition_backtrack_coset(self): #Assume the refinement families are LR-symmetric. r_base = self._r_base() count = 0 while self._cur_height > -1: count += 1 #a = self._cur_position #b = self.right[self._cur_height] #c = self.left[self._cur_height] #if self.size == 13: #print("\n{}:{}\n{} -> {}".format(self._cur_height,a,c,b)) #Alternatice 1: premature rule out. if self.alt1(): #print("alt_1") self.backtrack(self._cur_height - 1) #Alternative 2: discrete check. elif self.right.discrete(): #print("alt_2") perm = Permutation.read_partitions(self.left[self._cur_height], self.right[self._cur_height]) if self.group_property.check(perm): return perm #self.backtrack() self.backtrack(self._cur_position.min_changed_index()) #Alternative 3: not a special level elif r_base[self._cur_height] is not None: #print("alt_3") r_base[self._cur_height](None, self.right) self._cur_height += 1 #Alternative 4: is a special level else: #print("alt_4") self.extend_right() print(count) return generators def backtrack(self, new_height = None): if new_height is None: new_height = self._cur_height self._cur_height = self._cur_position.increment(new_height) if self._cur_height > -1: while len(self.right) > self._cur_height + 1: self.right.pop() self.extend_right() def alt1(self): #Size violation if len(self.right) != self._cur_height + 1: return True elif len(self.right[-1][-1]) != len(self.left[self._cur_height][-1]): return True #Contained group violation. #Minimal in double coset violation. #Property dependant violation. return False def extend_right(self): index = self._cur_height split_index, _ = self._special_lookup[index] cell = self.right[index][split_index] try: split_val = cell[self._cur_position[index]] except IndexError: for i in range(index + 1): a = self._cur_position b = self.right[i] c = self.left[i] print("\n{} -> {}\n{}".format(c,b,a)) #this is where it screws raise IndexError("DIFFERENT") self.right.extend(split_index, [split_val]) self._cur_height += 1
def test_fix(self): stack = PartitionStack([6,2,4,7,0,0,3,5,1,4],[1,1,0,2,-1,-1,0,4,0,0]) self.assertEqual(stack.fix(), [7,8,1,9,4,2])
def initialise_partition_stacks(self): size = self.degree self.left = PartitionStack([0]*size,[-1]*size) self.right = PartitionStack([0]*size,[-1]*size)
def test_extend(self): base = PartitionStack([0,0,0,0,0],[-1,-1,-1,-1,-1]) self.assertEqual(base[-1], Partition([[1,2,3,4,5]])) base.extend(0,[3,4,5]) self.assertEqual(base[-1], Partition([[1,2],[3,4,5]])) base.extend(1,[4]) self.assertEqual(base[-1], Partition([[1,2],[3,5],[4]])) base.extend(0,[2]) self.assertEqual(base[-1], Partition([[1],[3,5],[4],[2]])) extention = base.extend(1,[5]) self.assertEqual(extention[-1], Partition([[1],[3],[4],[2],[5]])) self.assertEqual(base[-1], Partition([[1],[3],[4],[2],[5]])) self.assertEqual(extention[0], Partition([[1,2,3,4,5]])) self.assertEqual(extention[1], Partition([[1,2],[3,4,5]])) self.assertEqual(extention[2], Partition([[1,2],[3,5],[4]])) self.assertEqual(extention[3], Partition([[1],[3,5],[4],[2]])) self.assertEqual(extention[4], Partition([[1],[3],[4],[2],[5]])) base = PartitionStack([0,0,0,0,0],[-1,-1,-1,-1,-1]) base.extend(0, []) self.assertEqual(len(base[1]), 1) base = PartitionStack([0,0,0,0,0],[-1,-1,-1,-1,-1]) base.extend(0, [1,2,3,4,5]) self.assertEqual(len(base[1]), 1)
def test_single_partition_stack(self): stack = PartitionStack([0,0,1,1,2,2],[-1,-1,0,0,0,0]) part = Partition([[1,2],[3,4],[5,6]]) self.assertEqual(stack[-1], part) self.assertEqual(stack, PartitionStack.single_partition_stack(part))
class LeonSearch(): def __init__(self, leon_modifiers, degree): self.degree = degree self.tree_modifiers = leon_modifiers self.left = None self.right = None self._left_split_index = None self._cur_height = None self._r_base = None self._special_level_sizes = None #Tree position tracker to store traversal information. self._cur_position = None #Dictionary with heights as keys and split index and point pairs as values. self._special_lookup = None def initialise_partition_stacks(self): size = self.degree self.left = PartitionStack([0]*size,[-1]*size) self.right = PartitionStack([0]*size,[-1]*size) def initialise_r_base(self): r_base = [] special_cell_sizes = [] special_lookup = dict() height = 0 while height < self.degree -1: funcs = self.tree_modifiers.extension_functions(self.left) if funcs is not None: left_func, right_func = funcs left_func(self.left) r_base.append(right_func) special_cell_sizes.append(0) else: r_base.append(None) cell, cell_size, point = self._split_left_cell() special_cell_sizes.append(cell_size) special_lookup[height] = (cell, point) height += 1 self._r_base = r_base self._special_level_sizes = special_cell_sizes self._special_lookup = special_lookup def initialise_search_tree(self): self._cur_height = 0 self._cur_position = PositionTracker(self._special_level_sizes) def subgroup(self): #Needs to be done in this order. self.initialise_partition_stacks() self.initialise_r_base() self.initialise_search_tree() gens = [] while self._cur_height > -1: #alt_1 rule out. backtrack_index = self.tree_modifiers.exclude_backtrack_index(self.left, self.right, self._cur_position, self._cur_height) if backtrack_index is not None: self.backtrack(backtrack_index) #alt_2 discrete check. elif self.right.discrete(): perm = Permutation.read_partitions(self.left[self._cur_height], self.right[self._cur_height]) if not perm.trivial() and self.tree_modifiers.property_check(perm): gens.append(perm) backtrack_index = self.tree_modifiers.leaf_pass_backtrack_index(self.left,self.right,self._cur_position) else: backtrack_index = self.tree_modifiers.leaf_fail_backtrack_index(self.left,self.right,self._cur_position) self.backtrack(backtrack_index) #alt_3 function to extend. elif self._r_base[self._cur_height] is not None: func = self._r_base[self._cur_height] func(self.right) self._cur_height += 1 #alt_4 special level. else: self._extend_right() return gens def backtrack(self, new_height = None): if new_height is None: new_height = self._cur_height self._cur_height = self._cur_position.increment(new_height) if self._cur_height > -1: while len(self.right) > self._cur_height + 1: self.right.pop() self._extend_right() def _extend_right(self): index = self._cur_height split_index, _ = self._special_lookup[index] cell = self.right[index][split_index] try: split_val = cell[self._cur_position[index]] except IndexError: for i in range(index + 1): a = self._cur_position b = self.right[i] c = self.left[i] print("\n{} -> {}\n{}".format(c,b,a)) #this is where it screws raise IndexError("DIFFERENT") self.right.extend(split_index, [split_val]) self._cur_height += 1 def _split_left_cell(self): #Overwrite this with a clever function that queries the refinements for #clues as to which cell and element to split. top = self.left[-1] if self._left_split_index is None or len(top[self._left_split_index]) < 2: _, self._left_split_index = min([(len(cell),index) for index, cell in enumerate(top) if len(cell) > 1]) cell = top[self._left_split_index] point = cell[0] cell_size = len(cell) #Turn checks off. self.left.extend(self._left_split_index, [point]) return self._left_split_index, cell_size, point
def test_pop(self): stack = PartitionStack([6,2,4,7,0,0,3,5,1,4],[1,1,0,2,-1,-1,0,4,0,0]) self.assertEqual(stack[-1], Partition([[5,6],[9],[2],[7],[3,10],[8],[1],[4]])) stack.pop() self.assertEqual(stack[-1], Partition([[5,6],[9],[2,4],[7],[3,10],[8],[1]])) self.assertEqual(stack, PartitionStack([6,2,4,2,0,0,3,5,1,4],[1,1,0,1,-1,-1,0,4,0,0])) stack.pop() self.assertEqual(stack[-1], Partition([[5,6],[1,9],[2,4],[7],[3,10],[8]])) stack.pop() self.assertEqual(stack[-1], Partition([[5,6],[1,9],[2,4],[7],[3,8,10]])) stack.pop() self.assertEqual(stack[-1], Partition([[3,5,6,8,10],[1,9],[2,4],[7]])) stack.pop() self.assertEqual(stack[-1], Partition([[3,5,6,7,8,10],[1,9],[2,4]])) stack.pop() self.assertEqual(stack[-1], Partition([[3,5,6,8,10,7],[1,9,2,4]])) stack.pop() self.assertEqual(stack[-1], Partition([[1,2,3,4,5,6,7,8,9,10]])) raised_error = False try: stack.pop() except IndexError: raised_error = True self.assertTrue(raised_error) self.assertEqual(stack[-1], Partition([[1,2,3,4,5,6,7,8,9,10]])) self.assertEqual(len(stack), 1)
def test_valid_intersection(self): base = PartitionStack([0,0,0,0,0],[-1,-1,-1,-1,-1]) base.extend(0, [1,3,5]) self.assertEqual(base[-1], Partition([[2,4],[1,3,5]])) self.assertTrue(base._valid_intersection(1,[2,4]) is None) self.assertEqual(base._valid_intersection(1,[1,2,4,5]), [1,5])