def check_singularity(self): r""" Raise an error if this step goes through a singular point or seems to do so at our working precision. TESTS:: sage: from ore_algebra import * sage: from ore_algebra.analytic.path import Point, Step sage: Dops, x, Dx = DifferentialOperators(); i = QuadraticField(-1, 'i').gen() sage: dop = (x^2 + 1)*Dx sage: Step(Point(0, dop), Point(0, dop)).check_singularity() sage: Step(Point(0, dop), Point(1, dop)).check_singularity() sage: Step(Point(1, dop), Point(1, dop)).check_singularity() sage: Step(Point(1, dop), Point(i, dop)).check_singularity() sage: Step(Point(i, dop), Point(0, dop)).check_singularity() sage: Step(Point(i, dop), Point(i, dop)).check_singularity() sage: Step(Point(2*i+1, dop), Point(-11/10, dop)).check_singularity() sage: Step(Point(2*i, dop), Point(0, dop)).check_singularity() Traceback (most recent call last): ... ValueError: Step 2*i --> 0 passes through or too close to singular point 1*I (to compute the connection to a singular point, make it a vertex of the path) sage: Step(Point(2*i+1, dop), Point(-1, dop)).check_singularity() Traceback (most recent call last): ... ValueError: Step 2*i + 1 --> -1 passes through or too close to singular point 1*I (to compute the connection to a singular point, make it a vertex of the path) """ dop = self.start.dop # TODO: solve over CBF directly? sing = [ IC(s) for s in dop_singularities(dop, CIF) if s != CIF(self.start.value) and s != CIF(self.end.value) ] for s in sing: ds = s - self.start.iv() t = self.delta() / ds if (ds.contains_zero() or t.imag().contains_zero() and not safe_lt(t.real(), IR.one())): raise ValueError( "Step {} passes through or too close to singular point {} " "(to compute the connection to a singular point, make it " "a vertex of the path)".format(self, sing_as_alg(dop, s)))
def bypass_singularities(self): r""" TESTS:: sage: from ore_algebra import * sage: Dops, x, Dx = DifferentialOperators() sage: ((x-1)*Dx - 1).numerical_solution([1], [0,2], assume_analytic=True) [-1.0000000000000...] + [+/- ...]*I sage: dop = ((x - 1)*Dx - 1)*((x - 2)*Dx - 2) sage: dop.numerical_solution([1, 0], [0, 3], assume_analytic=True) [-3.5000000000000...] + [+/- ...]*I sage: QQi.<i> = QuadraticField(-1) sage: dop = ((x - i - 1)*Dx - 1)*((x - 2*i - 2)*Dx - 2) sage: dop.numerical_solution([1, 0], [0, 3*i + 3], assume_analytic=True) [-3.5000000000000...] + [+/- ...]*I """ new = [] for step in self: new.append(step.start) dir = step.direction() sings = step.singularities() sings.sort(key=lambda s: (s - step.start.iv()).abs()) for s in sings: ds = Point(s, self.dop, singular=True).dist_to_sing() d0 = abs(s - step.start.iv()) d1 = abs(s - step.end.iv()) zs = [] if not safe_lt(d0, ds): zs.append(-1) zs.append(IC.gen(0)) if not safe_lt(d1, ds): zs.append(1) rad = (ds / 2).min(d0, d1) new.extend([_rationalize(CIF(s + rad * z * dir)) for z in zs]) new.append(self.vert[-1]) new = Path(new, self.dop) return new
def followstrand(f, x0, x1, y0a, prec=53): r""" Return a piecewise linear aproximation of the homotopy continuation of the root ``y0a`` from ``x0`` to ``x1``. INPUT: - ``f`` -- a polynomial in two variables - ``x0`` -- a complex value, where the homotopy starts - ``x1`` -- a complex value, where the homotopy ends - ``y0a`` -- an approximate solution of the polynomial `F(y) = f(x_0, y)` - ``prec`` -- the precision to use OUTPUT: A list of values `(t, y_{tr}, y_{ti})` such that: - ``t`` is a real number between zero and one - `f(t \cdot x_1 + (1-t) \cdot x_0, y_{tr} + I \cdot y_{ti})` is zero (or a good enough aproximation) - the piecewise linear path determined by the points has a tubular neighborhood where the actual homotopy continuation path lies, and no other root intersects it. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import followstrand # optional - sirocco sage: R.<x,y> = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1, 0) sage: x1 = CC(1, 0.5) sage: followstrand(f, x0, x1, -1.0) # optional - sirocco # abs tol 1e-15 [(0.0, -1.0, 0.0), (0.7500000000000001, -1.015090921153253, -0.24752813818386948), (1.0, -1.026166099551513, -0.32768940253604323)] """ CIF = ComplexIntervalField(prec) CC = ComplexField(prec) G = f.change_ring(QQbar).change_ring(CIF) (x, y) = G.variables() g = G.subs({x: (1-x)*CIF(x0) + x*CIF(x1)}) coefs = [] deg = g.total_degree() for d in range(deg + 1): for i in range(d + 1): c = CIF(g.coefficient({x: d-i, y: i})) cr = c.real() ci = c.imag() coefs += list(cr.endpoints()) coefs += list(ci.endpoints()) yr = CC(y0a).real() yi = CC(y0a).imag() from sage.libs.sirocco import contpath, contpath_mp try: if prec == 53: points = contpath(deg, coefs, yr, yi) else: points = contpath_mp(deg, coefs, yr, yi, prec) return points except Exception: return followstrand(f, x0, x1, y0a, 2*prec)
def __getitem__(self, arg): """ Extend this ring by one or several elements to create a polynomial ring, a power series ring, or an algebraic extension. This is a convenience method intended primarily for interactive use. .. SEEALSO:: :func:`~sage.rings.polynomial.polynomial_ring_constructor.PolynomialRing`, :func:`~sage.rings.power_series_ring.PowerSeriesRing`, :meth:`~sage.rings.ring.Ring.extension`, :meth:`sage.rings.integer_ring.IntegerRing_class.__getitem__`, :meth:`sage.rings.matrix_space.MatrixSpace.__getitem__`, :meth:`sage.structure.parent.Parent.__getitem__` EXAMPLES: We create several polynomial rings:: sage: ZZ['x'] Univariate Polynomial Ring in x over Integer Ring sage: QQ['x'] Univariate Polynomial Ring in x over Rational Field sage: GF(17)['abc'] Univariate Polynomial Ring in abc over Finite Field of size 17 sage: GF(17)['a,b,c'] Multivariate Polynomial Ring in a, b, c over Finite Field of size 17 sage: GF(17)['a']['b'] Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17 We can create skew polynomial rings:: sage: k.<t> = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: k['x',Frob] Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 We can also create power series rings by using double brackets:: sage: QQ[['t']] Power Series Ring in t over Rational Field sage: ZZ[['W']] Power Series Ring in W over Integer Ring sage: ZZ[['x,y,z']] Multivariate Power Series Ring in x, y, z over Integer Ring sage: ZZ[['x','T']] Multivariate Power Series Ring in x, T over Integer Ring Use :func:`~sage.rings.fraction_field.Frac` or :meth:`~sage.rings.ring.CommutativeRing.fraction_field` to obtain the fields of rational functions and Laurent series:: sage: Frac(QQ['t']) Fraction Field of Univariate Polynomial Ring in t over Rational Field sage: Frac(QQ[['t']]) Laurent Series Ring in t over Rational Field sage: QQ[['t']].fraction_field() Laurent Series Ring in t over Rational Field Note that the same syntax can be used to create number fields:: sage: QQ[I] Number Field in I with defining polynomial x^2 + 1 sage: QQ[I].coerce_embedding() Generic morphism: From: Number Field in I with defining polynomial x^2 + 1 To: Complex Lazy Field Defn: I -> 1*I :: sage: QQ[sqrt(2)] Number Field in sqrt2 with defining polynomial x^2 - 2 sage: QQ[sqrt(2)].coerce_embedding() Generic morphism: From: Number Field in sqrt2 with defining polynomial x^2 - 2 To: Real Lazy Field Defn: sqrt2 -> 1.414213562373095? :: sage: QQ[sqrt(2),sqrt(3)] Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field and orders in number fields:: sage: ZZ[I] Order in Number Field in I with defining polynomial x^2 + 1 sage: ZZ[sqrt(5)] Order in Number Field in sqrt5 with defining polynomial x^2 - 5 sage: ZZ[sqrt(2)+sqrt(3)] Order in Number Field in a with defining polynomial x^4 - 10*x^2 + 1 Embeddings are found for simple extensions (when that makes sense):: sage: QQi.<i> = QuadraticField(-1, 'i') sage: QQ[i].coerce_embedding() Generic morphism: From: Number Field in i with defining polynomial x^2 + 1 To: Complex Lazy Field Defn: i -> 1*I TESTS: A few corner cases:: sage: QQ[()] Multivariate Polynomial Ring in no variables over Rational Field sage: QQ[[]] Traceback (most recent call last): ... TypeError: power series rings must have at least one variable These kind of expressions do not work:: sage: QQ['a,b','c'] Traceback (most recent call last): ... ValueError: variable name 'a,b' is not alphanumeric sage: QQ[['a,b','c']] Traceback (most recent call last): ... ValueError: variable name 'a,b' is not alphanumeric sage: QQ[[['x']]] Traceback (most recent call last): ... TypeError: expected R[...] or R[[...]], not R[[[...]]] Extension towers are built as follows and use distinct generator names:: sage: K = QQ[2^(1/3), 2^(1/2), 3^(1/3)] sage: K Number Field in a with defining polynomial x^3 - 2 over its base field sage: K.base_field() Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field sage: K.base_field().base_field() Number Field in b with defining polynomial x^3 - 3 Embeddings:: sage: QQ[I](I.pyobject()) I sage: a = 10^100; expr = (2*a + sqrt(2))/(2*a^2-1) sage: QQ[expr].coerce_embedding() is None False sage: QQ[sqrt(5)].gen() > 0 True sage: expr = sqrt(2) + I*(cos(pi/4, hold=True) - sqrt(2)/2) sage: QQ[expr].coerce_embedding() Generic morphism: From: Number Field in a with defining polynomial x^2 - 2 To: Real Lazy Field Defn: a -> 1.414213562373095? """ def normalize_arg(arg): if isinstance(arg, (tuple, list)): # Allowing arbitrary iterables would create confusion, but we # may want to support a few more. return tuple(arg) elif isinstance(arg, str): return tuple(arg.split(',')) else: return (arg,) # 1. If arg is a list, try to return a power series ring. if isinstance(arg, list): if arg == []: raise TypeError("power series rings must have at least one variable") elif len(arg) == 1: # R[["a,b"]], R[[(a,b)]]... if isinstance(arg[0], list): raise TypeError("expected R[...] or R[[...]], not R[[[...]]]") elts = normalize_arg(arg[0]) else: elts = normalize_arg(arg) from sage.rings.power_series_ring import PowerSeriesRing return PowerSeriesRing(self, elts) if isinstance(arg, tuple): from sage.categories.morphism import Morphism if len(arg) == 2 and isinstance(arg[1], Morphism): from sage.rings.polynomial.skew_polynomial_ring_constructor import SkewPolynomialRing return SkewPolynomialRing(self, arg[1], names=arg[0]) # 2. Otherwise, if all specified elements are algebraic, try to # return an algebraic extension elts = normalize_arg(arg) try: minpolys = [a.minpoly() for a in elts] except (AttributeError, NotImplementedError, ValueError, TypeError): minpolys = None if minpolys: # how to pass in names? names = tuple(_gen_names(elts)) if len(elts) == 1: from sage.rings.all import CIF, CLF, RIF, RLF elt = elts[0] try: iv = CIF(elt) except (TypeError, ValueError): emb = None else: # First try creating an ANRoot manually, because # extension(..., embedding=CLF(expr)) (or # ...QQbar(expr)) would normalize the expression in # QQbar, which currently is VERY slow in many cases. # This may fail when minpoly has close roots or elt is # a complicated symbolic expression. # TODO: Rewrite using #19362 and/or #17886 and/or # #15600 once those issues are solved. from sage.rings.qqbar import AlgebraicNumber, ANRoot try: elt = AlgebraicNumber(ANRoot(minpolys[0], iv)) except ValueError: pass # Force a real embedding when possible, to get the # right ordered ring structure. if (iv.imag().is_zero() or iv.imag().contains_zero() and elt.imag().is_zero()): emb = RLF(elt) else: emb = CLF(elt) return self.extension(minpolys[0], names[0], embedding=emb) try: # Doing the extension all at once is best, if possible... return self.extension(minpolys, names) except (TypeError, ValueError): # ...but we can also construct it iteratively return reduce(lambda R, ext: R.extension(*ext), zip(minpolys, names), self) # 2. Otherwise, try to return a polynomial ring from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing return PolynomialRing(self, elts)
def followstrand(f, x0, x1, y0a, prec=53): """ Return a piecewise linear aproximation of the homotopy continuation of the root y0a from x0 to x1 INPUT: - ``f`` -- a polynomial in two variables - ``x0`` -- a complex value, where the homotopy starts - ``x1`` -- a complex value, where the homotopy ends - ``y0a`` -- an approximate solution of the polynomial $F(y)=f(x_0,y)$ - ``prec`` -- the precission to use OUTPUT: A list of values (t, ytr, yti) such that: - ``t`` is a real number between zero and one - $f(t\cdot x_1+(1-t)\cdot x_0, y_{tr} + I\cdot y_{ti})$ is zero (or a good enough aproximation) - the piecewise linear path determined by the points has a tubular neighborhood where the actual homotopy continuation path lies, and no other root intersects it. EXAMPLES:: sage: R.<x,y> = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1, 0) sage: x1 = CC(1, 0.5) sage: followstrand(f, x0, x1, -1.0) # optional [(0.0, -1.0, 0.0), (0.063125, -1.0001106593601545, -0.02104011456212618), (0.12230468750000001, -1.0004151100003031, -0.04075695242737829), (0.17778564453125, -1.0008762007617709, -0.059227299979315154), (0.28181243896484376, -1.0021948141328698, -0.09380038023464156), (0.3728358840942383, -1.0038270754728402, -0.123962953123039), (0.4524813985824585, -1.005613540368227, -0.15026634875747985), (0.5221712237596512, -1.0074443351354077, -0.17320066690539515), (0.5831498207896948, -1.009246118007067, -0.1931978258913501), (0.636506093190983, -1.0109719597443307, -0.21063630045261386), (0.6831928315421101, -1.0125937110987449, -0.2258465242053158), (0.7648946236565826, -1.0156754074572174, -0.2523480191006915), (0.8261709677424369, -1.0181837235093538, -0.2721208327884435), (0.8721282258068277, -1.0201720738092597, -0.2868892148703381), (0.9410641129034139, -1.0233210275568283, -0.3089391941950436), (1.0, -1.026166099551513, -0.3276894025360433)] """ CIF = ComplexIntervalField(prec) CC = ComplexField(prec) G = f.change_ring(QQbar).change_ring(CIF) (x, y) = G.variables() g = G.subs({x: (1 - x) * CIF(x0) + x * CIF(x1)}) coefs = [] deg = g.total_degree() for d in range(deg + 1): for i in range(d + 1): c = CIF(g.coefficient({x: d - i, y: i})) cr = c.real() ci = c.imag() coefs += list(cr.endpoints()) coefs += list(ci.endpoints()) yr = CC(y0a).real() yi = CC(y0a).imag() try: if prec == 53: points = contpath(deg, coefs, yr, yi) else: points = contpath_mp(deg, coefs, yr, yi, prec) return points except: return followstrand(f, x0, x1, y0a, 2 * prec)
def _sing_as_alg(dop, iv): pol = dop.leading_coefficient().radical() return QQbar.polynomial_root(pol, CIF(iv))
def followstrand(f, x0, x1, y0a): """ Return a piecewise linear aproximation of the homotopy continuation of the root y0a from x0 to x1 INPUT: - ``f`` -- a polynomial in two variables - ``x0`` -- a complex value, where the homotopy starts - ``x1`` -- a complex value, where the homotopy ends - ``y0a`` -- an approximate solution of the polynomial $F(y)=f(x_0,y)$ OUTPUT: A list of values (t, ytr, yti) such that: - ``t`` is a real number between zero and one - $f(t\cdot x_1+(1-t)\cdot x_0, y_{tr} + I\cdot y_{ti})$ is zero (or a good enough aproximation) - the piecewise linear path determined by the points has a tubular neighborhood where the actual homotopy continuation path lies, and no other root intersects it. EXAMPLES:: sage: R.<x,y> = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1, 0) sage: x1 = CC(1, 0.5) sage: followstrand(f, x0, x1, -1.0) # optional [(0.0, -1.0, 0.0), (0.063125, -1.0001106593601545, -0.02104011456212618), (0.12230468750000001, -1.0004151100003031, -0.04075695242737829), (0.17778564453125, -1.0008762007617709, -0.059227299979315154), (0.28181243896484376, -1.0021948141328698, -0.09380038023464156), (0.3728358840942383, -1.0038270754728402, -0.123962953123039), (0.4524813985824585, -1.005613540368227, -0.15026634875747985), (0.5221712237596512, -1.0074443351354077, -0.17320066690539515), (0.5831498207896948, -1.009246118007067, -0.1931978258913501), (0.636506093190983, -1.0109719597443307, -0.21063630045261386), (0.6831928315421101, -1.0125937110987449, -0.2258465242053158), (0.7648946236565826, -1.0156754074572174, -0.2523480191006915), (0.8261709677424369, -1.0181837235093538, -0.2721208327884435), (0.8721282258068277, -1.0201720738092597, -0.2868892148703381), (0.9410641129034139, -1.0233210275568283, -0.3089391941950436), (1.0, -1.026166099551513, -0.3276894025360433)] """ G = f.change_ring(QQbar).change_ring(CIF) (x, y) = G.variables() g = G.subs({x: (1-x)*CIF(x0) + x*CIF(x1)}) coefs = [] deg = g.total_degree() for d in range(deg + 1): for i in range(d + 1): c = CIF(g.coefficient({x: d-i, y: i})) cr = c.real() ci = c.imag() coefs += list(cr.endpoints()) coefs += list(ci.endpoints()) yr = CC(y0a).real() yi = CC(y0a).imag() points = contpath(deg, coefs, yr, yi) return points