def brackets_to_table(L): r""" Return a html table enumerating all Lie brackets. INPUT: - ``L`` -- a Lie algebra """ # if the base ring is QQbar, display coefficients as radicals disp = QQbar.options('display_format') QQbar.options(display_format="radical") rows = [] for X, Y in combinations(L.basis(), 2): Z = X.bracket(Y) if Z: rows.append((X, Y, Z)) if not rows: return "" htmlstr = '<table class="brackets">\n' for (X, Y, Z) in rows: htmlstr += '<tr>' htmlstr += '<td class="brkt">[%s, %s]</td>' % (X, Y) htmlstr += '<td class="eq">=</td>' htmlstr += '<td class="res">%s</td>' % Z htmlstr += '</tr>\n' htmlstr += '</table>' return htmlstr
def __cmp__(self, other): r""" Comparison (using the complex embedding). TESTS:: sage: UCF = UniversalCyclotomicField() sage: l = [UCF.gen(3), UCF.gen(3)+1, UCF.gen(5), UCF.gen(5,2), ....: UCF.gen(4), 2*UCF.gen(4), UCF.gen(5)-22/3] sage: lQQbar = map(QQbar,l) sage: lQQbar.sort() sage: l.sort() sage: lQQbar == map(QQbar,l) True sage: for i in range(len(l)): ....: assert l[i] >= l[i] and l[i] <= l[i] ....: for j in range(i): ....: assert l[i] > l[j] and l[j] < l[i] """ if self._obj == other._obj: return 0 else: from sage.rings.qqbar import QQbar return cmp(QQbar(self), QQbar(other))
def regular_ngon(n): r""" Return a regular n-gon. EXAMPLES:: sage: from flatsurf.geometry.polygon import polygons sage: p = polygons.regular_ngon(17) sage: p Polygon: (0, 0), (1, 0), ..., (-1/2*a^14 + 15/2*a^12 - 45*a^10 + 275/2*a^8 - 225*a^6 + 189*a^4 - 70*a^2 + 15/2, 1/2*a) """ # The code below crashes for n=4! if n==4: return polygons.square(QQ(1)) from sage.rings.qqbar import QQbar c = QQbar.zeta(n).real() s = QQbar.zeta(n).imag() field, (c,s) = number_field_elements_from_algebraics((c,s)) cn = field.one() sn = field.zero() edges = [(cn,sn)] for _ in range(n-1): cn,sn = c*cn - s*sn, c*sn + s*cn edges.append((cn,sn)) return Polygons(field)(edges=edges)
def segments(points): """ Return the bounded segments of the Voronoi diagram of the given points. INPUT: - ``points`` -- a list of complex points OUTPUT: A list of pairs ``(p1, p2)``, where ``p1`` and ``p2`` are the endpoints of the segments in the Voronoi diagram. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import discrim, segments sage: R.<x,y> = QQ[] sage: f = y^3 + x^3 - 1 sage: disc = discrim(f) sage: sorted(segments(disc)) [(-192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316, -192951821525958031/90044183378780414), (-192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316, -144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), (192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316, 144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), (-192951821525958031/90044183378780414, 192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316), (-192951821525958031/90044183378780414, 1/38590364305191606), (1/38590364305191606, -144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), (1/38590364305191606, 144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), (-5/2*I + 5/2, -144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), (-5/2*I + 5/2, 5/2*I + 5/2), (5/2*I + 5/2, 144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326)] """ V = corrected_voronoi_diagram(tuple(points)) res = set([]) for region in V.regions().values(): if region.rays(): continue segments = region.facets() for s in segments: t = tuple((tuple(v.vector()) for v in s.vertices())) if t not in res and not tuple(reversed(t)) in res: res.add(t) return [(r[0] + QQbar.gen() * r[1], s[0] + QQbar.gen() * s[1]) for (r, s) in res]
def xseries(self, all_conjugates=True): r"""Returns the corresponding x-series. Parameters ---------- all_conjugates : bool (default: True) If ``True``, returns all conjugates x-representations of this Puiseux t-series. If ``False``, only returns one representative. Returns ------- list List of PuiseuxXSeries representations of this PuiseuxTSeries. """ # obtain relevant rings: # o R = parent ring of curve # o L = parent ring of T-series # o S = temporary polynomial ring over base ring of T-series # o P = Puiseux series ring L = self.ypart.parent() t = L.gen() S = L.base_ring()['z'] z = S.gen() R = self.f.parent() x,y = R.gens() P = PuiseuxSeriesRing(L.base_ring(), str(x)) x = P.gen() # given x = alpha + lambda*t^e solve for t. this involves finding an # e-th root of either (1/lambda) or of lambda, depending on e's sign ## (A sign on a ramification index ? hm) e = self.ramification_index abse = abs(e) lamb = S(self.xcoefficient) order = self.order if e > 0: phi = lamb*z**e - 1 else: phi = z**abse - lamb mu = phi.roots(QQbar, multiplicities=False)[0] if all_conjugates: zeta_e=QQbar.zeta(abse) conjugates = [mu*zeta_e**k for k in range(abse)] else: conjugates = [mu] map(lambda x: x.exactify(), conjugates) # determine the resulting x-series xseries = [] for c in conjugates: t = self.ypart.parent().gen() fconj = self.ypart(c*t) p = P(fconj(x**(QQ(1)/e))) p = p.add_bigoh(QQ(order+1)/abse) xseries.append(p) return xseries
def braid_in_segment(f, x0, x1): """ Return the braid formed by the `y` roots of ``f`` when `x` moves from ``x0`` to ``x1``. INPUT: - ``f`` -- a polynomial in two variables - ``x0`` -- a complex number - ``x1`` -- a complex number OUTPUT: A braid. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # 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: braid_in_segment(f, x0, x1) # optional - sirocco s1 """ CC = ComplexField(64) (x, y) = f.variables() I = QQbar.gen() X0 = QQ(x0.real()) + I*QQ(x0.imag()) X1 = QQ(x1.real()) + I*QQ(x1.imag()) F0 = QQbar[y](f(X0, y)) y0s = F0.roots(multiplicities=False) strands = [followstrand(f, x0, x1, CC(a)) for a in y0s] complexstrands = [[(a[0], CC(a[1], a[2])) for a in b] for b in strands] centralbraid = braid_from_piecewise(complexstrands) initialstrands = [] y0aps = [c[0][1] for c in complexstrands] used = [] for y0ap in y0aps: distances = [((y0ap - y0).norm(), y0) for y0 in y0s] y0 = sorted(distances)[0][1] if y0 in used: raise ValueError("different roots are too close") used.append(y0) initialstrands.append([(0, CC(y0)), (1, y0ap)]) initialbraid = braid_from_piecewise(initialstrands) F1 = QQbar[y](f(X1,y)) y1s = F1.roots(multiplicities=False) finalstrands = [] y1aps = [c[-1][1] for c in complexstrands] used = [] for y1ap in y1aps: distances = [((y1ap - y1).norm(), y1) for y1 in y1s] y1 = sorted(distances)[0][1] if y1 in used: raise ValueError("different roots are too close") used.append(y1) finalstrands.append([(0, y1ap), (1, CC(y1))]) finallbraid = braid_from_piecewise(finalstrands) return initialbraid * centralbraid * finallbraid
def generalized_series_term_valuation(z, i, j, iota=None): r""" Given z, i, j, return the valuation of the term x^(z+i) log(x)^j """ try: z = QQbar(z) except ValueError: # FIXME: It would be great if we could show the warning once per # relevant field warn( "Choosing an arbitrary complex embedding for {}.\n" "To avoid it, extend the coefficients of the base ring.".format( z.parent()), RuntimeWarning) z = z.parent().embeddings(QQbar)[0](z) if iota is None: iota = generalized_series_default_iota return ZZ(z + i - iota(z, j))
def _call_(self, x): r""" TESTS:: sage: UCF = UniversalCyclotomicField() sage: UCFtoQQbar = UCF.coerce_embedding() sage: UCFtoQQbar(UCF.gen(3)) # indirect doctest -0.500000000000000? + 0.866025403784439?*I """ obj = x._obj QQbar = self.codomain() if obj.IsRat(): return QQbar(obj.sage()) k = obj.Conductor().sage() coeffs = obj.CoeffsCyc(k).sage() zeta = QQbar.zeta(k) return QQbar(sum(coeffs[a] * zeta**a for a in range(1, k)))
def brackets_to_txt(L): r""" Return a text string enumerating all Lie brackets. INPUT: - ``L`` -- a Lie algebra """ # if the base ring is QQbar, display coefficients as radicals disp = QQbar.options('display_format') QQbar.options(display_format="radical") bracketstr = "" for X, Y in combinations(L.basis(), 2): Z = X.bracket(Y) if Z: bracketstr += " [%s, %s] = %s\n" % (X, Y, Z) QQbar.options(display_format=disp) return bracketstr
def dim_Joli( k, L, h): """ Return the dimension of the space $J_{k,L}(\varepsilon^h)$. INPUT k -- a half integer L -- an instance of Lattice_class h -- an integer """ h = Integer(h) g = k - h/2 try: g = Integer(g) except: return Integer(0) # preparations o_inv = L.o_invariant() V = L.shadow_vectors() V2 = L.shadow_vectors_of_order_2() n = L.rank() if k < n/2: return Integer(0) if n/2 <= k and k < 2+n/2: raise NotImplementedError( 'special weight %s: not yet implemented'%k) # scalar term scal = (L.det() + len(V2) * (-1)**(g+o_inv)) * (k-n/2-1)/24 # elliptic S-term ell_S = (L.chi(2) * QQbar.zeta(4)**g).real()/4 + Rational(Integer(12).kronecker(2*g+2*n+1))/6 # elliptic R-term ell_R = (L.chi(-3) * QQbar.zeta(24)**(n+2) * QQbar.zeta(6)**g).real() * (-1)**g/QQbar(27).sqrt() # parabolic term B = lambda t: t - t.floor() - Rational(1)/2 par = -sum( B(h/24-L.beta(x)) for x in V)/2 par -= (-1)**(g+o_inv) * sum(B(h/24-L.beta(x)) for x in V2)/2 return Integer(scal + ell_S + ell_R + par)
def isom_class_to_html_tablerow(label, ic, mathjax): r""" Return a html table row describing a labelled isomorphism class of gradings. INPUT: - ``label`` -- a string identifier - ``ic`` -- a :class:`GradingIsomorphismClass` - ``mathjax`` -- a boolean; whether to output the table as mathjax or not """ # if the base ring is QQbar, display coefficients as radicals disp = QQbar.options('display_format') QQbar.options(display_format="radical") indent = " " * 4 classname = "grading" # test if stratification or positivisable L = ic.representative().lie_algebra() try: st = stratification(L) strat = True except ValueError: strat = False if strat and st in ic: classname += " stratification positive" else: if ic.representative().has_positive_realization(): classname += " positive" else: classname += " nonpositive" tablerow = '<tr class="%s">\n' % classname tablerow += indent + '<td class="label">%s</td>\n' % label if mathjax: icstr = "\\(%s\\)" % grading_to_array(ic.representative(), amp="&") else: icstr = grading_to_html_table(ic.representative()) tablerow += indent + '<td>%s</td>\n' % icstr tablerow += "</tr>" QQbar.options(display_format=disp) return tablerow
def sqrt(self): """ Return a square root of ``self`` as an algebraic number. EXAMPLES:: sage: f = E(33) sage: f.sqrt() 0.9954719225730846? + 0.0950560433041827?*I sage: f.sqrt()**2 == f True """ return QQbar(self).sqrt()
def brackets_to_align(L, amp="&"): r""" Return a latex align* environment enumerating all Lie brackets. INPUT: - ``L`` -- a Lie algebra - ``amp`` -- a string to use as the ampersand symbol in the output """ rows = [] for X, Y in combinations(L.basis(), 2): Z = X.bracket(Y) if Z: row = "[%s, %s] %s = %s" % (latex(X), latex(Y), amp, latex(Z)) rows.append(row) if not rows: return "" latexstr = "\\begin{align*}\n" latexstr += "\\\\\n".join(rows) latexstr += "\\end{align*}" QQbar.options(display_format=disp) return latexstr
def _mpfr_(self, R): r""" TESTS:: sage: RR(E(7) + E(7,6)) 1.24697960371747 sage: 2*cos(2*pi/7).n() 1.24697960371747 """ if not self.is_real(): raise TypeError("self is not real") from sage.rings.qqbar import QQbar, AA return AA(QQbar(self))._mpfr_(R)
def _algebraic_(self, R): r""" TESTS:: sage: UCF = UniversalCyclotomicField() sage: AA(UCF.gen(5) + UCF.gen(5,4)) 0.618033988749895? sage: AA(UCF.gen(5)) Traceback (most recent call last): ... ValueError: Cannot coerce algebraic number with non-zero imaginary part to algebraic real """ return R(QQbar(self))
def family_two(n, backend=None): r""" Return the vector configuration of the simplicial arrangement `A(n,1)` from the family `\mathcal R(1)` in Grunbaum's list [Gru]_. The arrangement will have an ``n`` hyperplanes, with ``n`` even, consisting of the edges of the regular `n/2`-gon and the `n/2` lines of mirror symmetry. INPUT: - ``n`` -- integer. ``n`` `\geq 6`. The number of lines in the arrangement. - ``backend`` -- string (default = ``None``). The backend to use. OUTPUT: A vector configuration. EXAMPLES:: sage: from cn_hyperarr.infinite_families import * sage: pf = family_two(8,'normaliz'); pf # optional - pynormaliz Vector configuration of 8 vectors in dimension 3 The number of lines must be even:: sage: pf3 = family_two(3,'normaliz'); # optional - pynormaliz Traceback (most recent call last): ... AssertionError: n must be even The number of lines must be at least 6:: sage: pf4 = family_two(4,'normaliz') # optional - pynormaliz Traceback (most recent call last): ... ValueError: n (=2) must be an integer greater than 2 """ assert n % 2 == 0, "n must be even" reg_poly = polytopes.regular_polygon(n / QQ(2), backend='normaliz') reg_cone = Polyhedron( rays=[list(v.vector()) + [1] for v in reg_poly.vertices()], backend=backend) vecs = [h.A() for h in reg_cone.Hrepresentation()] z = QQbar.zeta(n) vecs += [[(z**k).real(), (z**k).imag(), 0] for k in range(n / QQ(2))] return VectorConfiguration(vecs, backend=backend)
def chi( self, t): """ Return the value of the Gauss sum $\sum_{x\in L^\bullet/L} e(tG[x]/2)/\sqrt D$, where $D = |L^\bullet/L|$. """ t = Integer(t)%self.shadow_level() if self.__chi.has_key(t): return self.__chi[t] l = self.shadow_level() z = QQbar.zeta(l) w = QQbar(self.det().sqrt()) V = self.values() self.__chi[t] = sum(len(V[a])*z**(t*a) for a in V)/w return self.__chi[t]
def _call_(self, x): r""" TESTS:: sage: UCF = UniversalCyclotomicField() sage: UCFtoQQbar = UCF.coerce_embedding() sage: UCFtoQQbar(UCF.gen(3)) # indirect doctest -0.500000000000000? + 0.866025403784439?*I """ obj = x._obj QQbar = self.codomain() if obj.IsRat(): return QQbar(obj.sage()) k = obj.Conductor().sage() coeffs = obj.CoeffsCyc(k).sage() zeta = QQbar.zeta(k) return QQbar(sum(coeffs[a] * zeta**a for a in range(1,k)))
def near_pencil_family(n, backend=None): r""" Return the vector configuration of the near pencil with ``n`` lines. All lines except for one share a common intersection point. INPUT: - ``n`` -- integer. ``n`` `\geq 3`. The number of lines in the near pencil. - ``backend`` -- string (default = ``None``). The backend to use. OUTPUT: A vector configuration. EXAMPLES: The near pencil with three hyperplanes:: sage: from cn_hyperarr.infinite_families import * sage: np = near_pencil_family(3,'normaliz'); np # optional - pynormaliz Vector configuration of 3 vectors in dimension 3 The near pencil arrangements are always congruence normal. Note, this test just shows there exists a region such that they are CN:: sage: near_pencils = [near_pencil_family(n,'normaliz') for n in range(3,6)] # optional - pynormaliz sage: [ np.is_congruence_normal() for np in near_pencils] # optional - pynormaliz [True, True, True] TESTS: Test that the backend is normaliz:: sage: np = near_pencil_family(3,'normaliz'); # optional - pynormaliz sage: np.backend() # optional - pynormaliz 'normaliz' """ z = QQbar.zeta(2 * n) vecs = [[(z**k).real(), (z**k).imag(), 0] for k in range(1, n)] vecs += [vector([0, -1, 1])] return VectorConfiguration(vecs, backend=backend)
def right_triangle(angle,leg0=None, leg1=None, hypotenuse=None): r""" Return a right triangle in a numberfield with an angle of pi*m/n. You can specify the length of the first leg (leg0), the second leg (leg1), or the hypotenuse. """ from sage.rings.qqbar import QQbar z=QQbar.zeta(2*angle.denom())**angle.numer() c = z.real() s = z.imag() if not leg0 is None: c,s = leg0*c/c,leg0*s/c elif not leg1 is None: c,s = leg1*c/s,leg1*s/s elif not hypotenuse is None: c,s = hypotenuse*c, hypotenuse*s field, (c,s) = number_field_elements_from_algebraics((c,s)) return Polygons(field)(edges=[(c,field.zero()),(field.zero(),s),(-c,-s)])
def braid_in_segment(g, x0, x1): """ Return the braid formed by the `y` roots of ``f`` when `x` moves from ``x0`` to ``x1``. INPUT: - ``g`` -- a polynomial factorization in two variables - ``x0`` -- a complex number - ``x1`` -- a complex number OUTPUT: A braid. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # 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: braid_in_segment(f.factor(), x0, x1) # optional - sirocco s1 TESTS: Check that :trac:`26503` is fixed:: sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0] sage: Kw.<wp> = NumberField(wp.minpoly(), embedding=wp) sage: R.<x, y> = Kw[] sage: z = -wp - 1 sage: f = y*(y + z)*x*(x - 1)*(x - y)*(x + z*y - 1)*(x + z*y + wp) sage: from sage.schemes.curves import zariski_vankampen as zvk sage: g = f.subs({x: x + 2*y}) sage: p1 = QQbar(sqrt(-1/3)) sage: p2 = QQbar(1/2+sqrt(-1/3)/2) sage: B = zvk.braid_in_segment(g.factor(),CC(p1),CC(p2)) # optional - sirocco sage: B # optional - sirocco s5*s3^-1 """ (x, y) = g.value().parent().gens() I = QQbar.gen() X0 = QQ(x0.real()) + I * QQ(x0.imag()) X1 = QQ(x1.real()) + I * QQ(x1.imag()) intervals = {} precision = {} y0s = [] for (f, naux) in g: if f.variables() == (y, ): F0 = QQbar[y](f.base_ring()[y](f)) else: F0 = QQbar[y](f(X0, y)) y0sf = F0.roots(multiplicities=False) y0s += list(y0sf) precision[f] = 53 while True: CIFp = ComplexIntervalField(precision[f]) intervals[f] = [r.interval(CIFp) for r in y0sf] if not any( a.overlaps(b) for a, b in itertools.combinations(intervals[f], 2)): break precision[f] *= 2 strands = [ followstrand(f[0], [p[0] for p in g if p[0] != f[0]], x0, x1, i.center(), precision[f[0]]) for f in g for i in intervals[f[0]] ] complexstrands = [[(QQ(a[0]), QQ(a[1]), QQ(a[2])) for a in b] for b in strands] centralbraid = braid_from_piecewise(complexstrands) initialstrands = [] finalstrands = [] initialintervals = roots_interval_cached(g.value(), X0) finalintervals = roots_interval_cached(g.value(), X1) for cs in complexstrands: ip = cs[0][1] + I * cs[0][2] fp = cs[-1][1] + I * cs[-1][2] matched = 0 for center, interval in initialintervals.items(): if ip in interval: initialstrands.append([(0, center.real(), center.imag()), (1, cs[0][1], cs[0][2])]) matched += 1 if matched == 0: raise ValueError("unable to match braid endpoint with root") if matched > 1: raise ValueError("braid endpoint mathes more than one root") matched = 0 for center, interval in finalintervals.items(): if fp in interval: finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())]) matched += 1 if matched == 0: raise ValueError("unable to match braid endpoint with root") if matched > 1: raise ValueError("braid endpoint mathes more than one root") initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) return initialbraid * centralbraid * finalbraid
def closed_form(self, n='n'): r""" Return a symbolic expression in ``n``, which equals the n-th term of the sequence. It is a well-known property of C-finite sequences ``a_n`` that they have a closed form of the type: .. MATH:: a_n = \sum_{i=1}^d c_i(n) \cdot r_i^n, where ``r_i`` are the roots of the characteristic equation and ``c_i(n)`` is a polynomial (whose degree equals the multiplicity of ``r_i`` minus one). This is a natural generalization of Binet's formula for Fibonacci numbers. See, for instance, [KP2011, Theorem 4.1]. Note that if the o.g.f. has a polynomial part, that is, if the numerator degree is not strictly less than the denominator degree, then this closed form holds only when ``n`` exceeds the degree of that polynomial part. In that case, the returned expression will differ from the sequence for small ``n``. EXAMPLES:: sage: CFiniteSequence(1/(1-x)).closed_form() 1 sage: CFiniteSequence(x^2/(1-x)).closed_form() 1 sage: CFiniteSequence(1/(1-x^2)).closed_form() 1/2*(-1)^n + 1/2 sage: CFiniteSequence(1/(1+x^3)).closed_form() 1/3*(-1)^n + 1/3*(1/2*I*sqrt(3) + 1/2)^n + 1/3*(-1/2*I*sqrt(3) + 1/2)^n sage: CFiniteSequence(1/(1-x)/(1-2*x)/(1-3*x)).closed_form() 9/2*3^n - 4*2^n + 1/2 Binet's formula for the Fibonacci numbers:: sage: CFiniteSequence(x/(1-x-x^2)).closed_form() sqrt(1/5)*(1/2*sqrt(5) + 1/2)^n - sqrt(1/5)*(-1/2*sqrt(5) + 1/2)^n sage: [_.subs(n=k).full_simplify() for k in range(6)] [0, 1, 1, 2, 3, 5] sage: CFiniteSequence((4*x+3)/(1-2*x-5*x^2)).closed_form() 1/2*(sqrt(6) + 1)^n*(7*sqrt(1/6) + 3) - 1/2*(-sqrt(6) + 1)^n*(7*sqrt(1/6) - 3) Examples with multiple roots:: sage: CFiniteSequence(x*(x^2+4*x+1)/(1-x)^5).closed_form() 1/4*n^4 + 1/2*n^3 + 1/4*n^2 sage: CFiniteSequence((1+2*x-x^2)/(1-x)^4/(1+x)^2).closed_form() 1/12*n^3 - 1/8*(-1)^n*(n + 1) + 3/4*n^2 + 43/24*n + 9/8 sage: CFiniteSequence(1/(1-x)^3/(1-2*x)^4).closed_form() 4/3*(n^3 - 3*n^2 + 20*n - 36)*2^n + 1/2*n^2 + 19/2*n + 49 sage: CFiniteSequence((x/(1-x-x^2))^2).closed_form() 1/5*(n - sqrt(1/5))*(1/2*sqrt(5) + 1/2)^n + 1/5*(n + sqrt(1/5))*(-1/2*sqrt(5) + 1/2)^n """ from sage.arith.all import binomial from sage.rings.qqbar import QQbar from sage.symbolic.ring import SR n = SR(n) expr = SR.zero() R = FractionField(PolynomialRing(QQbar, self.parent().variable_name())) ogf = R(self.ogf()) __, parts = ogf.partial_fraction_decomposition(decompose_powers=False) for part in parts: denom = part.denominator().factor() denom_base, denom_exp = denom[0] # denominator is of the form (x+b)^{m+1} m = denom_exp - 1 b = denom_base.constant_coefficient() # check that the partial fraction decomposition was indeed done correctly # (that is, there is only one factor, of degree 1, and monic) assert len(denom) == 1 and len(denom_base.list( )) == 2 and denom_base[1] == 1 and denom.unit() == 1 r = SR((-1 / b).radical_expression()) c = SR.zero() for k, a in enumerate(part.numerator()): a = -QQbar(a) if k % 2 else QQbar(a) bino = binomial(n + m - k, m) c += bino * SR((a * b**(k - m - 1)).radical_expression()) expr += c.expand() * r**n return expr
def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0): """ Compute the complex roots of a given polynomial with exact coefficients (integer, rational, Gaussian rational, and algebraic coefficients are supported). Returns a list of pairs of a root and its multiplicity. Roots are returned as a ComplexIntervalFieldElement; each interval includes exactly one root, and the intervals are disjoint. By default, the algorithm will do a squarefree decomposition to get squarefree polynomials. The skip_squarefree parameter lets you skip this step. (If this step is skipped, and the polynomial has a repeated root, then the algorithm will loop forever!) You can specify retval='interval' (the default) to get roots as complex intervals. The other options are retval='algebraic' to get elements of QQbar, or retval='algebraic_real' to get only the real roots, and to get them as elements of AA. EXAMPLES:: sage: from sage.rings.polynomial.complex_roots import complex_roots sage: x = polygen(ZZ) sage: complex_roots(x^5 - x - 1) [(1.167303978261419?, 1), (-0.764884433600585? - 0.352471546031727?*I, 1), (-0.764884433600585? + 0.352471546031727?*I, 1), (0.181232444469876? - 1.083954101317711?*I, 1), (0.181232444469876? + 1.083954101317711?*I, 1)] sage: v=complex_roots(x^2 + 27*x + 181) Unfortunately due to numerical noise there can be a small imaginary part to each root depending on CPU, compiler, etc, and that affects the printing order. So we verify the real part of each root and check that the imaginary part is small in both cases:: sage: v # random [(-14.61803398874990?..., 1), (-12.3819660112501...? + 0.?e-27*I, 1)] sage: sorted((v[0][0].real(),v[1][0].real())) [-14.61803398874989?, -12.3819660112501...?] sage: v[0][0].imag() < 1e25 True sage: v[1][0].imag() < 1e25 True sage: K.<im> = NumberField(x^2 + 1) sage: eps = 1/2^100 sage: x = polygen(K) sage: p = (x-1)*(x-1-eps)*(x-1+eps)*(x-1-eps*im)*(x-1+eps*im) This polynomial actually has all-real coefficients, and is very, very close to (x-1)^5:: sage: [RR(QQ(a)) for a in list(p - (x-1)^5)] [3.87259191484932e-121, -3.87259191484932e-121] sage: rts = complex_roots(p) sage: [ComplexIntervalField(10)(rt[0] - 1) for rt in rts] [-7.8887?e-31, 0, 7.8887?e-31, -7.8887?e-31*I, 7.8887?e-31*I] We can get roots either as intervals, or as elements of QQbar or AA. :: sage: p = (x^2 + x - 1) sage: p = p * p(x*im) sage: p -x^4 + (im - 1)*x^3 + im*x^2 + (-im - 1)*x + 1 Two of the roots have a zero real component; two have a zero imaginary component. These zero components will be found slightly inaccurately, and the exact values returned are very sensitive to the (non-portable) results of NumPy. So we post-process the roots for printing, to get predictable doctest results. :: sage: def tiny(x): ....: return x.contains_zero() and x.absolute_diameter() < 1e-14 sage: def smash(x): ....: x = CIF(x[0]) # discard multiplicity ....: if tiny(x.imag()): return x.real() ....: if tiny(x.real()): return CIF(0, x.imag()) sage: rts = complex_roots(p); type(rts[0][0]), sorted(map(smash, rts)) (<type 'sage.rings.complex_interval.ComplexIntervalFieldElement'>, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) sage: rts = complex_roots(p, retval='algebraic'); type(rts[0][0]), sorted(map(smash, rts)) (<class 'sage.rings.qqbar.AlgebraicNumber'>, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) sage: rts = complex_roots(p, retval='algebraic_real'); type(rts[0][0]), rts (<class 'sage.rings.qqbar.AlgebraicReal'>, [(-1.618033988749895?, 1), (0.618033988749895?, 1)]) TESTS: Verify that :trac:`12026` is fixed:: sage: f = matrix(QQ, 8, lambda i, j: 1/(i + j + 1)).charpoly() sage: from sage.rings.polynomial.complex_roots import complex_roots sage: len(complex_roots(f)) 8 """ if skip_squarefree: factors = [(p, 1)] else: factors = p.squarefree_decomposition() prec = 53 while True: CC = ComplexField(prec) CCX = CC['x'] all_rts = [] ok = True for (factor, exp) in factors: cfac = CCX(factor) rts = cfac.roots(multiplicities=False) # Make sure the number of roots we found is the degree. If # we don't find that many roots, it's because the # precision isn't big enough and though the (possibly # exact) polynomial "factor" is squarefree, it is not # squarefree as an element of CCX. if len(rts) < factor.degree(): ok = False break irts = interval_roots(factor, rts, max(prec, min_prec)) if irts is None: ok = False break if retval != 'interval': factor = QQbar.common_polynomial(factor) for irt in irts: all_rts.append((irt, factor, exp)) if ok and intervals_disjoint([rt for (rt, fac, mult) in all_rts]): all_rts = sort_complex_numbers_for_display(all_rts) if retval == 'interval': return [(rt, mult) for (rt, fac, mult) in all_rts] elif retval == 'algebraic': return [(QQbar.polynomial_root(fac, rt), mult) for (rt, fac, mult) in all_rts] elif retval == 'algebraic_real': rts = [] for (rt, fac, mult) in all_rts: qqbar_rt = QQbar.polynomial_root(fac, rt) if qqbar_rt.imag().is_zero(): rts.append((AA(qqbar_rt), mult)) return rts prec = prec * 2
def complex_roots(p, skip_squarefree=False, retval='interval', min_prec=0): """ Compute the complex roots of a given polynomial with exact coefficients (integer, rational, Gaussian rational, and algebraic coefficients are supported). Returns a list of pairs of a root and its multiplicity. Roots are returned as a ComplexIntervalFieldElement; each interval includes exactly one root, and the intervals are disjoint. By default, the algorithm will do a squarefree decomposition to get squarefree polynomials. The skip_squarefree parameter lets you skip this step. (If this step is skipped, and the polynomial has a repeated root, then the algorithm will loop forever!) You can specify retval='interval' (the default) to get roots as complex intervals. The other options are retval='algebraic' to get elements of QQbar, or retval='algebraic_real' to get only the real roots, and to get them as elements of AA. EXAMPLES:: sage: from sage.rings.polynomial.complex_roots import complex_roots sage: x = polygen(ZZ) sage: complex_roots(x^5 - x - 1) [(1.167303978261419?, 1), (-0.764884433600585? - 0.352471546031727?*I, 1), (-0.764884433600585? + 0.352471546031727?*I, 1), (0.181232444469876? - 1.083954101317711?*I, 1), (0.181232444469876? + 1.083954101317711?*I, 1)] sage: v=complex_roots(x^2 + 27*x + 181) Unfortunately due to numerical noise there can be a small imaginary part to each root depending on CPU, compiler, etc, and that affects the printing order. So we verify the real part of each root and check that the imaginary part is small in both cases:: sage: v # random [(-14.61803398874990?..., 1), (-12.3819660112501...? + 0.?e-27*I, 1)] sage: sorted((v[0][0].real(),v[1][0].real())) [-14.61803398874989?, -12.3819660112501...?] sage: v[0][0].imag() < 1e25 True sage: v[1][0].imag() < 1e25 True sage: K.<im> = NumberField(x^2 + 1) sage: eps = 1/2^100 sage: x = polygen(K) sage: p = (x-1)*(x-1-eps)*(x-1+eps)*(x-1-eps*im)*(x-1+eps*im) This polynomial actually has all-real coefficients, and is very, very close to (x-1)^5:: sage: [RR(QQ(a)) for a in list(p - (x-1)^5)] [3.87259191484932e-121, -3.87259191484932e-121] sage: rts = complex_roots(p) sage: [ComplexIntervalField(10)(rt[0] - 1) for rt in rts] [-7.8887?e-31, 0, 7.8887?e-31, -7.8887?e-31*I, 7.8887?e-31*I] We can get roots either as intervals, or as elements of QQbar or AA. :: sage: p = (x^2 + x - 1) sage: p = p * p(x*im) sage: p -x^4 + (im - 1)*x^3 + im*x^2 + (-im - 1)*x + 1 Two of the roots have a zero real component; two have a zero imaginary component. These zero components will be found slightly inaccurately, and the exact values returned are very sensitive to the (non-portable) results of NumPy. So we post-process the roots for printing, to get predictable doctest results. :: sage: def tiny(x): ... return x.contains_zero() and x.absolute_diameter() < 1e-14 sage: def smash(x): ... x = CIF(x[0]) # discard multiplicity ... if tiny(x.imag()): return x.real() ... if tiny(x.real()): return CIF(0, x.imag()) sage: rts = complex_roots(p); type(rts[0][0]), sorted(map(smash, rts)) (<type 'sage.rings.complex_interval.ComplexIntervalFieldElement'>, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) sage: rts = complex_roots(p, retval='algebraic'); type(rts[0][0]), sorted(map(smash, rts)) (<class 'sage.rings.qqbar.AlgebraicNumber'>, [-1.618033988749895?, -0.618033988749895?*I, 1.618033988749895?*I, 0.618033988749895?]) sage: rts = complex_roots(p, retval='algebraic_real'); type(rts[0][0]), rts (<class 'sage.rings.qqbar.AlgebraicReal'>, [(-1.618033988749895?, 1), (0.618033988749895?, 1)]) TESTS: Verify that trac 12026 is fixed:: sage: f = matrix(QQ, 8, lambda i, j: 1/(i + j + 1)).charpoly() sage: from sage.rings.polynomial.complex_roots import complex_roots sage: len(complex_roots(f)) 8 """ if skip_squarefree: factors = [(p, 1)] else: factors = p.squarefree_decomposition() prec = 53 while True: CC = ComplexField(prec) CCX = CC['x'] all_rts = [] ok = True for (factor, exp) in factors: cfac = CCX(factor) rts = cfac.roots(multiplicities=False) # Make sure the number of roots we found is the degree. If # we don't find that many roots, it's because the # precision isn't big enough and though the (possibly # exact) polynomial "factor" is squarefree, it is not # squarefree as an element of CCX. if len(rts) < factor.degree(): ok = False break irts = interval_roots(factor, rts, max(prec, min_prec)) if irts is None: ok = False break if retval != 'interval': factor = QQbar.common_polynomial(factor) for irt in irts: all_rts.append((irt, factor, exp)) if ok and intervals_disjoint([rt for (rt, fac, mult) in all_rts]): all_rts = sort_complex_numbers_for_display(all_rts) if retval == 'interval': return [(rt, mult) for (rt, fac, mult) in all_rts] elif retval == 'algebraic': return [(QQbar.polynomial_root(fac, rt), mult) for (rt, fac, mult) in all_rts] elif retval == 'algebraic_real': rts = [] for (rt, fac, mult) in all_rts: qqbar_rt = QQbar.polynomial_root(fac, rt) if qqbar_rt.imag().is_zero(): rts.append((AA(qqbar_rt), mult)) return rts prec = prec * 2
def braid_in_segment(f, x0, x1): """ Return the braid formed by the `y` roots of ``f`` when `x` moves from ``x0`` to ``x1``. INPUT: - ``f`` -- a polynomial in two variables - ``x0`` -- a complex number - ``x1`` -- a complex number OUTPUT: A braid. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # 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: braid_in_segment(f, x0, x1) # optional - sirocco s1 TESTS: Check that :trac:`26503` is fixed:: sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0] sage: Kw.<wp> = NumberField(wp.minpoly(), embedding=wp) sage: R.<x, y> = Kw[] sage: z = -wp - 1 sage: f = y*(y + z)*x*(x - 1)*(x - y)*(x + z*y - 1)*(x + z*y + wp) sage: from sage.schemes.curves import zariski_vankampen as zvk sage: g = f.subs({x: x + 2*y}) sage: p1 = QQbar(sqrt(-1/3)) sage: p2 = QQbar(1/2+sqrt(-1/3)/2) sage: B = zvk.braid_in_segment(g,CC(p1),CC(p2)) # optional - sirocco sage: B.left_normal_form() # optional - sirocco (1, s5) """ CC = ComplexField(64) (x, y) = f.variables() I = QQbar.gen() X0 = QQ(x0.real()) + I * QQ(x0.imag()) X1 = QQ(x1.real()) + I * QQ(x1.imag()) F0 = QQbar[y](f(X0, y)) y0s = F0.roots(multiplicities=False) strands = [followstrand(f, x0, x1, CC(a)) for a in y0s] complexstrands = [[(a[0], CC(a[1], a[2])) for a in b] for b in strands] centralbraid = braid_from_piecewise(complexstrands) initialstrands = [] y0aps = [c[0][1] for c in complexstrands] used = [] for y0ap in y0aps: distances = [((y0ap - y0).norm(), y0) for y0 in y0s] y0 = sorted(distances)[0][1] if y0 in used: raise ValueError("different roots are too close") used.append(y0) initialstrands.append([(0, y0), (1, y0ap)]) initialbraid = braid_from_piecewise(initialstrands) F1 = QQbar[y](f(X1, y)) y1s = F1.roots(multiplicities=False) finalstrands = [] y1aps = [c[-1][1] for c in complexstrands] used = [] for y1ap in y1aps: distances = [((y1ap - y1).norm(), y1) for y1 in y1s] y1 = sorted(distances)[0][1] if y1 in used: raise ValueError("different roots are too close") used.append(y1) finalstrands.append([(0, y1ap), (1, y1)]) finallbraid = braid_from_piecewise(finalstrands) return initialbraid * centralbraid * finallbraid
def braid_monodromy(f): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial INPUT: - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers. OUTPUT: A list of braids. The braids correspond to paths based in the same point; each of this paths is the conjugated of a loop around one of the points in the discriminant of the projection of ``f``. .. NOTE:: The projection over the `x` axis is used if there are no vertical asymptotes. Otherwise, a linear change of variables is done to fall into the previous case. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy sage: R.<x,y> = QQ[] sage: f = (x^2-y^3)*(x+3*y-5) sage: braid_monodromy(f) # optional - sirocco [s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1, s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1, s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, s1*s0*s2*s0^-1*s2*s1^-1] """ global roots_interval_cache (x, y) = f.parent().gens() F = f.base_ring() g = f.radical() d = g.degree(y) while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) disc = discrim(g) V = corrected_voronoi_diagram(tuple(disc)) G = Graph() for reg in V.regions().values(): G = G.union(reg.vertex_graph()) E = Graph() for reg in V.regions().values(): if reg.rays() or reg.lines(): E = E.union(reg.vertex_graph()) p = next(E.vertex_iterator()) geombasis = geometric_basis(G, E, p) segs = set([]) for p in geombasis: for s in zip(p[:-1], p[1:]): if (s[1], s[0]) not in segs: segs.add((s[0], s[1])) I = QQbar.gen() segs = [(a[0] + I * a[1], b[0] + I * b[1]) for (a, b) in segs] vertices = list(set(flatten(segs))) tocacheverts = [(g, v) for v in vertices] populate_roots_interval_cache(tocacheverts) gfac = g.factor() try: braidscomputed = list( braid_in_segment([(gfac, seg[0], seg[1]) for seg in segs])) except ChildProcessError: # hack to deal with random fails first time braidscomputed = list( braid_in_segment([(gfac, seg[0], seg[1]) for seg in segs])) segsbraids = dict() for braidcomputed in braidscomputed: seg = (braidcomputed[0][0][1], braidcomputed[0][0][2]) beginseg = (QQ(seg[0].real()), QQ(seg[0].imag())) endseg = (QQ(seg[1].real()), QQ(seg[1].imag())) b = braidcomputed[1] segsbraids[(beginseg, endseg)] = b segsbraids[(endseg, beginseg)] = b.inverse() B = b.parent() result = [] for path in geombasis: braidpath = B.one() for i in range(len(path) - 1): x0 = tuple(path[i].vector()) x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) return result
def lie_algebra_isomorphism_classes(F, dim): r""" Return a list of isomorphism classes of Lie algebras. INPUT: - ``F`` -- the base field of the Lie algebras - ``dim`` -- the dimension of Lie algebras to list The full listing is implemented for the fields of rationals ``QQ`` and algebraic numbers ``QQbar`` up to dimension 6. For dimension 7, the returned list is incomplete due to existence of one parameter families of Lie algebras, but contains at least one representative from each family. """ if F not in [QQ, QQbar]: return NotImplemented if dim == 1: return [L1_1(F)] if dim == 2: return [L2_1(F)] if dim == 3: return [L3_1(F), L3_2(F)] if dim == 4: return [L4_1(F), L4_2(F), L4_3(F)] if dim == 5: return [L5_1(F), L5_2(F), L5_3(F), L5_4(F), L5_5(F), L5_6(F), L5_7(F), L5_8(F), L5_9(F)] if dim == 6: l = [L6_1(F), L6_2(F), L6_3(F), L6_4(F), L6_5(F), L6_6(F), L6_7(F), L6_8(F), L6_9(F), L6_10(F), L6_11(F), L6_12(F), L6_13(F), L6_14(F), L6_15(F), L6_16(F), L6_17(F), L6_18(F), L6_19(F, -1)] if F == QQ: l += [L6_19(F, 1)] l += [L6_20(F), L6_21(F, -1)] if F == QQ: l += [L6_21(F, 1)] l += [L6_22(F, 0), L6_22(F, 1)] if F == QQ: l += [L6_22(F, -1)] l += [L6_23(F)] l += [L6_24(F, 0), L6_24(F, 1)] if F == QQ: l += [L6_24(F, -1)] l += [L6_25(F), L6_26(F), L6_27(F), L6_28(F)] return l if dim == 7: if F == QQ: return NotImplemented i = QQbar.gens()[0] l = [L37A(F), L37B(F), L37C(F), L37D(F)] l += [L357A(F), L357B(F), L357C(F)] l += [L27A(F), L27B(F)] l += [L257A(F), L257B(F), L257C(F), L257D(F), L257E(F), L257F(F), L257G(F), L257H(F), L257I(F), L257J(F), L257K(F), L257L(F)] l += [L247A(F), L247B(F), L247C(F), L247D(F), L247E(F), L247F(F), L247G(F), L247H(F), L247I(F), L247J(F), L247K(F), L247L(F), L247M(F), L247N(F), L247O(F), L247P(F), L247Q(F), L247R(F)] l += [L2457A(F), L2457B(F), L2457C(F), L2457D(F), L2457E(F), L2457F(F), L2457G(F), L2457H(F), L2457I(F), L2457J(F), L2457K(F), L2457L(F), L2457M(F)] l += [L2357A(F), L2357B(F), L2357C(F), L2357D(F)] l += [L23457A(F), L23457B(F), L23457C(F), L23457D(F), L23457E(F), L23457F(F), L23457G(F)] l += [L17(F)] l += [L157(F)] # 147 has a family # 147C = 147E(0) = 147E(1) = 247P l += [L147A(F), L147B(F), L147D(F)] l += [L147E(F, 2)] # singular value 147E(2)=147E(1/2)=147E(-1) # complex singular value same as 1/2-sqrt(3)/2*i l += [L147E(F, QQbar(1) / 2 + QQbar(sqrt(3)) / 2 * i)] # non-singular value # 147E(3)=147E(1/3)=147E(2/3)=147E(3/2)= 147E(-1/2)=147E(-2) l += [L147E(F, 3)] # non-singular value # 147E(5)=147E(1/5)=147E(4/5)=147E(5/4)= 147E(-1/4)=147E(-4) l += [L147E(F, 5)] l += [L1457A(F), L1457B(F)] l += [L137A(F), L137B(F), L137C(F), L137D(F)] # 1357 has a family l += [L1357A(F), L1357B(F), L1357C(F), L1357D(F), L1357E(F), L1357F(F), L1357G(F), L1357H(F), L1357I(F), L1357J(F), L1357K(F), L1357L(F)] # 1357M(a)=1357M(a') iff a=a' # 1357M(0)=2357B l += [L1357M(F, 1)] # singular value l += [L1357M(F, 2)] # singular value l += [L1357M(F, -1)] # singular value # l += [L1357M(F, QQbar(1) / 2)] # singular value, same as 1357K l += [L1357M(F, -QQbar(1) / 3)] # singular value # complex singular value l += [L1357M(F, QQbar(1) / 2 - QQbar(sqrt(3)) / 2 * i)] # complex singular value l += [L1357M(F, QQbar(1) / 2 + QQbar(sqrt(3)) / 2 * i)] l += [L1357M(F, 3)] # non singular value l += [L1357M(F, 5)] # non singular value # 1357N(a)=1357N(a') iff a=a' l += [L1357N(F, 0)] # singular value l += [L1357N(F, 1)] # singular value l += [L1357N(F, 3)] # non singular value l += [L1357N(F, 5)] # non singular value l += [L1357O(F), L1357P(F), L1357Q(F), L1357R(F)] # 1357S(a)=1357S(a') iff a*a'=1 # 1357S(1)=2357D l += [L1357S(F, 0)] # singular value l += [L1357S(F, QQbar(1) / 2)] # singular value l += [L1357S(F, -1)] # singular value # complex singular value l += [L1357S(F, QQbar(1) / 2 - QQbar(sqrt(3)) / 2 * i)] # complex singular value l += [L1357S(F, QQbar(1) / 2 + QQbar(sqrt(3)) / 2 * i)] l += [L1357S(F, 3)] # non singular value l += [L1357S(F, 5)] # non singular value # 13457 # 13457H in Seeley is not a Lie algebra l += [L13457A(F), L13457B(F), L13457C(F), L13457D(F), L13457E(F), L13457F(F), L13457G(F), L13457I(F)] # 12457 has a family l += [L12457A(F), L12457B(F), L12457C(F), L12457D(F), L12457E(F), L12457F(F), L12457G(F), L12457H(F), L12457I(F), L12457J(F), L12457K(F), L12457L(F), L12457M(F)] # 12457N(a)=12457N(a') iff a=a' or a=-a' # l += [L12457N(F, 0)] # singular value, same as 12457M l += [L12457N(F, 1)] # non singular value l += [L12457N(F, 3)] # non singular value l += [L12457N(F, 5)] # non singular value l += [L12357A(F), L12357B(F), L12357C(F)] # 123457 has a family l += [L123457A(F), L123457B(F), L123457C(F), L123457D(F), L123457E(F), L123457F(F), L123457G(F), L123457H(F)] # 123457I(a)=123457I(a') iff a=a' l += [L123457I(F, 0)] # non singular value #l += [L123457I(F, 1)] # singular value, same as 123457G l += [L123457I(F, 2)] # singular value l += [L123457I(F, 3)] # singular value l += [L123457I(F, -1)] # singular value # complex singular value l += [L123457I(F, QQbar(1) / 2 - QQbar(sqrt(3)) / 2 * i)] # complex singular value l += [L123457I(F, QQbar(1) / 2 + QQbar(sqrt(3)) / 2 * i)] return l return NotImplemented
def sqrt(self, extend=True, all=False): """ Return a square root of ``self``. With default options, the output is an element of the universal cyclotomic field when this element is expressed via a single root of unity (including rational numbers). Otherwise, return an algebraic number. INPUT: - ``extend`` -- bool (default: ``True``); if ``True``, might return a square root in the algebraic closure of the rationals. If false, return a square root in the universal cyclotomic field or raises an error. - ``all`` -- bool (default: ``False``); if ``True``, return a list of all square roots. EXAMPLES:: sage: UCF = UniversalCyclotomicField() sage: UCF(3).sqrt() E(12)^7 - E(12)^11 sage: (UCF(3).sqrt())**2 3 sage: r = UCF(-1400 / 143).sqrt() sage: r**2 -1400/143 sage: E(33).sqrt() -E(33)^17 sage: E(33).sqrt() ** 2 E(33) sage: (3 * E(5)).sqrt() -E(60)^11 + E(60)^31 sage: (3 * E(5)).sqrt() ** 2 3*E(5) Setting ``all=True`` you obtain the two square roots in a list:: sage: UCF(3).sqrt(all=True) [E(12)^7 - E(12)^11, -E(12)^7 + E(12)^11] sage: (1 + UCF.zeta(5)).sqrt(all=True) [1.209762576525833? + 0.3930756888787117?*I, -1.209762576525833? - 0.3930756888787117?*I] In the following situation, Sage is not (yet) able to compute a square root within the universal cyclotomic field:: sage: (E(5) + E(5, 2)).sqrt() 0.7476743906106103? + 1.029085513635746?*I sage: (E(5) + E(5, 2)).sqrt(extend=False) Traceback (most recent call last): ... NotImplementedError: sqrt() not fully implemented for elements of Universal Cyclotomic Field """ if all: s = self.sqrt(all=False) return [s, -s] UCF = self.parent() # rational case if self._obj.IsRat(): D = self._obj.sage() if self._obj.IsInt(): return UCF_sqrt_int(D, UCF) else: return UCF_sqrt_int(D.numerator(), UCF) / \ UCF_sqrt_int(D.denominator(), UCF) # root of unity k = self._obj.Conductor() coeffs = self._obj.CoeffsCyc(k).sage() if sum(bool(x) for x in coeffs) == 1: for i, x in enumerate(coeffs): if x: break return UCF(x).sqrt() * UCF.zeta(2 * k, i) # no method to construct square roots yet... if extend: return QQbar(self).sqrt() else: raise NotImplementedError( "sqrt() not fully implemented for elements of Universal Cyclotomic Field" )
def roots_interval(f, x0): """ Find disjoint intervals that isolate the roots of a polynomial for a fixed value of the first variable. INPUT: - ``f`` -- a bivariate squarefree polynomial - ``x0`` -- a value where the first coordinate will be fixed The intervals are taken as big as possible to be able to detect when two approximate roots of `f(x_0, y)` correspond to the same exact root. The result is given as a dictionary, where the keys are approximations to the roots with rational real and imaginary parts, and the values are intervals containing them. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import roots_interval sage: R.<x,y> = QQ[] sage: f = y^3 - x^2 sage: ri = roots_interval(f, 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, 138907099/160396102*I - 1/2: -1.? + 1.?*I, 1: 1.? + 0.?*I} sage: [r.endpoints() for r in ri.values()] [(0.566987298107781 - 0.433012701892219*I, 1.43301270189222 + 0.433012701892219*I, 0.566987298107781 + 0.433012701892219*I, 1.43301270189222 - 0.433012701892219*I), (-0.933012701892219 - 1.29903810567666*I, -0.0669872981077806 - 0.433012701892219*I, -0.933012701892219 - 0.433012701892219*I, -0.0669872981077806 - 1.29903810567666*I), (-0.933012701892219 + 0.433012701892219*I, -0.0669872981077806 + 1.29903810567666*I, -0.933012701892219 + 1.29903810567666*I, -0.0669872981077806 + 0.433012701892219*I)] """ x, y = f.parent().gens() I = QQbar.gen() fx = QQbar[y](f.subs({x: QQ(x0.real()) + I * QQ(x0.imag())})) roots = fx.roots(QQbar, multiplicities=False) result = {} for i in range(len(roots)): r = roots[i] prec = 53 IF = ComplexIntervalField(prec) CF = ComplexField(prec) divisor = 4 diam = min((CF(r) - CF(r0)).abs() for r0 in roots[:i] + roots[i + 1:]) / divisor envelop = IF(diam) * IF((-1, 1), (-1, 1)) while not newton(fx, r, r + envelop) in r + envelop: prec += 53 IF = ComplexIntervalField(prec) CF = ComplexField(prec) divisor *= 2 diam = min([(CF(r) - CF(r0)).abs() for r0 in roots[:i] + roots[i + 1:]]) / divisor envelop = IF(diam) * IF((-1, 1), (-1, 1)) qapr = QQ(CF(r).real()) + QQbar.gen() * QQ(CF(r).imag()) if qapr not in r + envelop: raise ValueError("Could not approximate roots with exact values") result[qapr] = r + envelop return result