def __init__(self, polygon, involutions, z, w): self._poly = polygon self._cur_poly = [] self._involutions = involutions self._w = w self._z = z self._line = None self._decomposition = [] self._mul_dec = Matrix() self._previous_edge = None self._involution_dict = None self._crossing = True
def gen_reprs(self): # Generating set of orbit representatives # of action $\Gamma_1(N)$ on $\Gamma_0(N)$ self.reprs = [] for a in range(1, self.N // 2 + 1): if gcd(a, self.N) == 1: self.reprs.append(Matrix(a, 0, 0, inv_element(a, self.N)))
def decompose_matrix(self, matrix_str): try: matrix = Matrix.from_str(matrix_str) except Exception: raise ApiError('Matrix should be in following format: a, b, c, d') if not self.is_in_subgroup(matrix): raise ApiError('Matrix does not belong to subgroup') z = Field(0.5+1.5j) w = matrix.moe(z) decomposer = Decomposer(polygon=self._domain, involutions=self._involutions, z=z, w=w) try: self._decomposition = decomposer.decompose() except Exception: raise ApiError('unexpected algorithm error occurred.')
def is_in_subgroup(self, matrix: Matrix): if matrix.det() != 1: return False a, b, c, d = matrix.a, matrix.b, matrix.c, matrix.d n = self._n if self._subgroup_name is ClassicalSubgroups.GammaBotZero: return c % n == 0 elif self._subgroup_name is ClassicalSubgroups.GammaTopZero: return b % n == 0 elif self._subgroup_name is ClassicalSubgroups.GammaBotOne: return a % n == 1 and d % n == 1 and c % n == 0 elif self._subgroup_name is ClassicalSubgroups.GammaTopOne: return a % n == 1 and d % n == 1 and b % n == 0 elif self._subgroup_name is ClassicalSubgroups.Gamma: return a % n == 1 and d % n == 1 and b % n == 0 and c % n == 0 return False
def decompose(self): if self._w == self._z: return [Matrix(1, 0, 0, 1)] self._line = Geodesic(self._w, self._z) self.prepare_involutions() self._poly = cyclic_sorted(self._poly) self._cur_poly = self._poly[::] previous_edges = get_cross_edges(self._cur_poly, self._line) if len(previous_edges) != 1: raise Exception('Number of crossed edges is not equal to 1') self._previous_edge = previous_edges[0] while self._crossing: self._iteration() return self._decomposition
def not_cached_reduced(self, mat): a, b = mat.a, mat.b a, b = self.pair_reduced(a, b)[0:2] d, c = list(map(lambda x: x % self.N, get_xy(a, b, self.N))) return Matrix(a, b, -c, d) % self.N
def gen_reprs(self): for a, b, N in self.pair_reprs: self.reprs.append(self.reduced(Matrix(a, b, 0, 0)))
def gen_reprs(self): self.reprs = [] for a, b, N in self.pair_reprs: self.reprs.append(self.reduced(Matrix(0, 0, a, b)))
def reduced(self, mat: Matrix): a, b = mat._a, mat._d if a > self.N // 2: a = (-a) % self.N b = (-b) % self.N return Matrix(a, 0, 0, b)
def gen_reprs(self): for a in range(self.N): self.reprs.append(Matrix(1, a, 0, 1))
class Decomposer(object): def __init__(self, polygon, involutions, z, w): self._poly = polygon self._cur_poly = [] self._involutions = involutions self._w = w self._z = z self._line = None self._decomposition = [] self._mul_dec = Matrix() self._previous_edge = None self._involution_dict = None self._crossing = True def decompose(self): if self._w == self._z: return [Matrix(1, 0, 0, 1)] self._line = Geodesic(self._w, self._z) self.prepare_involutions() self._poly = cyclic_sorted(self._poly) self._cur_poly = self._poly[::] previous_edges = get_cross_edges(self._cur_poly, self._line) if len(previous_edges) != 1: raise Exception('Number of crossed edges is not equal to 1') self._previous_edge = previous_edges[0] while self._crossing: self._iteration() return self._decomposition def _iteration(self): _g_i = self._get_involution(self._previous_edge).inv() g_i = self._mul_dec * _g_i * self._mul_dec.inv() self._decomposition.append(_g_i) self._mul_dec = g_i * self._mul_dec for i in range(len(self._cur_poly)): self._cur_poly[i] = g_i.moe(self._cur_poly[i]) # geo_drawer.draw(self._cur_poly, color='grey') crossed = get_cross_edges(self._cur_poly, self._line) crossed = list( filter(lambda e: not undirected_eq(e, self._previous_edge), crossed)) if not crossed: self._crossing = False elif len(crossed) == 1: self._previous_edge = crossed[0] elif len(crossed) == 2: # Decision: which way should we go # Trying first first_poly = self._cur_poly[::] for i in range(len(first_poly)): first_poly[i] = g_i.moe(first_poly[i]) _crossed = get_cross_edges(self._cur_poly, self._line) _crossed = list( filter(lambda e: not undirected_eq(e, crossed[0]), _crossed)) if _crossed: self._previous_edge = crossed[0] else: self._previous_edge = crossed[1] else: raise Exception('there are more than 2 crossing edges') def prepare_involutions(self): self._involution_dict = dict() for a, b, g in self._involutions: self._involution_dict[a] = g self._involution_dict[b] = g.inv() def _get_involution(self, edge): edge_ = self._poly[self._cur_poly.index(edge)] return self._involution_dict[edge_]
def get_decomposition(self): return Matrix.beautify(self._decomposition)
def get_generators_str(self): return Matrix.beautify(self._generators)