def update_equalitiy_classes(self, element, another_element): m1, m2 = self.to_class[to_string(element)], self.to_class[to_string(another_element)], if m1 != m2: m = min(m1, m2) M = max(m1, m2) for element in self.equality_classes[M]: self.to_class[to_string(element)] = m self.equality_classes[m] += self.equality_classes[M] self.equality_classes[M] = [] self.equality_classes[m] = sorted(self.equality_classes[m], key = lambda x: (len(x), str(x)))
def _simplify(self, element): if to_string(element) in self.to_class.keys(): self.visited.append(element) return self._getClass(element)[0] if len(element) <= self.nb_terms_in_simplest_form: self.visited.append(element) self.create_new_class(element) return element transformed_element = copy.copy(element) for i in range(len(transformed_element), -1, -1): for j in range (0, len(transformed_element) - i): sequence = transformed_element[j : j + i + 1] if to_string(sequence) not in self.to_class.keys(): continue for replacement in self._getClass(sequence): transformed_element = element[:j] + replacement + element[j + i + 1:] if transformed_element in self.visited: print transformed_element, " visited" continue else: self.visited.append(transformed_element) simplified_transformed_element = self._simplify(transformed_element) if simplified_transformed_element in self.visited: continue else: self.visited.append(transformed_element) if simplified_transformed_element in self.to_class.keys(): self.join_class(element, simplified_transformed_element) return self._getClass(simplified_transformed_element)[0] if transformed_element != return element def simplify(self, element): """ Find the simplest form of an element. For example, for the group of 1 generator x1 and 1 relation x1^5=0, x1^4 will return x1^-1 This works with element in string form. """ #if len(self.all_elements) > 0: # return self._elementToElementString(self._strongSimplify(self.elementStringToElement(element))) self.visited = [] return self._elementToElementString(self._simplify(self.elementStringToElement(element))) def _multiply(self, element1, element2): """ Multiply to 2 elements of the group in list form. E.g, for the group of 1 generator x1 and 1 relation x1^5=0, multiplying [1,1,1] and [1,1] returns []. This works with elements in list form. """ self.visited = [] element1 = self._simplify(element1) self.visited = [] element2 = self._simplify(element2) self.visited = [] return self._simplify(element1 + element2) def multiply(self, element1, element2): """ Multiply to 2 elements of the group in list form. E.g, for the group of 1 generator x1 and 1 relation x1^5=0, multiplying x1^3 and x1^2 returns e. This works with elements in string form. """ element1 = self.simplify(element1) self.visited = [] element2 = self.simplify(element2) self.visited = [] return self.simplify(self.elementStringToElement(element1) + self.elementStringToElement(element2)) def _power(self, element, exponent): """ Return element to a power in group. For example, for the group of 1 generator x1 and 1 relation x1^5=0, _power(1,4) returns [3]. This works with elements in list form. """ if self.order > 0: exponent = exponent % self.order if exponent == 0: return [] return self._simplify( self._multiply (self._power(element, exponent - 1), element ) ) def power(self, element, exponent): """ Return element to a power in group. For example, for the group of 1 generator x1 and 1 relation x1^5=0, power('x1',4) returns x1^-1. This works with elements in list form. """ return self._elementToElementString(self._exponential(self.elementStringToElement(element), exponent)) def _isNeutral(self, element): """ Check if an element (in list form) is neutral. """ self.visited = [] return self._simplify(element) == [] def isNeutral(self, element): """ Check if an element (in string form) is neutral. """ return self._isNeutral(self.elementStringToElement(element)) def _inverse(self, element): """ Return the inverse of an element (in list form). """ return [self._index_of_inverse(i) for i in reversed(element)] def _areEqual(self, element1, element2): """ Return the inverse of an element (in string form). """ return self._isNeutral(element1 + self._inverse(element2)) def _order(self, element): """ Return the order of an element (in list form) in the group """ if self.order > 0: for i in range(1, self.order + 1): if self._isNeutral(self._power(element, i)): return i return "Unknown" def order(self, element): """ Return the order of an element (in string form) in the group """ return self._order(self.elementStringToElement(element)) def _shortenSequence(self, sequence): """ Sequence is the list form of an element """ ordered_generalized_relations = sorted(self.generalized_relations, key = lambda x: len(x)) transformed_sequence = sequence for relation in ordered_generalized_relations: #relation is sth like [0,1,2,2] if len(sequence) > len(relation)/2 and position_of_list_in_list(sequence, relation) >= 0: transformed_sequence = self._shortenSequenceInSpecifiedRelation(sequence, relation) break return transformed_sequence def _shortenSequenceWithAlgo2(self, sequence): """ Sequence is the list form of an element """ ordered_generalized_relations = sorted(self.generalized_relations, key = lambda x: len(x)) transformed_sequence = sequence for relation in ordered_generalized_relations: #relation is sth like [0,1,2,2] if 2 * len(sequence) >= len(relation) and position_of_list_in_list(sequence, relation) >= 0: transformed_sequence = self._shortenSequenceInSpecifiedRelation(sequence, relation) break return transformed_sequence def _shortenSequenceInSpecifiedRelation(self, sequence, relation): """ Sequence is the list form of an element """ position = position_of_list_in_list(sequence, relation) remaining = remaining_of_list_after_removing(sequence, relation, position) transformed_sequence = self._inverse(remaining[0]) + self._inverse(remaining[1]) return transformed_sequence def elementStringToElement(self, element_string): for variable in self.retrieveVariablesFromElement(element_string): element_string = element_string.replace(variable, self.generators[variable]) element_list = element_string.split(MULTIPLICATION_SIGN) element = [] for term in element_list: #term is sth like x0^3, 1 if term.find(NEUTRAL_ELEMENT) >= 0: pass elif EXPONENT_SIGN in term: base, exponent = int(term.split(EXPONENT_SIGN)[0].replace(VARIABLE, "")), int(term.split(EXPONENT_SIGN)[1]) if exponent > 0: element += [base] * exponent else: element += [self._index_of_inverse(base)] * (-exponent) else: element.append(int(term.replace(VARIABLE, ""))) return element def _relationToExpression(self, relation): expression = "" for pair in relation: expression += "%s%s%d%s" % (self.reversed_generators[VARIABLE + str(pair[0])], EXPONENT_SIGN, pair[1], MULTIPLICATION_SIGN) expression += END_OF_EQUATION expression = expression.replace(MULTIPLICATION_SIGN + END_OF_EQUATION, END_OF_EQUATION) return expression def _elementToElementString(self, element): element_string = "" pairs = [] if len(element) > 0: if element[0] <= self.nb_generators: pairs.append([element[0], 1]) else: pairs.append([self._index_of_inverse(element[0]), -1]) for i in range(1, len(element)): if (element[i] == element[i-1] or element[i]==self._index_of_inverse(element[i-1])) and element[i] <= self.nb_generators: pairs[-1][1] += 1 elif (element[i] == element[i-1] or element[i]==self._index_of_inverse(element[i-1])) and element[i] > self.nb_generators: pairs[-1][1] -= 1 elif element[i] <= self.nb_generators: pairs.append([element[i], 1]) elif element[i] > self.nb_generators: pairs.append([self._index_of_inverse(element[i]), -1]) for pair in pairs: if pair[1] != 0: element_string += "%s%s%d%s" % ( self.reversed_generators[VARIABLE + str(pair[0])], EXPONENT_SIGN, pair[1], MULTIPLICATION_SIGN) if len(element_string) > 0 and element_string[-1] == MULTIPLICATION_SIGN: element_string = element_string[:-1] element_string = element_string.replace("%s1" % EXPONENT_SIGN, "") if element_string == "": element_string = NEUTRAL_ELEMENT return element_string def retrieveVariablesFromExpression(self, expression): regex = re.compile('[^a-zA-Z]') simplified_expression = regex.sub('', expression) variables = set(list(simplified_expression)) index = len(self.generators) for character in sorted(list(variables)): if character != "e": if character not in self.generators.keys(): self.generators[character] = "x" + str(index) expression = expression.replace(character, "$" + str(index)) index += 1 else: expression = expression.replace(character, self.generators[character].replace("x", "$")) expression = expression.replace("$", "x") return expression def retrieveVariablesFromElement(self, element): regex = re.compile('[^a-zA-Z]') simplified_element = regex.sub('', element) variables = set(list(simplified_element)) return sorted(list(variables)) def _listElementsOfLength(self, length): all_classes = [] if length==0: all_classes.append([]) return all_classes for element in self._listElementsOfLength(length-1): for index in range (1, 2*self.nb_generators + 1): new_element = element + [index] if self.commutative: new_element = self._sortElementInCommutativeGroup(new_element) if len(new_element) == length and new_element not in all_classes: all_classes.append(self._sortElementInCommutativeGroup(new_element)) elif len(self._simplify(new_element)) == length and new_element not in all_classes: all_classes.append(new_element) return all_classes def _listAllElements(self): all_classes = [] for length in range(self.nb_terms_in_simplest_form + 1): all_classes += self._listElementsOfLength(length) distinct_classes = [] for some_class in all_classes: is_new = True for another_class in distinct_classes: try: if self._areEqual(some_class, another_class): is_new = False break except: print "Unable to multiply" pass if is_new: distinct_classes.append(some_class) return distinct_classes def _checkCommutative(self): if self.commutative: return True for generator in range(1, self.nb_generators + 1): for another_generator in range(1, self.nb_generators + 1): if not self._areEqual([generator, another_generator], [another_generator, generator]): return False return True def _sortElementInCommutativeGroup(self, element): if not self.commutative: return element preres = [0] * (self.nb_generators + 1) for term in element: #term is sth like 1 or 2 or 3 if term <= self.nb_generators: preres[term] += 1 else: preres[self._index_of_inverse(term)] -= 1 res = [] for generator in range(1, self.nb_generators + 1): if preres[generator] > 0: res += [generator] * preres[generator] elif preres[generator] < 0: res += [self._index_of_inverse(generator)] * (-preres[generator]) return res def _createMultiplicationTable(self): multi_tab = [] for idx1 in range(self.order): row = [] for idx2 in range(self.order): if self.commutative: row += [self._simplify(self._sortElementInCommutativeGroup(self._multiply(self.all_elements[idx1], self.all_elements[idx2])))] else: row += [self._simplify(self._multiply(self.all_elements[idx1], self.all_elements[idx2]))] multi_tab += [row] return multi_tab def createMultiplicationTable(self): multi_tab = self._createMultiplicationTable() multi_string_tab = [[self._elementToElementString(element) for element in row] for row in multi_tab] return multi_string_tab
def _getClass(self, element): return self.equality_classes[self.to_class[to_string(element)]]
def join_class(self, element, another_element): self.to_class[to_string(element)] = self.to_class[to_string(another_element)] self.equality_classes[self.to_class[to_string(another_element)]].append(element) self.equality_classes[self.to_class[to_string(another_element)]] = sorted(self.equality_classes[self.to_class[to_string(another_element)]], key = lambda x: (len(x), str(x)))
def create_new_class(self, element): self.to_class[to_string(element)] = len(self.equality_classes) self.equality_classes.append([element])
def _generalizeRelations(self): for i in range(1, self.nb_generators + 1): # Firstly, find all relations of the form x * inverse(x) = e self.generalized_relations.append([i, self._index_of_inverse(i)]) self.generalized_relations.append([self._index_of_inverse(i), i]) #Now, generate all relations that is a permutation of relations in self.relations, for example [1,2,3] will be [1,2,3], [2,3,1] and [3,1,2] #We call them relation of type 1 relations_type_1 = [] for relation in self.relations: # Here relation is sth like ([(1,1), (2,-1), (3,1)]) reformulated_relation = [] for term in relation: #term is sth like (1,1) base, exponent = term[0], term[1] if exponent > 0: reformulated_relation += [base] * exponent else: reformulated_relation += [self._index_of_inverse(base)] * (-exponent) #After this step, our example become [1,5,3] n = len(reformulated_relation) for permutator in range(n): permutated_relation = [reformulated_relation[(permutator + i) % n] for i in range(n)] if permutated_relation not in relations_type_1: relations_type_1.append(permutated_relation) #After this step, relations_type_1 contains [1,5,3], [5,3,1], [3,1,5] #We also express the inverse form of the relation. E.g, [1,5,3] becomes [6,2,4] #Do the same, we get more elements of relations_type_1: [6,2,4], [2,4,6], [4,6,2] reformulated_relation = [] for term in reversed(relation): #term is sth like (0,1) base, exponent = term[0], term[1] if exponent > 0: reformulated_relation += [self._index_of_inverse(base)] * exponent else: reformulated_relation += [base] * (-exponent) n = len(reformulated_relation) for permutator in range(n): permutated_relation = [reformulated_relation[(permutator + i) % n] for i in range(n)] if permutated_relation not in relations_type_1: relations_type_1.append(permutated_relation) #We want to define relations of type 2, which is formed by mean of: find another form of some sequence form a relation, then replace it in another relation #For example, if x1*x1=e, we have [1,1]=[] or equivalently, [1]=[4], hence in [1,5,3], we can substitute [1] in this relation and it becomes [4,5,3] relations_type_2 = relations_type_1 relations_type_1 = sorted(relations_type_1, key = lambda x: len(x)) for relation in relations_type_1: #print "-----Current relation-------", relation relation_sublists = sublists(relation) for sequence in relation_sublists: #print "---Current sequence---", sequence for other_relation in relations_type_1: #print "Working with", other_relation positions = all_discrete_positions_of_list_in_list(sequence, other_relation) #print "Positions", positions if len(positions) > 1: for subset in all_subsets(positions): if len(subset) > 0: #print "Subset", subset for position in subset: new_relation = other_relation[:position] + self._shortenSequenceInSpecifiedRelation(sequence, relation) + other_relation[position + len(sequence):] #print "Use %s in position %d in %s, %s becomes %s"% (sequence, position, relation, other_relation, new_relation) #print "New relation", new_relation if new_relation not in relations_type_2 and new_relation not in self.generalized_relations: relations_type_2.append(new_relation) self.generalized_relations += relations_type_2 relations_type_3 = [] for relation in self.relations: if len(relation) == 1 and relation[0][1] == 2: x = relation[0][0] y = self._index_of_inverse(x) for another_relation in self.generalized_relations: new_another_relation = copy.copy(another_relation) for idx in range(len(another_relation)): if another_relation[idx] == x: new_another_relation[idx] = y if another_relation[idx] == y: new_another_relation[idx] = x if new_another_relation not in self.generalized_relations and new_another_relation not in relations_type_3: relations_type_3.append(new_another_relation) self.generalized_relations += relations_type_3 self.to_class[to_string([])] = 0 self.equality_classes.append([]) self.equality_classes[0].append([]) for reformulated_relation in self.generalized_relations: for i in range(len(reformulated_relation), -1, -1): for j in range (0, len(reformulated_relation) - i): sequence = reformulated_relation[j : j + i + 1] remaining = remaining_of_list_after_removing(sequence, reformulated_relation, j) transformed_sequence = self._inverse(remaining[0]) + self._inverse(remaining[1]) if to_string(sequence) not in self.to_class.keys(): if to_string(transformed_sequence) not in self.to_class.keys(): self.create_new_class(transformed_sequence) if sequence != transformed_sequence: self.join_class(sequence, transformed_sequence) else: if to_string(transformed_sequence) not in self.to_class.keys(): self.join_class(transformed_sequence, sequence) else: self.update_equalitiy_classes(transformed_sequence, sequence) print self.equality_classes