def monodromy_group(self): """Returns the monodromy group of the algebraic curve defining the Riemann surface. Returns ------- list : list A list containing the base point, base sheets, branch points, and corresponding monodromy group elements. """ x0 = self.base_point y0 = self.base_sheets # compute the monodromy element for each discriminant point. if # it's the identity permutation, don't add to monodromy group branch_points = [] permutations = [] for bi in self.discriminant_points: gamma = self.monodromy_path(bi) # compute the end fibre and the corresponding permutation yend = array(gamma.get_y(1.0), dtype=complex) phi = matching_permutation(y0, yend) # add the point to the monodromy group if the permutation is # not the identity. if not phi.is_identity(): branch_points.append(bi) permutations.append(phi) # compute the monodromy element of the point at infinity gamma_x = self.complex_path_factory.monodromy_path_infinity() gamma = self.RiemannSurfacePath_from_complex_path(gamma_x, x0, y0) yend = array(gamma.get_y(1.0), dtype=complex) phi_oo = matching_permutation(y0, yend) # sanity check: the product of the finite branch point permutations # should be equal to the inverse of the permutation at infinity phi_prod_all = phi_oo for sigma in permutations: phi_prod_all = sigma * phi_prod_all if not phi_prod_all.is_identity(): raise ValueError('Contradictory permutation at infinity.') # add infinity if it's a branch point if not phi_oo.is_identity(): branch_points.append(infinity) permutations.append(phi_oo) return branch_points, permutations
def monodromy_group(self): """Returns the monodromy group of the algebraic curve defining the Riemann surface. Returns ------- list : list A list containing the base point, base sheets, branch points, and corresponding monodromy group elements. """ x0 = self.base_point y0 = self.base_sheets # compute the monodromy element for each discriminant point. if # it's the identity permutation, don't add to monodromy group branch_points = [] permutations = [] for bi in self.discriminant_points: gamma = self.monodromy_path(bi) # compute the end fibre and the corresponding permutation yend = array(gamma.get_y(1.0), dtype=complex) phi = matching_permutation(y0, yend) # add the point to the monodromy group if the permutation is # not the identity. if not phi.is_identity(): branch_points.append(bi) permutations.append(phi) # compute the monodromy element of the point at infinity gamma_x = self.complex_path_factory.monodromy_path_infinity() gamma = self.RiemannSurfacePath_from_complex_path(gamma_x, x0, y0) yend = array(gamma.get_y(1.0), dtype=complex) phi_oo = matching_permutation(y0, yend) # sanity check: the product of the finite branch point permutations # should be equal to the inverse of the permutation at infinity phi_prod = reduce(lambda phi1,phi2: phi2*phi1, permutations) phi_prod_all = phi_prod * phi_oo if not phi_prod_all.is_identity(): raise ValueError('Contradictory permutation at infinity.') # add infinity if it's a branch point if not phi_oo.is_identity(): branch_points.append(infinity) permutations.append(phi_oo) return branch_points, permutations
def _find_permutation_path(self, fineness=10): def _norm(x, scale=1, center=self.cp.cut_point): x = np.complex(x) r = np.complex(x - center) return np.complex(center + scale*r/np.abs(r)) # radius of a circle encompassing all branch points R = 1.5*max(np.abs(x.val - self.cp.cut_point) for x in self.cp.branch_points if x.is_finite) # angle of this branch point self.angle = np.angle(self.val - self.cp.cut_point) base_point = _norm(self.cp.monodromy_point, scale=R) if self.is_finite: # half the distance to closest problematic point r = np.abs(np.complex(self.val - self.cp.cut_point)) for x in self.cp.surface.discriminant_points: if x.n() != self.val: r = min(r, 0.5*np.abs(np.complex(self.val - np.complex(x)))) outter = _norm(self.val, scale=R) circle_start = _norm(self.val, scale=np.abs(self.val - self.cp.cut_point) + r) ang = np.angle(outter - self.cp.cut_point) - np.angle(base_point - self.cp.cut_point) if self.imag < imag_part(self.cp.cut_point): ang = 2*np.pi + ang points_to_outter = [self.cp.monodromy_point] + [np.complex(self.cp.cut_point) + np.complex(base_point-self.cp.cut_point)*np.exp(1j*ang*float(i)/fineness) for i in range(fineness+1)] around = [self.val + (circle_start-self.val)*np.exp(2j*np.pi*i/fineness) for i in range(fineness+1)] points = points_to_outter + around + points_to_outter[::-1] else: points = [self.cp.monodromy_point] + [np.complex(self.cp.cut_point) + np.complex(base_point-self.cp.cut_point)*np.exp(-2j*np.pi*i/fineness) for i in range(fineness+1)] + [self.cp.monodromy_point] # self.cp.ax.scatter([np.real(x) for x in points], [np.imag(x) for x in points]) # return self.permutation_path = CyclePainterPath(points, 0, self.cp, color_sheets=False) self.permutation = matching_permutation(self.permutation_path.get_y(0), self.permutation_path.get_y(1))
def ordered_puiseux_series(riemann_surface, complex_path, y0, target_point): r"""Returns an ordered list of Puiseux series such that each Puiseux series matches with the corresponding y-fibre element above the starting point of `complex_path`. In order to analytically continue from the regular places at the beginning of the path :math:`x=a` to the discriminant places at the end of the path :math:`x=b`we need to compute all of the `PuiseuxXSeries` at :math:`x=b`. There are two steps to this calculation: * compute enough terms of the Puiseux series centered at :math:`x=b` in order to accurately capture the y-roots at :math:`x=a`. * permute the series accordingly to match up with the y-roots at :math:`x=a`. Parameters ---------- riemann_surface : RiemannSurface The riemann surface on which all of this lives. complex_path : ComplexPath The path or path segment starting at a regular point and ending at a discriminant point. y0 : list of complex The starting fibre lying above the starting point of `complex_path`. The first component of the list indicates the starting sheet. target_point : complex The point to analytically continue to. Usually a discriminant point. Methods ------- .. autosummary:: analytically_continue Returns ------- list, Place : a list of Puiseux series and a Place A list of ordered Puiseux series corresponding to each branch above :math:`x=a` as well as the place that the first y-fibre element analytically continues to. """ # obtain all puiseux series above the target place f = riemann_surface.f x0 = CC(complex_path(0)) # XXX - need to coerce input to CC y0 = numpy.array(y0, dtype=complex) P = puiseux(f, target_point) # extend the Puiseux series to enough terms to accurately captue the # y-fibre above x=a (the starting point of the complex path) for Pi in P: Pi.extend_to_x(x0) # compute the corresponding x-series representations of the Puiseux series alpha = 0 if target_point == infinity else target_point px = [Pi.xseries() for Pi in P] p = [pxi for sublist in px for pxi in sublist] ramification_indices = [Pi.ramification_index for Pi in P] # reorder them according to the ordering of the y-fibre above x=x0 p_evals_above_x0 = [pj(x0 - alpha) for pj in p] p_evals_above_x0 = numpy.array(p_evals_above_x0, dtype=complex) sigma = matching_permutation(p_evals_above_x0, y0) p = sigma.action(p) # also return the place that the first y-fibre element ends up analytically # continuing to px_idx = sigma[0] # index of target x-series in unsorted list place_idx = -1 # index of place corresponding to this x-series while px_idx >= 0: place_idx += 1 px_idx -= abs(ramification_indices[place_idx]) target_place = DiscriminantPlace(riemann_surface, P[place_idx]) return p, target_place
def ordered_puiseux_series(riemann_surface, complex_path, y0, target_point): r"""Returns an ordered list of Puiseux series such that each Puiseux series matches with the corresponding y-fibre element above the starting point of `complex_path`. In order to analytically continue from the regular places at the beginning of the path :math:`x=a` to the discriminant places at the end of the path :math:`x=b`we need to compute all of the `PuiseuxXSeries` at :math:`x=b`. There are two steps to this calculation: * compute enough terms of the Puiseux series centered at :math:`x=b` in order to accurately capture the y-roots at :math:`x=a`. * permute the series accordingly to match up with the y-roots at :math:`x=a`. Parameters ---------- riemann_surface : RiemannSurface The riemann surface on which all of this lives. complex_path : ComplexPath The path or path segment starting at a regular point and ending at a discriminant point. y0 : list of complex The starting fibre lying above the starting point of `complex_path`. The first component of the list indicates the starting sheet. target_point : complex The point to analytically continue to. Usually a discriminant point. Methods ------- .. autosummary:: analytically_continue Returns ------- list, Place : a list of Puiseux series and a Place A list of ordered Puiseux series corresponding to each branch above :math:`x=a` as well as the place that the first y-fibre element analytically continues to. """ # obtain all puiseux series above the target place f = riemann_surface.f x0 = CC(complex_path(0)) # XXX - need to coerce input to CC y0 = numpy.array(y0, dtype=complex) P = puiseux(f, target_point) # extend the Puiseux series to enough terms to accurately captue the # y-fibre above x=a (the starting point of the complex path) for Pi in P: Pi.extend_to_x(x0) # compute the corresponding x-series representations of the Puiseux series alpha = 0 if target_point == infinity else target_point px = [Pi.xseries() for Pi in P] p = [pxi for sublist in px for pxi in sublist] ramification_indices = [Pi.ramification_index for Pi in P] # reorder them according to the ordering of the y-fibre above x=x0 p_evals_above_x0 = [pj(x0-alpha) for pj in p] p_evals_above_x0 = numpy.array(p_evals_above_x0, dtype=complex) sigma = matching_permutation(p_evals_above_x0, y0) p = sigma.action(p) # also return the place that the first y-fibre element ends up analytically # continuing to px_idx = sigma[0] # index of target x-series in unsorted list place_idx = -1 # index of place corresponding to this x-series while px_idx >= 0: place_idx += 1 px_idx -= abs(ramification_indices[place_idx]) target_place = DiscriminantPlace(riemann_surface,P[place_idx]) return p, target_place