def test_analytic_continuation_X1(self): gammax = ComplexLine(1, 0) y0 = [-1, 1] gamma = RiemannSurfacePathPuiseux(self.X1, gammax, y0) y = gamma.get_y(0) self.assertAlmostEqual(y[0], -1) self.assertAlmostEqual(y[1], 1) y = gamma.get_y(0.5) self.assertAlmostEqual(y[0], -sqrt(complex(0.5))) self.assertAlmostEqual(y[1], sqrt(complex(0.5))) y = gamma.get_y(0.75) self.assertAlmostEqual(y[0], -sqrt(complex(0.25))) self.assertAlmostEqual(y[1], sqrt(complex(0.25))) y = gamma.get_y(1) self.assertAlmostEqual(y[0], 0) self.assertAlmostEqual(y[1], 0) gammax = ComplexArc(2, 2, 0, pi) y0 = [-2, 2] gamma = RiemannSurfacePathPuiseux(self.X1, gammax, y0) y = gamma.get_y(0) self.assertAlmostEqual(y[0], -2) self.assertAlmostEqual(y[1], 2) y = gamma.get_y(1) self.assertAlmostEqual(y[0], 0) self.assertAlmostEqual(y[1], 0)
def test_rays(self): # test that analytic continuation to places at infinity work gammax = ComplexRay(-9) y0 = [-3.j, 3.j] gamma = RiemannSurfacePathPuiseux(self.X1, gammax, y0) y = gamma.get_y(0) self.assertAlmostEqual(y[0], -3.j) self.assertAlmostEqual(y[1], 3.j) # note: the infinity behavior may change in the future y = gamma.get_y(1) self.assertTrue(numpy.isnan(y[0])) self.assertTrue(numpy.isnan(y[1]))
def test_simple_line_puiseux_discriminant(self): gammax = ComplexLine(2, 0) #dxds = 2 y0 = [-sqrt(2.0), sqrt(2.0)] gamma = RiemannSurfacePathPuiseux(self.X1, gammax, y0) nu = lambda x, y: y nu_gamma = gamma.parameterize(nu) val = nu_gamma(0.0) self.assertAlmostEqual(val, -sqrt(2.0)) val = nu_gamma(0.5) self.assertAlmostEqual(val, -sqrt(1.0)) val = nu_gamma(1.0) self.assertAlmostEqual(val, 0)
def _path_to_infinite_place(self, P): r"""Returns a path to a place at an infintiy of the surface. A place at infinity is one where the `x`-projection of the place is the point `x = \infty` of the complex Riemann sphere. An infinite place is a type of discirminant place. Parameters ---------- P : Place The target infinite place. Returns ------- gamma : RiemannSurfacePath A path from the base place to the place at infinity. """ # determine a place Q from where we can reach the target place P. first, # pick an appropriate x-point over which a Q is chosen x0 = self.base_point if numpy.real(x0) < 0: xa = 5 * x0 # move away from the origin else: xa = -5 # arbitrary choice away from the origin # next, determine an appropriate y-part from where we can reach the # place at infinity p = P.puiseux_series center, coefficient, ramification_index = p.xdata ta = CC(xa / coefficient).nth_root(abs(ramification_index)) ta = ta if ramification_index > 0 else 1 / ta p.extend_to_t(ta) ya = complex(p.eval_y(ta)) # construct the place Q and compute the path going from P0 to Q Q = self.riemann_surface(xa, ya) gamma_P0_to_Q = self.path_to_place(Q) # construct the path going from Q to P xend = complex(gamma_P0_to_Q.get_x(1.0)) yend = array(gamma_P0_to_Q.get_y(1.0), dtype=complex) gamma_x = ComplexRay(xend) gamma_Q_to_P = RiemannSurfacePathPuiseux(self.riemann_surface, gamma_x, yend) gamma = gamma_P0_to_Q + gamma_Q_to_P return gamma
def _path_to_discriminant_place(self, P): r"""Returns a path to a discriminant place on the surface. A "discriminant" place :math:`P` is a place on the Riemann surface where Puiseux series are required to determine the x- and y-projections of the place. Parameters ---------- P : Place A place on the Riemann surface whose x-projection is a discriminant point of the curve. Returns ------- gamma : RiemannSurfacePath A path from the base place to the discriminant place `P`. """ # compute a valid y-value at x=a=b-R, where b is the # discriminant point center of the series and R is the radius of # bounding circle around b, such that analytically continuing # from that (x,y) to x=b will reach the designated place p = P.puiseux_series center, coefficient, ramification_index = p.xdata R = self.complex_path_factory.radius(center) a = center - R t = (-R / coefficient)**(1.0 / ramification_index) # p.coerce_to_numerical() p.extend_to_t(t) y = p.eval_y(t) # construct a path going from the base place to this regular # place to the left of the target discriminant point P1 = self.riemann_surface(a, y) gamma1 = self._path_to_regular_place(P1) # construct the RiemannSurfacePath going from this regular place to the # discriminant point. xend = complex(gamma1.get_x(1.0)) yend = array(gamma1.get_y(1.0), dtype=complex) gamma_x = ComplexLine(complex(a), complex(center)) segment = RiemannSurfacePathPuiseux(self.riemann_surface, gamma_x, yend) gamma = gamma1 + segment return gamma
def test_analytic_continuation_X2(self): S = QQ['t'] t = S.gen() a, b, c = (t**3 - 1).roots(ring=QQbar, multiplicities=False) gammax = ComplexLine(1, 0) y0 = [a, b, c] gamma = RiemannSurfacePathPuiseux(self.X2, gammax, y0) y = gamma.get_y(0) self.assertAlmostEqual(y[0], a) self.assertAlmostEqual(y[1], b) self.assertAlmostEqual(y[2], c) scale = (0.5)**(1 / 3.) y = gamma.get_y(0.5) self.assertAlmostEqual(y[0], scale * a) self.assertAlmostEqual(y[1], scale * b) self.assertAlmostEqual(y[2], scale * c) y = gamma.get_y(1) self.assertAlmostEqual(y[0], 0) self.assertAlmostEqual(y[1], 0) self.assertAlmostEqual(y[2], 0)
def test_construction(self): gammax = ComplexLine(1, 0) y0 = [-1, 1] gamma = RiemannSurfacePathPuiseux(self.X1, gammax, y0)
def RiemannSurfacePath_from_complex_path(self, complex_path, x0=None, y0=None): r"""Constructs a :class:`RiemannSurfacePath` object from x-path data. Parameters ---------- complex_path : ComplexPath A complex path. x0 : complex (default `self.base_point`) The starting x-point of the path. y0 : complex list (default `self.base_sheets`) The starting ordering of the y-sheets. Returns ------- RiemannSurfacePath A path on the Riemann surface with the prescribed x-path. """ if x0 is None: x0 = self.base_point if y0 is None: y0 = self.base_sheets # coerce and assert that x0,y0 lies on the path and curve x0 = complex(x0) y0 = array(y0, dtype=complex) if abs(x0 - complex_path(0)) > 1e-7: raise ValueError('The point %s is not at the start of the ' 'ComplexPath %s' % (x0, complex_path)) f = self.riemann_surface.f curve_error = [abs(complex(f(x0, y0k))) for y0k in y0] if max(curve_error) > 1e-7: raise ValueError('The fibre %s above %s does not lie on the ' 'curve %s' % (y0.tolist(), x0, f)) # build a list of path segments from each tuple of xdata. build a line # segment or arc depending on xdata input x0_segment = x0 y0_segment = y0 segments = [] for segment_x in complex_path.segments: # for each segment determine if we're far enough away to use Smale # alpha theory paths or if we have to use Puiseux. we add a # relazation factor to the radius to account for monodromy paths xend = segment_x(1.0) b = self.complex_path_factory.closest_discriminant_point(xend) R = self.complex_path_factory.radius(b) if abs(xend - b) > 0.9 * R: segment = RiemannSurfacePathSmale(self.riemann_surface, segment_x, y0_segment) else: segment = RiemannSurfacePathPuiseux(self.riemann_surface, segment_x, y0_segment) # determine the starting place of the next segment x0_segment = segment.get_x(1.0) y0_segment = segment.get_y(1.0) segments.append(segment) # build the entire path from the path segments gamma = RiemannSurfacePath(self.riemann_surface, complex_path, y0, segments) return gamma