diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst index b95ab97f839..2bc0d098cba 100644 --- a/src/doc/en/reference/curves/index.rst +++ b/src/doc/en/reference/curves/index.rst @@ -10,6 +10,7 @@ Curves sage/schemes/curves/projective_curve sage/schemes/curves/point sage/schemes/curves/closed_point + sage/schemes/curves/zariski_vankampen sage/schemes/jacobians/abstract_jacobian diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index 6a6efb98ad8..f1ba2d8940f 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -135,6 +135,7 @@ from sage.libs.gap.libgap import libgap from sage.libs.gap.element import GapElement from sage.misc.cachefunc import cached_method +from sage.groups.free_group import FreeGroup from sage.groups.free_group import FreeGroupElement from sage.functions.generalized import sign from sage.matrix.constructor import matrix @@ -1426,6 +1427,35 @@ def simplified(self): """ return self.simplification_isomorphism().codomain() + def sorted_presentation(self): + """ + Return the same presentation with the relations sorted to ensure equality. + + OUTPUT: + + A new finitely presented group with the relations sorted. + + EXAMPLES:: + + sage: G = FreeGroup(2) / [(1, 2, -1, -2), ()]; G + Finitely presented group < x0, x1 | x0*x1*x0^-1*x1^-1, 1 > + sage: G.sorted_presentation() + Finitely presented group < x0, x1 | 1, x1^-1*x0^-1*x1*x0 > + """ + F = FreeGroup(self.ngens()) + L0 = [r.Tietze() for r in self.relations()] + L1 = [] + for rel in L0: + C = [rel] + for j in range(len(rel) - 1): + C.append(rel[j + 1:] + rel[:j + 1]) + C1 = [tuple(-j for j in reversed(l)) for l in C] + C += C1 + C.sort() + L1.append(C[0]) + L1.sort() + return F/L1 + def epimorphisms(self, H): r""" Return the epimorphisms from `self` to `H`, up to automorphism of `H`. diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index f74dcc36d25..326c0d86e77 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.libs.singular r""" Affine curves @@ -27,49 +28,50 @@ EXAMPLES:: - sage: k. = GF(2) # optional - sage.rings.finite_rings - sage: A. = AffineSpace(k, 3) # optional - sage.rings.finite_rings - sage: C = Curve([x^2 + x - y^3, y^4 - y - z^3], A) # optional - sage.rings.finite_rings - sage: C.genus() # optional - sage.rings.finite_rings + sage: k. = GF(2) + sage: A. = AffineSpace(k, 3) + sage: C = Curve([x^2 + x - y^3, y^4 - y - z^3], A) + sage: C.genus() 10 - sage: C.function_field() # optional - sage.rings.finite_rings + sage: C.function_field() Function field in z defined by z^9 + x^8 + x^6 + x^5 + x^4 + x^3 + x Closed points of arbitrary degree can be computed:: - sage: C.closed_points() # long time # optional - sage.rings.finite_rings + sage: # long time + sage: C.closed_points() [Point (x, y, z), Point (x + 1, y, z)] - sage: C.closed_points(2) # long time # optional - sage.rings.finite_rings + sage: C.closed_points(2) [Point (x^2 + x + 1, y + 1, z), Point (y^2 + y + 1, x + y, z), Point (y^2 + y + 1, x + y + 1, z)] - sage: p = _[0] # long time # optional - sage.rings.finite_rings - sage: p.places() # long time # optional - sage.rings.finite_rings + sage: p = _[0] + sage: p.places() [Place (x^2 + x + 1, (1/(x^4 + x^2 + 1))*z^7 + (1/(x^4 + x^2 + 1))*z^6 + 1)] The places at infinity correspond to the extra closed points of the curve's projective closure:: - sage: C.places_at_infinity() # long time # optional - sage.rings.finite_rings + sage: C.places_at_infinity() # long time [Place (1/x, 1/x*z)] It is easy to transit to and from the function field of the curve:: - sage: fx = C(x) # optional - sage.rings.finite_rings - sage: fy = C(y) # optional - sage.rings.finite_rings - sage: fx^2 + fx - fy^3 # optional - sage.rings.finite_rings + sage: fx = C(x) + sage: fy = C(y) + sage: fx^2 + fx - fy^3 0 - sage: fx.divisor() # optional - sage.rings.finite_rings + sage: fx.divisor() -9*Place (1/x, 1/x*z) + 9*Place (x, z) - sage: p, = fx.zeros() # optional - sage.rings.finite_rings - sage: C.place_to_closed_point(p) # optional - sage.rings.finite_rings + sage: p, = fx.zeros() + sage: C.place_to_closed_point(p) Point (x, y, z) - sage: _.rational_point() # optional - sage.rings.finite_rings + sage: _.rational_point() (0, 0, 0) - sage: _.closed_point() # optional - sage.rings.finite_rings + sage: _.closed_point() Point (x, y, z) - sage: _.place() # optional - sage.rings.finite_rings + sage: _.place() Place (x, z) Integral affine curves over `\QQ` @@ -95,10 +97,10 @@ s sage: sy = sy.polynomial(10); sy -7/256*s^10 - 5/128*s^8 - 1/16*s^6 - 1/8*s^4 - 1/2*s^2 + 1 - sage: s = var('s') # optional - sage.symbolic - sage: P1 = parametric_plot([sx, sy], (s, -1, 1), color='red') # optional - sage.plot sage.symbolic - sage: P2 = C.plot((x, -1, 1), (y, 0, 2)) # half circle # optional - sage.plot sage.symbolic - sage: P1 + P2 # optional - sage.plot sage.symbolic + sage: s = var('s') # needs sage.symbolic + sage: P1 = parametric_plot([sx, sy], (s, -1, 1), color='red') # needs sage.plot sage.symbolic + sage: P2 = C.plot((x, -1, 1), (y, 0, 2)) # half circle # needs sage.plot sage.symbolic + sage: P1 + P2 # needs sage.plot sage.symbolic Graphics object consisting of 2 graphics primitives AUTHORS: @@ -168,18 +170,19 @@ class AffineCurve(Curve_generic, AlgebraicScheme_subscheme_affine): EXAMPLES:: sage: R. = QQ[] - sage: K. = NumberField(v^2 + 3) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 3) # optional - sage.rings.number_field - sage: C = Curve([z - u*x^2, y^2], A); C # optional - sage.rings.number_field + sage: K. = NumberField(v^2 + 3) # needs sage.rings.number_field + sage: A. = AffineSpace(K, 3) # needs sage.rings.number_field + sage: C = Curve([z - u*x^2, y^2], A); C # needs sage.rings.number_field Affine Curve over Number Field in u with defining polynomial v^2 + 3 defined by (-u)*x^2 + z, y^2 :: - sage: A. = AffineSpace(GF(7), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x^2 - z, z - 8*x], A); C # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(7), 3) + sage: C = Curve([x^2 - z, z - 8*x], A); C Affine Curve over Finite Field of size 7 defined by x^2 - z, -x + z """ + def __init__(self, A, X): r""" Initialize. @@ -187,16 +190,16 @@ def __init__(self, A, X): EXAMPLES:: sage: R. = QQ[] - sage: K. = NumberField(v^2 + 3) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 3) # optional - sage.rings.number_field - sage: C = Curve([z - u*x^2, y^2], A); C # optional - sage.rings.number_field + sage: K. = NumberField(v^2 + 3) # needs sage.rings.number_field + sage: A. = AffineSpace(K, 3) # needs sage.rings.number_field + sage: C = Curve([z - u*x^2, y^2], A); C # needs sage.rings.number_field Affine Curve over Number Field in u with defining polynomial v^2 + 3 defined by (-u)*x^2 + z, y^2 :: - sage: A. = AffineSpace(GF(7), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x^2 - z, z - 8*x], A); C # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(7), 3) + sage: C = Curve([x^2 - z, z - 8*x], A); C Affine Curve over Finite Field of size 7 defined by x^2 - z, -x + z """ if not is_AffineSpace(A): @@ -271,6 +274,7 @@ class AffinePlaneCurve(AffineCurve): """ Affine plane curves. """ + def __init__(self, A, f): r""" Initialize. @@ -322,18 +326,18 @@ def divisor_of_function(self, r): EXAMPLES:: - sage: F = GF(5) # optional - sage.rings.finite_rings - sage: P2 = AffineSpace(2, F, names='xy') # optional - sage.rings.finite_rings - sage: R = P2.coordinate_ring() # optional - sage.rings.finite_rings - sage: x, y = R.gens() # optional - sage.rings.finite_rings - sage: f = y^2 - x^9 - x # optional - sage.rings.finite_rings - sage: C = Curve(f) # optional - sage.rings.finite_rings - sage: K = FractionField(R) # optional - sage.rings.finite_rings - sage: r = 1/x # optional - sage.rings.finite_rings - sage: C.divisor_of_function(r) # todo: not implemented (broken) # optional - sage.rings.finite_rings + sage: F = GF(5) + sage: P2 = AffineSpace(2, F, names='xy') + sage: R = P2.coordinate_ring() + sage: x, y = R.gens() + sage: f = y^2 - x^9 - x + sage: C = Curve(f) + sage: K = FractionField(R) + sage: r = 1/x + sage: C.divisor_of_function(r) # not implemented (broken) [[-1, (0, 0, 1)]] - sage: r = 1/x^3 # optional - sage.rings.finite_rings - sage: C.divisor_of_function(r) # todo: not implemented (broken) # optional - sage.rings.finite_rings + sage: r = 1/x^3 + sage: C.divisor_of_function(r) # not implemented (broken) [[-3, (0, 0, 1)]] """ F = self.base_ring() @@ -376,13 +380,13 @@ def local_coordinates(self, pt, n): EXAMPLES:: - sage: F = GF(5) # optional - sage.rings.finite_rings - sage: pt = (2,3) # optional - sage.rings.finite_rings - sage: R = PolynomialRing(F, 2, names = ['x','y']) # optional - sage.rings.finite_rings - sage: x,y = R.gens() # optional - sage.rings.finite_rings - sage: f = y^2 - x^9 - x # optional - sage.rings.finite_rings - sage: C = Curve(f) # optional - sage.rings.finite_rings - sage: C.local_coordinates(pt, 9) # optional - sage.rings.finite_rings + sage: F = GF(5) + sage: pt = (2,3) + sage: R = PolynomialRing(F, 2, names = ['x','y']) + sage: x,y = R.gens() + sage: f = y^2 - x^9 - x + sage: C = Curve(f) + sage: C.local_coordinates(pt, 9) [t + 2, -2*t^12 - 2*t^11 + 2*t^9 + t^8 - 2*t^7 - 2*t^6 - 2*t^4 + t^3 - 2*t^2 - 2] """ f = self.defining_polynomial() @@ -447,26 +451,27 @@ def plot(self, *args, **kwds): sage: R. = QQ[] sage: C = Curve(x^3 - y^2) - sage: C.plot() + sage: C.plot() # needs sage.plot Graphics object consisting of 1 graphics primitive A 5-nodal curve of degree 11. This example also illustrates some of the optional arguments:: sage: R. = ZZ[] - sage: C = Curve(32*x^2 - 2097152*y^11 + 1441792*y^9 - 360448*y^7 + 39424*y^5 - 1760*y^3 + 22*y - 1) - sage: C.plot((x, -1, 1), (y, -1, 1), plot_points=400) + sage: C = Curve(32*x^2 - 2097152*y^11 + 1441792*y^9 + ....: - 360448*y^7 + 39424*y^5 - 1760*y^3 + 22*y - 1) + sage: C.plot((x, -1, 1), (y, -1, 1), plot_points=400) # needs sage.plot Graphics object consisting of 1 graphics primitive A line over `\mathbf{RR}`:: sage: R. = RR[] - sage: C = Curve(R(y - sqrt(2)*x)) - sage: C.plot() + sage: C = Curve(R(y - sqrt(2)*x)) # needs sage.symbolic + sage: C.plot() # needs sage.plot Graphics object consisting of 1 graphics primitive """ - I = self.defining_ideal() - return I.plot(*args, **kwds) + Id = self.defining_ideal() + return Id.plot(*args, **kwds) def is_transverse(self, C, P): r""" @@ -494,13 +499,14 @@ def is_transverse(self, C, P): :: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(a^3 + 2) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 2) # optional - sage.rings.number_field - sage: C = A.curve([x*y]) # optional - sage.rings.number_field - sage: D = A.curve([y - b*x]) # optional - sage.rings.number_field - sage: Q = A([0,0]) # optional - sage.rings.number_field - sage: C.is_transverse(D, Q) # optional - sage.rings.number_field + sage: K. = NumberField(a^3 + 2) + sage: A. = AffineSpace(K, 2) + sage: C = A.curve([x*y]) + sage: D = A.curve([y - b*x]) + sage: Q = A([0,0]) + sage: C.is_transverse(D, Q) False :: @@ -551,12 +557,13 @@ def multiplicity(self, P): :: - sage: A. = AffineSpace(QQbar,2) # optional - sage.rings.number_field - sage: C = Curve([-x^7 + (-7)*x^6 + y^6 + (-21)*x^5 + 12*y^5 # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: A. = AffineSpace(QQbar,2) + sage: C = Curve([-x^7 + (-7)*x^6 + y^6 + (-21)*x^5 + 12*y^5 ....: + (-35)*x^4 + 60*y^4 + (-35)*x^3 + 160*y^3 ....: + (-21)*x^2 + 240*y^2 + (-7)*x + 192*y + 63], A) - sage: Q = A([-1,-2]) # optional - sage.rings.number_field - sage: C.multiplicity(Q) # optional - sage.rings.number_field + sage: Q = A([-1,-2]) + sage: C.multiplicity(Q) 6 :: @@ -606,30 +613,32 @@ def tangents(self, P, factor=True): EXAMPLES:: + sage: # needs sage.rings.number_field sage: set_verbose(-1) - sage: A. = AffineSpace(QQbar, 2) # optional - sage.rings.number_field - sage: C = Curve([x^5*y^3 + 2*x^4*y^4 + x^3*y^5 + 3*x^4*y^3 # optional - sage.rings.number_field + sage: A. = AffineSpace(QQbar, 2) + sage: C = Curve([x^5*y^3 + 2*x^4*y^4 + x^3*y^5 + 3*x^4*y^3 ....: + 6*x^3*y^4 + 3*x^2*y^5 + 3*x^3*y^3 ....: + 6*x^2*y^4 + 3*x*y^5 + x^5 + 10*x^4*y ....: + 40*x^3*y^2 + 81*x^2*y^3 + 82*x*y^4 + 33*y^5], A) - sage: Q = A([0,0]) # optional - sage.rings.number_field - sage: C.tangents(Q) # optional - sage.rings.number_field + sage: Q = A([0,0]) + sage: C.tangents(Q) [x + 3.425299577684700?*y, x + (1.949159013086856? + 1.179307909383728?*I)*y, x + (1.949159013086856? - 1.179307909383728?*I)*y, x + (1.338191198070795? + 0.2560234251008043?*I)*y, x + (1.338191198070795? - 0.2560234251008043?*I)*y] - sage: C.tangents(Q, factor=False) # optional - sage.rings.number_field + sage: C.tangents(Q, factor=False) [120*x^5 + 1200*x^4*y + 4800*x^3*y^2 + 9720*x^2*y^3 + 9840*x*y^4 + 3960*y^5] :: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(a^2 - 3) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 2) # optional - sage.rings.number_field - sage: C = Curve([(x^2 + y^2 - 2*x)^2 - x^2 - y^2], A) # optional - sage.rings.number_field - sage: Q = A([0,0]) # optional - sage.rings.number_field - sage: C.tangents(Q) # optional - sage.rings.number_field + sage: K. = NumberField(a^2 - 3) + sage: A. = AffineSpace(K, 2) + sage: C = Curve([(x^2 + y^2 - 2*x)^2 - x^2 - y^2], A) + sage: Q = A([0,0]) + sage: C.tangents(Q) [x + (-1/3*b)*y, x + (1/3*b)*y] :: @@ -689,7 +698,7 @@ def tangents(self, P, factor=True): fact.extend([vars[1] - roots[i][0]*vars[0] for i in range(len(roots))]) return [ff(coords) for ff in fact] else: - return [l[0](coords) for l in T.factor()] + return [ll[0](coords) for ll in T.factor()] def is_ordinary_singularity(self, P): r""" @@ -720,12 +729,13 @@ def is_ordinary_singularity(self, P): :: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(a^2 - 3) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 2) # optional - sage.rings.number_field - sage: C = Curve([(x^2 + y^2 - 2*x)^2 - x^2 - y^2], A) # optional - sage.rings.number_field - sage: Q = A([0,0]) # optional - sage.rings.number_field - sage: C.is_ordinary_singularity(Q) # optional - sage.rings.number_field + sage: K. = NumberField(a^2 - 3) + sage: A. = AffineSpace(K, 2) + sage: C = Curve([(x^2 + y^2 - 2*x)^2 - x^2 - y^2], A) + sage: Q = A([0,0]) + sage: C.is_ordinary_singularity(Q) True :: @@ -829,16 +839,16 @@ def __init__(self, A, X): EXAMPLES:: sage: R. = QQ[] - sage: K. = NumberField(v^2 + 3) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 3) # optional - sage.rings.number_field - sage: C = Curve([z - u*x^2, y^2], A); C # optional - sage.rings.number_field + sage: K. = NumberField(v^2 + 3) # needs sage.rings.number_field + sage: A. = AffineSpace(K, 3) # needs sage.rings.number_field + sage: C = Curve([z - u*x^2, y^2], A); C # needs sage.rings.number_field Affine Curve over Number Field in u with defining polynomial v^2 + 3 defined by (-u)*x^2 + z, y^2 :: - sage: A. = AffineSpace(GF(7), 3) # optional - sage.rings.number_field - sage: C = Curve([x^2 - z, z - 8*x], A); C # optional - sage.rings.number_field + sage: A. = AffineSpace(GF(7), 3) + sage: C = Curve([x^2 - z, z - 8*x], A); C Affine Curve over Finite Field of size 7 defined by x^2 - z, -x + z """ super().__init__(A, X) @@ -912,12 +922,11 @@ def projection(self, indices, AS=None): :: - sage: A. = AffineSpace(GF(11), 5) # optional - sage.rings.finite_rings - sage: C = Curve([x^3 - 5*y*z + u^2, x - y^2 + 3*z^2, # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 5) + sage: C = Curve([x^3 - 5*y*z + u^2, x - y^2 + 3*z^2, ....: w^2 + 2*u^3*y, y - u^2 + z*x], A) - sage: B. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: proj1 = C.projection([1,2,4], AS=B) # optional - sage.rings.finite_rings - sage: proj1 # optional - sage.rings.finite_rings + sage: B. = AffineSpace(GF(11), 3) + sage: proj1 = C.projection([1,2,4], AS=B); proj1 (Scheme morphism: From: Affine Curve over Finite Field of size 11 defined by x^3 - 5*y*z + u^2, -y^2 + 3*z^2 + x, 2*y*u^3 + w^2, x*z - u^2 + y @@ -930,12 +939,12 @@ def projection(self, indices, AS=None): 3*b*c^2 + 3*a*b, a^4*c^2 + 2*b^4*c^2 - a^5 - 2*a*b^4 + 5*b*c^4 + a*b*c^2 - 5*a*b^2 + 4*b^3 + b*c^2 + 5*c^2 - 5*a, a^6 - 5*b^6 - 5*b^3*c^2 + 5*a*b^3 + 2*c^4 - 4*a*c^2 + 2*a^2 - 5*a*b + c^2) - sage: proj1[1].ambient_space() is B # optional - sage.rings.finite_rings + sage: proj1[1].ambient_space() is B True - sage: proj2 = C.projection([1,2,4]) # optional - sage.rings.finite_rings - sage: proj2[1].ambient_space() is B # optional - sage.rings.finite_rings + sage: proj2 = C.projection([1,2,4]) + sage: proj2[1].ambient_space() is B False - sage: C.projection([1,2,3,5], AS=B) # optional - sage.rings.finite_rings + sage: C.projection([1,2,3,5], AS=B) Traceback (most recent call last): ... TypeError: (=Affine Space of dimension 3 over Finite Field of size 11) @@ -1009,10 +1018,10 @@ def projection(self, indices, AS=None): removecoords.pop(indices[i]) J = self.defining_ideal().elimination_ideal(removecoords) K = Hom(AA.coordinate_ring(), AA2.coordinate_ring()) - l = [0]*(n) + ll = [0]*(n) for i in range(len(indices)): - l[indices[i]] = AA2.gens()[i] - phi = K(l) + ll[indices[i]] = AA2.gens()[i] + phi = K(ll) G = [phi(f) for f in J.gens()] try: C = AA2.curve(G) @@ -1053,13 +1062,14 @@ def plane_projection(self, AP=None): :: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(a^2 + 2) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 3) # optional - sage.rings.number_field - sage: C = A.curve([x - b, y - 2]) # optional - sage.rings.number_field - sage: B. = AffineSpace(K, 2) # optional - sage.rings.number_field - sage: proj1 = C.plane_projection(AP=B) # optional - sage.rings.number_field - sage: proj1 # optional - sage.rings.number_field + sage: K. = NumberField(a^2 + 2) + sage: A. = AffineSpace(K, 3) + sage: C = A.curve([x - b, y - 2]) + sage: B. = AffineSpace(K, 2) + sage: proj1 = C.plane_projection(AP=B) + sage: proj1 (Scheme morphism: From: Affine Curve over Number Field in b with defining polynomial a^2 + 2 defined by x + (-b), y - 2 @@ -1069,10 +1079,10 @@ def plane_projection(self, AP=None): (x, z), Affine Plane Curve over Number Field in b with defining polynomial a^2 + 2 defined by a + (-b)) - sage: proj1[1].ambient_space() is B # optional - sage.rings.number_field + sage: proj1[1].ambient_space() is B True - sage: proj2 = C.plane_projection() # optional - sage.rings.number_field - sage: proj2[1].ambient_space() is B # optional - sage.rings.number_field + sage: proj2 = C.plane_projection() + sage: proj2[1].ambient_space() is B False """ n = self.ambient_space().dimension_relative() @@ -1141,18 +1151,19 @@ def blowup(self, P=None): :: - sage: K. = QuadraticField(2) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 3) # optional - sage.rings.number_field - sage: C = Curve([y^2 - a*x^5, x - z], A) # optional - sage.rings.number_field - sage: B = C.blowup() # optional - sage.rings.number_field - sage: B[0] # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: K. = QuadraticField(2) + sage: A. = AffineSpace(K, 3) + sage: C = Curve([y^2 - a*x^5, x - z], A) + sage: B = C.blowup() + sage: B[0] (Affine Curve over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? defined by s2 - 1, 2*x^3 + (-a)*s1^2, Affine Curve over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? defined by s0 - s2, 2*y^3*s2^5 + (-a), Affine Curve over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? defined by s0 - 1, 2*z^3 + (-a)*s1^2) - sage: B[1][0][2] # optional - sage.rings.number_field + sage: B[1][0][2] Scheme morphism: From: Affine Curve over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? @@ -1162,7 +1173,7 @@ def blowup(self, P=None): defined by s0 - 1, 2*z^3 + (-a)*s1^2 Defn: Defined on coordinates by sending (x, s1, s2) to (x*s2, 1/s2, s1/s2) - sage: B[1][2][0] # optional - sage.rings.number_field + sage: B[1][2][0] Scheme morphism: From: Affine Curve over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? @@ -1172,7 +1183,7 @@ def blowup(self, P=None): defined by s2 - 1, 2*x^3 + (-a)*s1^2 Defn: Defined on coordinates by sending (z, s0, s1) to (z*s0, s1/s0, 1/s0) - sage: B[2] # optional - sage.rings.number_field + sage: B[2] (Scheme morphism: From: Affine Curve over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? @@ -1277,9 +1288,9 @@ def blowup(self, P=None): :: - sage: A. = AffineSpace(QuadraticField(-1), 2) # optional - sage.rings.number_field - sage: C = A.curve([y^2 + x^2]) # optional - sage.rings.number_field - sage: C.blowup() # optional - sage.rings.number_field + sage: A. = AffineSpace(QuadraticField(-1), 2) # needs sage.rings.number_field + sage: C = A.curve([y^2 + x^2]) # needs sage.rings.number_field + sage: C.blowup() # needs sage.rings.number_field Traceback (most recent call last): ... TypeError: this curve must be irreducible @@ -1451,11 +1462,12 @@ def resolution_of_singularities(self, extend=False): :: + sage: # needs sage.rings.number_field sage: set_verbose(-1) - sage: K. = QuadraticField(3) # optional - sage.rings.number_field - sage: A. = AffineSpace(K, 2) # optional - sage.rings.number_field - sage: C = A.curve(x^4 + 2*x^2 + a*y^3 + 1) # optional - sage.rings.number_field - sage: C.resolution_of_singularities(extend=True)[0] # long time (2 s) # optional - sage.rings.number_field + sage: K. = QuadraticField(3) + sage: A. = AffineSpace(K, 2) + sage: C = A.curve(x^4 + 2*x^2 + a*y^3 + 1) + sage: C.resolution_of_singularities(extend=True)[0] # long time (2 s) (Affine Plane Curve over Number Field in a0 with defining polynomial y^4 - 4*y^2 + 16 defined by 24*x^2*ss1^3 + 24*ss1^3 + (a0^3 - 8*a0), @@ -1468,10 +1480,10 @@ def resolution_of_singularities(self, extend=False): :: - sage: A. = AffineSpace(GF(5), 3) # optional - sage.rings.finite_rings - sage: C = Curve([y - x^3, (z - 2)^2 - y^3 - x^3], A) # optional - sage.rings.finite_rings - sage: R = C.resolution_of_singularities() # optional - sage.rings.finite_rings - sage: R[0] # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(5), 3) + sage: C = Curve([y - x^3, (z - 2)^2 - y^3 - x^3], A) + sage: R = C.resolution_of_singularities() + sage: R[0] (Affine Curve over Finite Field of size 5 defined by x^2 - s1, s1^4 - x*s2^2 + s1, x*s1^3 - s2^2 + x, Affine Curve over Finite Field of size 5 @@ -1519,6 +1531,7 @@ def resolution_of_singularities(self, extend=False): working over a number field use extend=True """ # helper function for extending the base field (in the case of working over a number field) + def extension(self): F = self.base_ring() pts = self.change_ring(F.embeddings(QQbar)[0]).rational_points() @@ -1720,11 +1733,11 @@ def tangent_line(self, p): from sage.schemes.curves.constructor import Curve # translate to p - I = [] + I0 = [] for poly in Tp.defining_polynomials(): - I.append(poly.subs({x: x - c for x, c in zip(gens, p)})) + I0.append(poly.subs({x: x - c for x, c in zip(gens, p)})) - return Curve(I, A) + return Curve(I0, A) class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): @@ -1733,11 +1746,32 @@ class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): """ _point = AffinePlaneCurvePoint_field - def fundamental_group(self): + @cached_method + def fundamental_group(self, simplified=True, puiseux=False): r""" Return a presentation of the fundamental group of the complement of ``self``. + INPUT: + + - ``simplified`` -- (default: ``True``) boolean to simplify the presentation. + + - ``puiseux`` -- (default: ``False``) boolean to decide if the + presentation is constructed in the classical way or using Puiseux + shortcut. If ``True``, ``simplified`` is set to ``False``. + + OUTPUT: + + A presentation with generators `x_1, \dots, x_d` and relations. If ``puiseux`` + is ``False`` the relations are `(x_j\cdot \tau)\cdot x_j^{-1}` for `1\leq j = AffineSpace(QQ, 2) sage: C = A.curve(y^2 - x^3 - x^2) - sage: C.fundamental_group() # optional - sirocco + sage: C.fundamental_group() + Finitely presented group < x0 | > + sage: bm = C.braid_monodromy() + sage: g = C.fundamental_group(puiseux=True) + sage: g.sorted_presentation() + Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0, x1^-1*x0 > + sage: g.simplified() Finitely presented group < x0 | > In the case of number fields, they need to have an embedding to the algebraic field:: - sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] # optional - sage.rings.number_field - sage: F = NumberField(a.minpoly(), 'a', embedding=a) # optional - sage.rings.number_field - sage: F.inject_variables() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] + sage: F = NumberField(a.minpoly(), 'a', embedding=a) + sage: F.inject_variables() Defining a - sage: A. = AffineSpace(F, 2) # optional - sage.rings.number_field - sage: C = A.curve(y^2 - a*x^3 - x^2) # optional - sage.rings.number_field - sage: C.fundamental_group() # optional - sirocco # optional - sage.rings.number_field + sage: A. = AffineSpace(F, 2) + sage: C = A.curve(y^2 - a*x^3 - x^2) + sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | > .. WARNING:: This functionality requires the sirocco package to be installed. """ - from sage.schemes.curves.zariski_vankampen import fundamental_group - F = self.base_ring() - from sage.rings.qqbar import QQbar - if QQbar.coerce_map_from(F) is None: - raise NotImplementedError("the base field must have an embedding" - " to the algebraic field") - f = self.defining_polynomial() - return fundamental_group(f, projective=False) + from sage.schemes.curves.zariski_vankampen import fundamental_group_from_braid_mon + return fundamental_group_from_braid_mon(self.braid_monodromy(), simplified=simplified, puiseux=puiseux) + @cached_method def braid_monodromy(self): r""" Compute the braid monodromy of a projection of the curve. @@ -1783,7 +1820,7 @@ def braid_monodromy(self): 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 `self`. + in the discriminant of the projection of ``self``. NOTE: @@ -1808,7 +1845,7 @@ def braid_monodromy(self): raise NotImplementedError("the base field must have an embedding" " to the algebraic field") f = self.defining_polynomial() - return braid_monodromy(f) + return braid_monodromy(f)[0] def riemann_surface(self, **kwargs): r""" @@ -1859,12 +1896,12 @@ def riemann_roch_basis(self, D): EXAMPLES:: - sage: R = PolynomialRing(GF(5), 2, names=["x","y"]) # optional - sage.rings.finite_rings - sage: x, y = R.gens() # optional - sage.rings.finite_rings - sage: f = y^2 - x^9 - x # optional - sage.rings.finite_rings - sage: C = Curve(f) # optional - sage.rings.finite_rings - sage: D = [6,0,0,0,0,0] # optional - sage.rings.finite_rings - sage: C.riemann_roch_basis(D) # optional - sage.rings.finite_rings + sage: R = PolynomialRing(GF(5), 2, names=["x","y"]) + sage: x, y = R.gens() + sage: f = y^2 - x^9 - x + sage: C = Curve(f) + sage: D = [6,0,0,0,0,0] + sage: C.riemann_roch_basis(D) [1, (-x*z^5 + y^2*z^4)/x^6, (-x*z^6 + y^2*z^5)/x^7, (-x*z^7 + y^2*z^6)/x^8] """ F = self.base_ring() @@ -1910,33 +1947,33 @@ def rational_points(self, algorithm="enum"): EXAMPLES:: - sage: x, y = (GF(5)['x,y']).gens() # optional - sage.rings.finite_rings - sage: f = y^2 - x^9 - x # optional - sage.rings.finite_rings - sage: C = Curve(f); C # optional - sage.rings.finite_rings + sage: x, y = (GF(5)['x,y']).gens() + sage: f = y^2 - x^9 - x + sage: C = Curve(f); C Affine Plane Curve over Finite Field of size 5 defined by -x^9 + y^2 - x - sage: C.rational_points(algorithm='bn') # optional - sage.rings.finite_rings + sage: C.rational_points(algorithm='bn') [(0, 0), (2, 2), (2, 3), (3, 1), (3, 4)] - sage: C = Curve(x - y + 1) # optional - sage.rings.finite_rings - sage: C.rational_points() # optional - sage.rings.finite_rings + sage: C = Curve(x - y + 1) + sage: C.rational_points() [(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)] We compare Brill-Noether and enumeration:: - sage: x, y = (GF(17)['x,y']).gens() # optional - sage.rings.finite_rings - sage: C = Curve(x^2 + y^5 + x*y - 19) # optional - sage.rings.finite_rings - sage: v = C.rational_points(algorithm='bn') # optional - sage.rings.finite_rings - sage: w = C.rational_points(algorithm='enum') # optional - sage.rings.finite_rings - sage: len(v) # optional - sage.rings.finite_rings + sage: x, y = (GF(17)['x,y']).gens() + sage: C = Curve(x^2 + y^5 + x*y - 19) + sage: v = C.rational_points(algorithm='bn') + sage: w = C.rational_points(algorithm='enum') + sage: len(v) 20 - sage: v == w # optional - sage.rings.finite_rings + sage: v == w True - sage: A. = AffineSpace(2, GF(9,'a')) # optional - sage.rings.finite_rings - sage: C = Curve(x^2 + y^2 - 1) # optional - sage.rings.finite_rings - sage: C # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(2, GF(9,'a')) + sage: C = Curve(x^2 + y^2 - 1); C Affine Plane Curve over Finite Field in a of size 3^2 defined by x^2 + y^2 - 1 - sage: C.rational_points() # optional - sage.rings.finite_rings + sage: C.rational_points() [(0, 1), (0, 2), (1, 0), (2, 0), (a + 1, a + 1), (a + 1, 2*a + 2), (2*a + 2, a + 1), (2*a + 2, 2*a + 2)] """ @@ -2003,9 +2040,9 @@ def function_field(self): :: - sage: A. = AffineSpace(GF(8), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1) # optional - sage.rings.finite_rings - sage: C.function_field() # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(8), 2) # needs sage.rings.finite_rings + sage: C = Curve(x^5 + y^5 + x*y + 1) # needs sage.rings.finite_rings + sage: C.function_field() # needs sage.rings.finite_rings Function field in y defined by y^5 + x*y + x^5 + 1 """ return self._function_field @@ -2017,9 +2054,9 @@ def _genus(self): EXAMPLES:: - sage: A. = AffineSpace(GF(2), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1) # optional - sage.rings.finite_rings - sage: C.genus() # indirect doctest # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(2), 2) + sage: C = Curve(x^5 + y^5 + x*y + 1) + sage: C.genus() # indirect doctest 1 """ k = self.base_ring() @@ -2039,25 +2076,26 @@ def __call__(self, *args): EXAMPLES:: - sage: A. = AffineSpace(GF(8), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1) # optional - sage.rings.finite_rings - sage: C(1,1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(8), 2) + sage: C = Curve(x^5 + y^5 + x*y + 1) + sage: C(1,1) (1, 1) - sage: C(x/y) # optional - sage.rings.finite_rings + sage: C(x/y) (x/(x^5 + 1))*y^4 + x^2/(x^5 + 1) - sage: C(GF(8^2)) # optional - sage.rings.finite_rings + sage: C(GF(8^2)) Set of rational points of Closed subscheme of Affine Space of dimension 2 over Finite Field in z6 of size 2^6 defined by: x^5 + y^5 + x*y + 1 :: - sage: A. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) # optional - sage.rings.finite_rings - sage: C([0,0,0]) # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) + sage: C([0,0,0]) (0, 0, 0) - sage: C(y) # optional - sage.rings.finite_rings + sage: C(y) z^2 - sage: C(A.coordinate_ring()(y)) # optional - sage.rings.finite_rings + sage: C(A.coordinate_ring()(y)) z^2 """ try: @@ -2079,14 +2117,15 @@ def function(self, f): EXAMPLES:: - sage: A. = AffineSpace(GF(8), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1) # optional - sage.rings.finite_rings - sage: f = C.function(x/y) # optional - sage.rings.finite_rings - sage: f # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(8), 2) + sage: C = Curve(x^5 + y^5 + x*y + 1) + sage: f = C.function(x/y) + sage: f (x/(x^5 + 1))*y^4 + x^2/(x^5 + 1) - sage: df = f.differential(); df # optional - sage.rings.finite_rings + sage: df = f.differential(); df ((1/(x^10 + 1))*y^4 + x^6/(x^10 + 1)) d(x) - sage: df.divisor() # long time # optional - sage.rings.finite_rings + sage: df.divisor() # long time 2*Place (1/x, 1/x^4*y^4 + 1/x^3*y^3 + 1/x^2*y^2 + 1/x*y + 1) + 2*Place (1/x, 1/x*y + 1) - 2*Place (x + 1, y) @@ -2107,10 +2146,11 @@ def coordinate_functions(self): EXAMPLES:: - sage: A. = AffineSpace(GF(8), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1) # optional - sage.rings.finite_rings - sage: x, y = C.coordinate_functions() # optional - sage.rings.finite_rings - sage: x^5 + y^5 + x*y + 1 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(8), 2) + sage: C = Curve(x^5 + y^5 + x*y + 1) + sage: x, y = C.coordinate_functions() + sage: x^5 + y^5 + x*y + 1 0 """ return self._coordinate_functions @@ -2127,9 +2167,9 @@ def _nonsingular_model(self): TESTS:: - sage: A. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) # optional - sage.rings.finite_rings - sage: C._nonsingular_model # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) + sage: C._nonsingular_model (Function field in z defined by z^3 + 10*x, Ring morphism: From: Multivariate Polynomial Ring in x, y, z @@ -2142,18 +2182,18 @@ def _nonsingular_model(self): from sage.rings.function_field.constructor import FunctionField k = self.base_ring() - I = self.defining_ideal() + I0 = self.defining_ideal() # invlex is the lex order with x < y < z for R = k[x,y,z] for instance - R = I.parent().ring().change_ring(order='invlex') - I = I.change_ring(R) + R = I0.parent().ring().change_ring(order='invlex') + I0 = I0.change_ring(R) n = R.ngens() names = R.variable_names() - gbasis = I.groebner_basis() + gbasis = I0.groebner_basis() - if not I.is_prime(): + if not I0.is_prime(): raise TypeError("the curve is not integral") # Suppose the generators of the defining ideal I of the curve is @@ -2223,7 +2263,7 @@ def _nonsingular_model(self): lift_to_function_field = hom(R, M, coordinate_functions) # sanity check - assert all(lift_to_function_field(f).is_zero() for f in I.gens()) + assert all(lift_to_function_field(f).is_zero() for f in I0.gens()) return M, lift_to_function_field @@ -2234,9 +2274,9 @@ def _function_field(self): TESTS:: - sage: A. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) # optional - sage.rings.finite_rings - sage: C._function_field # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) + sage: C._function_field Function field in z defined by z^3 + 10*x """ return self._nonsingular_model[0] @@ -2248,9 +2288,9 @@ def _lift_to_function_field(self): TESTS:: - sage: A. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) # optional - sage.rings.finite_rings - sage: C._lift_to_function_field # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) + sage: C._lift_to_function_field Ring morphism: From: Multivariate Polynomial Ring in x, y, z over Finite Field of size 11 @@ -2268,9 +2308,9 @@ def _coordinate_functions(self): TESTS:: - sage: A. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) # optional - sage.rings.finite_rings - sage: C._coordinate_functions # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) + sage: C._coordinate_functions [x, z^2, z] """ return self._nonsingular_model[1].im_gens() @@ -2282,9 +2322,9 @@ def _singularities(self): TESTS:: - sage: A. = AffineSpace(GF(7^2), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^2 - x^4 - y^4) # optional - sage.rings.finite_rings - sage: C._singularities # long time # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(7^2), 2) # needs sage.rings.finite_rings + sage: C = Curve(x^2 - x^4 - y^4) # needs sage.rings.finite_rings + sage: C._singularities # long time # needs sage.rings.finite_rings [(Point (x, y), [Place (x, 1/x*y^3 + 1/x*y^2 + 1), Place (x, 1/x*y^3 + 1/x*y^2 + 6)])] """ @@ -2324,16 +2364,16 @@ def singular_closed_points(self): EXAMPLES:: - sage: A. = AffineSpace(GF(7^2), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^2 - x^4 - y^4) # optional - sage.rings.finite_rings - sage: C.singular_closed_points() # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(7^2), 2) # needs sage.rings.finite_rings + sage: C = Curve(x^2 - x^4 - y^4) # needs sage.rings.finite_rings + sage: C.singular_closed_points() # needs sage.rings.finite_rings [Point (x, y)] :: - sage: A. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) # optional - sage.rings.finite_rings - sage: C.singular_closed_points() # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) + sage: C.singular_closed_points() [] """ return [p for p, _ in self._singularities] @@ -2349,13 +2389,14 @@ def place_to_closed_point(self, place): EXAMPLES:: - sage: A. = AffineSpace(GF(4), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1) # optional - sage.rings.finite_rings - sage: F = C.function_field() # optional - sage.rings.finite_rings - sage: pls = F.places(1) # optional - sage.rings.finite_rings - sage: C.place_to_closed_point(pls[-1]) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(4), 2) + sage: C = Curve(x^5 + y^5 + x*y + 1) + sage: F = C.function_field() + sage: pls = F.places(1) + sage: C.place_to_closed_point(pls[-1]) Point (x + 1, y + 1) - sage: C.place_to_closed_point(pls[-2]) # optional - sage.rings.finite_rings + sage: C.place_to_closed_point(pls[-2]) Point (x + 1, y + 1) """ F = self.function_field() @@ -2433,17 +2474,18 @@ def places_at_infinity(self): :: - sage: F = GF(9) # optional - sage.rings.finite_rings - sage: A2. = AffineSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = A2.curve(y^3 + y - x^4) # optional - sage.rings.finite_rings - sage: C.places_at_infinity() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F = GF(9) + sage: A2. = AffineSpace(F, 2) + sage: C = A2.curve(y^3 + y - x^4) + sage: C.places_at_infinity() [Place (1/x, 1/x^3*y^2)] :: - sage: A. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) # optional - sage.rings.finite_rings - sage: C.places_at_infinity() # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) + sage: C.places_at_infinity() [Place (1/x, 1/x*z^2)] """ return list(set(p for f in self._coordinate_functions if f for p in f.poles())) @@ -2470,11 +2512,12 @@ def places_on(self, point): :: - sage: k. = GF(9) # optional - sage.rings.finite_rings - sage: A. = AffineSpace(k, 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) # optional - sage.rings.finite_rings - sage: pts = C.closed_points() # optional - sage.rings.finite_rings - sage: pts # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(9) + sage: A. = AffineSpace(k, 2) + sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) + sage: pts = C.closed_points() + sage: pts [Point (x, y + (a + 1)), Point (x, y + (-a - 1)), Point (x + (a + 1), y + (a - 1)), @@ -2485,20 +2528,21 @@ def places_on(self, point): Point (x + (-a - 1), y + (-a)), Point (x + 1, y + 1), Point (x + 1, y - 1)] - sage: p1, p2, p3 = pts[:3] # optional - sage.rings.finite_rings - sage: C.places_on(p1) # optional - sage.rings.finite_rings + sage: p1, p2, p3 = pts[:3] + sage: C.places_on(p1) [Place (x, y + a + 1)] - sage: C.places_on(p2) # optional - sage.rings.finite_rings + sage: C.places_on(p2) [Place (x, y + 2*a + 2)] - sage: C.places_on(p3) # optional - sage.rings.finite_rings + sage: C.places_on(p3) [Place (x + a + 1, y + a + 2)] :: - sage: F. = GF(8) # optional - sage.rings.finite_rings - sage: P. = ProjectiveSpace(F, 2) # optional - sage.rings.finite_rings - sage: Cp = Curve(x^3*y + y^3*z + x*z^3) # optional - sage.rings.finite_rings - sage: C = Cp.affine_patch(0) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = GF(8) + sage: P. = ProjectiveSpace(F, 2) + sage: Cp = Curve(x^3*y + y^3*z + x*z^3) + sage: C = Cp.affine_patch(0) """ phi = self._lift_to_function_field gs = [phi(g) for g in point.prime_ideal().gens()] @@ -2532,17 +2576,18 @@ def parametric_representation(self, place, name=None): :: - sage: A. = AffineSpace(GF(7^2), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^2 - x^4 - y^4) # optional - sage.rings.finite_rings - sage: p, = C.singular_closed_points() # optional - sage.rings.finite_rings - sage: b1, b2 = p.places() # optional - sage.rings.finite_rings - sage: xs, ys = C.parametric_representation(b1) # optional - sage.rings.finite_rings - sage: f = xs^2 - xs^4 - ys^4 # optional - sage.rings.finite_rings - sage: [f.coefficient(i) for i in range(5)] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(7^2), 2) + sage: C = Curve(x^2 - x^4 - y^4) + sage: p, = C.singular_closed_points() + sage: b1, b2 = p.places() + sage: xs, ys = C.parametric_representation(b1) + sage: f = xs^2 - xs^4 - ys^4 + sage: [f.coefficient(i) for i in range(5)] [0, 0, 0, 0, 0] - sage: xs, ys = C.parametric_representation(b2) # optional - sage.rings.finite_rings - sage: f = xs^2 - xs^4 - ys^4 # optional - sage.rings.finite_rings - sage: [f.coefficient(i) for i in range(5)] # optional - sage.rings.finite_rings + sage: xs, ys = C.parametric_representation(b2) + sage: f = xs^2 - xs^4 - ys^4 + sage: [f.coefficient(i) for i in range(5)] [0, 0, 0, 0, 0] """ F = place.function_field() @@ -2563,11 +2608,11 @@ class IntegralAffineCurve_finite_field(IntegralAffineCurve): EXAMPLES:: - sage: A. = AffineSpace(GF(11), 3) # optional - sage.rings.finite_rings - sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A); C # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A); C Affine Curve over Finite Field of size 11 defined by -y^2 + x*z, -z^2 + y, -y*z + x - sage: C.function_field() # optional - sage.rings.finite_rings + sage: C.function_field() Function field in z defined by z^3 + 10*x """ _point = IntegralAffineCurvePoint_finite_field @@ -2582,10 +2627,11 @@ def places(self, degree=1): EXAMPLES:: - sage: F = GF(9) # optional - sage.rings.finite_rings - sage: A2. = AffineSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = A2.curve(y^3 + y - x^4) # optional - sage.rings.finite_rings - sage: C.places() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F = GF(9) + sage: A2. = AffineSpace(F, 2) + sage: C = A2.curve(y^3 + y - x^4) + sage: C.places() [Place (1/x, 1/x^3*y^2), Place (x, y), Place (x, y + z2 + 1), @@ -2629,9 +2675,9 @@ def closed_points(self, degree=1): EXAMPLES:: - sage: A. = AffineSpace(GF(7), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^2 - x^4 - y^4) # optional - sage.rings.finite_rings - sage: C.closed_points() # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(7), 2) + sage: C = Curve(x^2 - x^4 - y^4) + sage: C.closed_points() [Point (x, y), Point (x + 1, y), Point (x + 2, y + 2), @@ -2674,11 +2720,11 @@ class IntegralAffinePlaneCurve_finite_field(AffinePlaneCurve_finite_field, Integ EXAMPLES:: - sage: A. = AffineSpace(GF(8), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1); C # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(8), 2) # needs sage.rings.finite_rings + sage: C = Curve(x^5 + y^5 + x*y + 1); C # needs sage.rings.finite_rings Affine Plane Curve over Finite Field in z3 of size 2^3 defined by x^5 + y^5 + x*y + 1 - sage: C.function_field() # optional - sage.rings.finite_rings + sage: C.function_field() # needs sage.rings.finite_rings Function field in y defined by y^5 + x*y + x^5 + 1 """ _point = IntegralAffinePlaneCurvePoint_finite_field diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index b27ab071863..958a402da19 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.libs.singular r""" Projective curves @@ -27,54 +28,54 @@ EXAMPLES:: - sage: k = GF(2) # optional - sage.rings.finite_rings - sage: P. = ProjectiveSpace(k, 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^2*z - y^3, P) # optional - sage.rings.finite_rings - sage: C.genus() # optional - sage.rings.finite_rings + sage: k = GF(2) + sage: P. = ProjectiveSpace(k, 2) + sage: C = Curve(x^2*z - y^3, P) + sage: C.genus() 0 - sage: C.function_field() # optional - sage.rings.finite_rings + sage: C.function_field() Function field in z defined by z + y^3 Closed points of arbitrary degree can be computed:: - sage: C.closed_points() # optional - sage.rings.finite_rings + sage: C.closed_points() [Point (x, y), Point (y, z), Point (x + z, y + z)] - sage: C.closed_points(2) # optional - sage.rings.finite_rings + sage: C.closed_points(2) [Point (y^2 + y*z + z^2, x + z)] - sage: C.closed_points(3) # optional - sage.rings.finite_rings + sage: C.closed_points(3) [Point (y^3 + y^2*z + z^3, x + y + z), Point (x^2 + y*z + z^2, x*y + x*z + y*z, y^2 + x*z + y*z + z^2)] All singular closed points can be found:: - sage: C.singular_closed_points() # optional - sage.rings.finite_rings + sage: C.singular_closed_points() [Point (x, y)] - sage: p = _[0] # optional - sage.rings.finite_rings - sage: p.places() # a unibranch singularity, that is, a cusp # optional - sage.rings.finite_rings + sage: p = _[0] + sage: p.places() # a unibranch singularity, that is, a cusp [Place (1/y)] - sage: pls = _[0] # optional - sage.rings.finite_rings - sage: C.place_to_closed_point(pls) # optional - sage.rings.finite_rings + sage: pls = _[0] + sage: C.place_to_closed_point(pls) Point (x, y) It is easy to transit to and from the function field of the curve:: - sage: fx = C(x/z) # optional - sage.rings.finite_rings - sage: fy = C(y/z) # optional - sage.rings.finite_rings - sage: fx^2 - fy^3 # optional - sage.rings.finite_rings + sage: fx = C(x/z) + sage: fy = C(y/z) + sage: fx^2 - fy^3 0 - sage: fx.divisor() # optional - sage.rings.finite_rings + sage: fx.divisor() 3*Place (1/y) - 3*Place (y) - sage: p, = fx.poles() # optional - sage.rings.finite_rings - sage: p # optional - sage.rings.finite_rings + sage: p, = fx.poles() + sage: p Place (y) - sage: C.place_to_closed_point(p) # optional - sage.rings.finite_rings + sage: C.place_to_closed_point(p) Point (y, z) - sage: _.rational_point() # optional - sage.rings.finite_rings + sage: _.rational_point() (1 : 0 : 0) - sage: _.closed_point() # optional - sage.rings.finite_rings + sage: _.closed_point() Point (y, z) - sage: _.place() # optional - sage.rings.finite_rings + sage: _.place() Place (y) Integral projective curves over `\QQ` @@ -188,19 +189,21 @@ class ProjectiveCurve(Curve_generic, AlgebraicScheme_subscheme_projective): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(7), 4) # optional - sage.rings.finite_rings - sage: C = Curve([y*u^2 - x^3, z*u^2 - x^3, w*u^2 - x^3, y^3 - x^3], P); C # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(7), 4) + sage: C = Curve([y*u^2 - x^3, z*u^2 - x^3, w*u^2 - x^3, y^3 - x^3], P); C Projective Curve over Finite Field of size 7 defined by -x^3 + y*u^2, -x^3 + z*u^2, -x^3 + w*u^2, -x^3 + y^3 :: - sage: K. = CyclotomicField(11) # optional - sage.rings.number_field - sage: P. = ProjectiveSpace(K, 3) # optional - sage.rings.number_field - sage: C = Curve([y*w - u*z^2 - x^2, x*w - 3*u^2*z*w], P); C # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: K. = CyclotomicField(11) + sage: P. = ProjectiveSpace(K, 3) + sage: C = Curve([y*w - u*z^2 - x^2, x*w - 3*u^2*z*w], P); C Projective Curve over Cyclotomic Field of order 11 and degree 10 defined by -x^2 + (-u)*z^2 + y*w, x*w + (-3*u^2)*z*w """ + def __init__(self, A, X): """ Initialize. @@ -299,12 +302,13 @@ def projection(self, P=None, PS=None): EXAMPLES:: - sage: K. = CyclotomicField(3) # optional - sage.rings.number_field - sage: P. = ProjectiveSpace(K, 3) # optional - sage.rings.number_field - sage: C = Curve([y*w - x^2, z*w^2 - a*x^3], P) # optional - sage.rings.number_field - sage: L. = ProjectiveSpace(K, 2) # optional - sage.rings.number_field - sage: proj1 = C.projection(PS=L) # optional - sage.rings.number_field - sage: proj1 # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: K. = CyclotomicField(3) + sage: P. = ProjectiveSpace(K, 3) + sage: C = Curve([y*w - x^2, z*w^2 - a*x^3], P) + sage: L. = ProjectiveSpace(K, 2) + sage: proj1 = C.projection(PS=L) + sage: proj1 (Scheme morphism: From: Projective Curve over Cyclotomic Field of order 3 and degree 2 defined by -x^2 + y*w, (-a)*x^3 + z*w^2 @@ -338,20 +342,20 @@ def projection(self, P=None, PS=None): :: - sage: P. = ProjectiveSpace(GF(2), 3) # optional - sage.rings.finite_rings - sage: C = P.curve([(x - y)*(x - z)*(x - w)*(y - z)*(y - w), # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(2), 3) + sage: C = P.curve([(x - y)*(x - z)*(x - w)*(y - z)*(y - w), ....: x*y*z*w*(x + y + z + w)]) - sage: C.projection() # optional - sage.rings.finite_rings + sage: C.projection() Traceback (most recent call last): ... NotImplementedError: this curve contains all points of its ambient space :: - sage: P. = ProjectiveSpace(GF(7), 4) # optional - sage.rings.finite_rings - sage: C = P.curve([x^3 - y*z*u, w^2 - u^2 + 2*x*z, 3*x*w - y^2]) # optional - sage.rings.finite_rings - sage: L. = ProjectiveSpace(GF(7), 3) # optional - sage.rings.finite_rings - sage: C.projection(PS=L) # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(7), 4) + sage: C = P.curve([x^3 - y*z*u, w^2 - u^2 + 2*x*z, 3*x*w - y^2]) + sage: L. = ProjectiveSpace(GF(7), 3) + sage: C.projection(PS=L) (Scheme morphism: From: Projective Curve over Finite Field of size 7 defined by x^3 - y*z*u, 2*x*z + w^2 - u^2, -y^2 + 3*x*w @@ -360,8 +364,8 @@ def projection(self, P=None, PS=None): (x : y : z : w), Projective Curve over Finite Field of size 7 defined by b^2 - 3*a*d, a^5*b + a*b*c^3*d - 3*b*c^2*d^3, a^6 + a^2*c^3*d - 3*a*c^2*d^3) - sage: Q. = ProjectiveSpace(GF(7), 2) # optional - sage.rings.finite_rings - sage: C.projection(PS=Q) # optional - sage.rings.finite_rings + sage: Q. = ProjectiveSpace(GF(7), 2) + sage: C.projection(PS=Q) Traceback (most recent call last): ... TypeError: (=Projective Space of dimension 2 over Finite Field of @@ -533,9 +537,9 @@ def plane_projection(self, PP=None): :: - sage: P. = ProjectiveSpace(GF(7), 4) # optional - sage.rings.finite_rings - sage: C = P.curve([x^2 - 6*y^2, w*z*u - y^3 + 4*y^2*z, u^2 - x^2]) # optional - sage.rings.finite_rings - sage: C.plane_projection() # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(7), 4) + sage: C = P.curve([x^2 - 6*y^2, w*z*u - y^3 + 4*y^2*z, u^2 - x^2]) + sage: C.plane_projection() (Scheme morphism: From: Projective Curve over Finite Field of size 7 defined by x^2 + y^2, -y^3 - 3*y^2*z + z*w*u, -x^2 + u^2 @@ -548,9 +552,9 @@ def plane_projection(self, PP=None): :: - sage: P. = ProjectiveSpace(GF(17), 2) # optional - sage.rings.finite_rings - sage: C = P.curve(x^2 - y*z - z^2) # optional - sage.rings.finite_rings - sage: C.plane_projection() # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(17), 2) + sage: C = P.curve(x^2 - y*z - z^2) + sage: C.plane_projection() Traceback (most recent call last): ... TypeError: this curve is already a plane curve @@ -590,19 +594,22 @@ class ProjectivePlaneCurve(ProjectiveCurve): A projective plane curve defined over an algebraic closure of `\QQ`:: - sage: P. = ProjectiveSpace(QQbar, 2) # optional - sage.rings.number_field - sage: set_verbose(-1) # suppress warnings for slow computation # optional - sage.rings.number_field - sage: C = Curve([y*z - x^2 - QQbar.gen()*z^2], P); C # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: P. = ProjectiveSpace(QQbar, 2) + sage: set_verbose(-1) # suppress warnings for slow computation + sage: C = Curve([y*z - x^2 - QQbar.gen()*z^2], P); C Projective Plane Curve over Algebraic Field defined by -x^2 + y*z + (-I)*z^2 A projective plane curve defined over a finite field:: - sage: P. = ProjectiveSpace(GF(5^2, 'v'), 2) # optional - sage.rings.finite_rings - sage: C = Curve([y^2*z - x*z^2 - z^3], P); C # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5^2, 'v'), 2) + sage: C = Curve([y^2*z - x*z^2 - z^3], P); C Projective Plane Curve over Finite Field in v of size 5^2 defined by y^2*z - x*z^2 - z^3 """ + def __init__(self, A, f): """ Initialize. @@ -644,18 +651,18 @@ def divisor_of_function(self, r): EXAMPLES:: - sage: FF = FiniteField(5) # optional - sage.rings.finite_rings - sage: P2 = ProjectiveSpace(2, FF, names=['x','y','z']) # optional - sage.rings.finite_rings - sage: R = P2.coordinate_ring() # optional - sage.rings.finite_rings - sage: x, y, z = R.gens() # optional - sage.rings.finite_rings - sage: f = y^2*z^7 - x^9 - x*z^8 # optional - sage.rings.finite_rings - sage: C = Curve(f) # optional - sage.rings.finite_rings - sage: K = FractionField(R) # optional - sage.rings.finite_rings - sage: r = 1/x # optional - sage.rings.finite_rings - sage: C.divisor_of_function(r) # todo: not implemented !!!! # optional - sage.rings.finite_rings + sage: FF = FiniteField(5) + sage: P2 = ProjectiveSpace(2, FF, names=['x','y','z']) + sage: R = P2.coordinate_ring() + sage: x, y, z = R.gens() + sage: f = y^2*z^7 - x^9 - x*z^8 + sage: C = Curve(f) + sage: K = FractionField(R) + sage: r = 1/x + sage: C.divisor_of_function(r) # todo: not implemented !!!! [[-1, (0, 0, 1)]] - sage: r = 1/x^3 # optional - sage.rings.finite_rings - sage: C.divisor_of_function(r) # todo: not implemented !!!! # optional - sage.rings.finite_rings + sage: r = 1/x^3 + sage: C.divisor_of_function(r) # todo: not implemented !!!! [[-3, (0, 0, 1)]] """ F = self.base_ring() @@ -690,12 +697,12 @@ def local_coordinates(self, pt, n): EXAMPLES:: - sage: FF = FiniteField(5) # optional - sage.rings.finite_rings - sage: P2 = ProjectiveSpace(2, FF, names=['x','y','z']) # optional - sage.rings.finite_rings - sage: x, y, z = P2.coordinate_ring().gens() # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: pt = C([2,3,1]) # optional - sage.rings.finite_rings - sage: C.local_coordinates(pt,9) # todo: not implemented !!!! # optional - sage.rings.finite_rings + sage: FF = FiniteField(5) + sage: P2 = ProjectiveSpace(2, FF, names=['x','y','z']) + sage: x, y, z = P2.coordinate_ring().gens() + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: pt = C([2,3,1]) + sage: C.local_coordinates(pt,9) # todo: not implemented !!!! [2 + t, 3 + 3*t^2 + t^3 + 3*t^4 + 3*t^6 + 3*t^7 + t^8 + 2*t^9 + 3*t^11 + 3*t^12] """ @@ -769,25 +776,25 @@ def plot(self, *args, **kwds): sage: R. = QQ[] sage: C = Curve(x^3 - y^2*z) - sage: C.plot() # optional - sage.plot + sage: C.plot() # needs sage.plot Graphics object consisting of 1 graphics primitive The other affine patches of the same curve:: - sage: C.plot(patch=0) # optional - sage.plot + sage: C.plot(patch=0) # needs sage.plot Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=1) # optional - sage.plot + sage: C.plot(patch=1) # needs sage.plot Graphics object consisting of 1 graphics primitive An elliptic curve:: sage: E = EllipticCurve('101a') sage: C = Curve(E) - sage: C.plot() # optional - sage.plot + sage: C.plot() # needs sage.plot Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=0) # optional - sage.plot + sage: C.plot(patch=0) # needs sage.plot Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=1) # optional - sage.plot + sage: C.plot(patch=1) # needs sage.plot Graphics object consisting of 1 graphics primitive A hyperelliptic curve:: @@ -795,11 +802,11 @@ def plot(self, *args, **kwds): sage: P. = QQ[] sage: f = 4*x^5 - 30*x^3 + 45*x - 22 sage: C = HyperellipticCurve(f) - sage: C.plot() # optional - sage.plot + sage: C.plot() # needs sage.plot Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=0) # optional - sage.plot + sage: C.plot(patch=0) # needs sage.plot Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=1) # optional - sage.plot + sage: C.plot(patch=1) # needs sage.plot Graphics object consisting of 1 graphics primitive """ # if user has not specified a favorite affine patch, take the @@ -838,19 +845,19 @@ def is_singular(self, P=None): Over a finite field:: - sage: F = GF(19) # optional - sage.rings.finite_rings - sage: P2. = ProjectiveSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = Curve(X^3 + Y^3 + Z^3) # optional - sage.rings.finite_rings - sage: C.is_singular() # optional - sage.rings.finite_rings + sage: F = GF(19) + sage: P2. = ProjectiveSpace(F, 2) + sage: C = Curve(X^3 + Y^3 + Z^3) + sage: C.is_singular() False - sage: D = Curve(X^4 - X*Z^3) # optional - sage.rings.finite_rings - sage: D.is_singular() # optional - sage.rings.finite_rings + sage: D = Curve(X^4 - X*Z^3) + sage: D.is_singular() True - sage: E = Curve(X^5 + 19*Y^5 + Z^5) # optional - sage.rings.finite_rings - sage: E.is_singular() # optional - sage.rings.finite_rings + sage: E = Curve(X^5 + 19*Y^5 + Z^5) + sage: E.is_singular() True - sage: E = Curve(X^5 + 9*Y^5 + Z^5) # optional - sage.rings.finite_rings - sage: E.is_singular() # optional - sage.rings.finite_rings + sage: E = Curve(X^5 + 9*Y^5 + Z^5) + sage: E.is_singular() False Over `\CC`:: @@ -858,20 +865,20 @@ def is_singular(self, P=None): sage: F = CC sage: P2. = ProjectiveSpace(F, 2) sage: C = Curve(X) - sage: C.is_singular() + sage: C.is_singular() # needs sage.rings.function_field False sage: D = Curve(Y^2*Z - X^3) - sage: D.is_singular() + sage: D.is_singular() # needs sage.rings.function_field True sage: E = Curve(Y^2*Z - X^3 + Z^3) - sage: E.is_singular() + sage: E.is_singular() # needs sage.rings.function_field False Showing that :trac:`12187` is fixed:: - sage: F. = GF(2)[] # optional - sage.rings.finite_rings - sage: G = Curve(X^2 + Y*Z) # optional - sage.rings.finite_rings - sage: G.is_singular() # optional - sage.rings.finite_rings + sage: F. = GF(2)[] + sage: G = Curve(X^2 + Y*Z) + sage: G.is_singular() False :: @@ -879,7 +886,7 @@ def is_singular(self, P=None): sage: P. = ProjectiveSpace(CC, 2) sage: C = Curve([y^4 - x^3*z], P) sage: Q = P([0,0,1]) - sage: C.is_singular() + sage: C.is_singular() # needs sage.rings.function_field True """ if P is None: @@ -928,16 +935,17 @@ def tangents(self, P, factor=True): EXAMPLES:: + sage: # needs sage.rings.number_field sage: set_verbose(-1) - sage: P. = ProjectiveSpace(QQbar, 2) # optional - sage.rings.number_field - sage: C = Curve([x^3*y + 2*x^2*y^2 + x*y^3 + x^3*z # optional - sage.rings.number_field + sage: P. = ProjectiveSpace(QQbar, 2) + sage: C = Curve([x^3*y + 2*x^2*y^2 + x*y^3 + x^3*z ....: + 7*x^2*y*z + 14*x*y^2*z + 9*y^3*z], P) - sage: Q = P([0,0,1]) # optional - sage.rings.number_field - sage: C.tangents(Q) # optional - sage.rings.number_field + sage: Q = P([0,0,1]) + sage: C.tangents(Q) [x + 4.147899035704788?*y, x + (1.426050482147607? + 0.3689894074818041?*I)*y, x + (1.426050482147607? - 0.3689894074818041?*I)*y] - sage: C.tangents(Q, factor=False) # optional - sage.rings.number_field + sage: C.tangents(Q, factor=False) [6*x^3 + 42*x^2*y + 84*x*y^2 + 54*y^3] :: @@ -1008,16 +1016,17 @@ def is_ordinary_singularity(self, P): :: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(a^2 - 3) # optional - sage.rings.number_field - sage: P. = ProjectiveSpace(K, 2) # optional - sage.rings.number_field - sage: C = P.curve([x^2*y^3*z^4 - y^6*z^3 - 4*x^2*y^4*z^3 - 4*x^4*y^2*z^3 # optional - sage.rings.number_field + sage: K. = NumberField(a^2 - 3) + sage: P. = ProjectiveSpace(K, 2) + sage: C = P.curve([x^2*y^3*z^4 - y^6*z^3 - 4*x^2*y^4*z^3 - 4*x^4*y^2*z^3 ....: + 3*y^7*z^2 + 10*x^2*y^5*z^2 + 9*x^4*y^3*z^2 ....: + 5*x^6*y*z^2 - 3*y^8*z - 9*x^2*y^6*z - 11*x^4*y^4*z ....: - 7*x^6*y^2*z - 2*x^8*z + y^9 + 2*x^2*y^7 + 3*x^4*y^5 ....: + 4*x^6*y^3 + 2*x^8*y]) - sage: Q = P([0,1,1]) # optional - sage.rings.number_field - sage: C.is_ordinary_singularity(Q) # optional - sage.rings.number_field + sage: Q = P([0,1,1]) + sage: C.is_ordinary_singularity(Q) True :: @@ -1070,9 +1079,9 @@ def quadratic_transform(self): :: - sage: P. = ProjectiveSpace(GF(17), 2) # optional - sage.rings.finite_rings - sage: C = P.curve([y^7*z^2 - 16*x^9 + x*y*z^7 + 2*z^9]) # optional - sage.rings.finite_rings - sage: C.quadratic_transform() # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(17), 2) + sage: C = P.curve([y^7*z^2 - 16*x^9 + x*y*z^7 + 2*z^9]) + sage: C.quadratic_transform() Scheme morphism: From: Projective Plane Curve over Finite Field of size 17 defined by x^9 + y^7*z^2 + x*y*z^7 + 2*z^9 @@ -1136,16 +1145,17 @@ def excellent_position(self, Q): :: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(a^2 - 3) # optional - sage.rings.number_field - sage: P. = ProjectiveSpace(K, 2) # optional - sage.rings.number_field - sage: C = P.curve([z^2*y^3*x^4 - y^6*x^3 - 4*z^2*y^4*x^3 - 4*z^4*y^2*x^3 # optional - sage.rings.number_field + sage: K. = NumberField(a^2 - 3) + sage: P. = ProjectiveSpace(K, 2) + sage: C = P.curve([z^2*y^3*x^4 - y^6*x^3 - 4*z^2*y^4*x^3 - 4*z^4*y^2*x^3 ....: + 3*y^7*x^2 + 10*z^2*y^5*x^2 + 9*z^4*y^3*x^2 ....: + 5*z^6*y*x^2 - 3*y^8*x - 9*z^2*y^6*x - 11*z^4*y^4*x ....: - 7*z^6*y^2*x - 2*z^8*x + y^9 + 2*z^2*y^7 + 3*z^4*y^5 ....: + 4*z^6*y^3 + 2*z^8*y]) - sage: Q = P([1,0,0]) # optional - sage.rings.number_field - sage: C.excellent_position(Q) # optional - sage.rings.number_field + sage: Q = P([1,0,0]) + sage: C.excellent_position(Q) Scheme morphism: From: Projective Plane Curve over Number Field in b with defining polynomial a^2 - 3 @@ -1174,13 +1184,14 @@ def excellent_position(self, Q): :: + sage: # needs sage.rings.number_field sage.symbolic sage: set_verbose(-1) - sage: a = QQbar(sqrt(2)) # optional - sage.rings.number_field - sage: P. = ProjectiveSpace(QQbar, 2) # optional - sage.rings.number_field - sage: C = Curve([(-1/4*a)*x^3 + (-3/4*a)*x^2*y # optional - sage.rings.number_field + sage: a = QQbar(sqrt(2)) + sage: P. = ProjectiveSpace(QQbar, 2) + sage: C = Curve([(-1/4*a)*x^3 + (-3/4*a)*x^2*y ....: + (-3/4*a)*x*y^2 + (-1/4*a)*y^3 - 2*x*y*z], P) - sage: Q = P([0,0,1]) # optional - sage.rings.number_field - sage: C.excellent_position(Q) # optional - sage.rings.number_field + sage: Q = P([0,0,1]) + sage: C.excellent_position(Q) Scheme morphism: From: Projective Plane Curve over Algebraic Field defined by (-0.3535533905932738?)*x^3 + (-1.060660171779822?)*x^2*y @@ -1331,11 +1342,12 @@ def ordinary_model(self): EXAMPLES:: + sage: # needs sage.rings.number_field sage: set_verbose(-1) - sage: K = QuadraticField(3) # optional - sage.rings.number_field - sage: P. = ProjectiveSpace(K, 2) # optional - sage.rings.number_field - sage: C = Curve([x^5 - K.0*y*z^4], P) # optional - sage.rings.number_field - sage: C.ordinary_model() # optional - sage.rings.number_field + sage: K = QuadraticField(3) + sage: P. = ProjectiveSpace(K, 2) + sage: C = Curve([x^5 - K.0*y*z^4], P) + sage: C.ordinary_model() Scheme morphism: From: Projective Plane Curve over Number Field in a with defining polynomial x^2 - 3 with a = 1.732050807568878? @@ -1436,6 +1448,7 @@ def ordinary_model(self): + (-3/16*a - 1/4)*y*z^3 + (1/16*a + 3/32)*z^4) """ # helper function for extending the base field + def extension(self): F = self.base_ring() pts = self.change_ring(F.embeddings(QQbar)[0]).rational_points() @@ -1533,12 +1546,13 @@ def is_transverse(self, C, P): :: - sage: K = QuadraticField(-1) # optional - sage.rings.number_field - sage: P. = ProjectiveSpace(K, 2) # optional - sage.rings.number_field - sage: C = Curve([y^2*z - K.0*x^3], P) # optional - sage.rings.number_field - sage: D = Curve([z*x + y^2], P) # optional - sage.rings.number_field - sage: Q = P([0,0,1]) # optional - sage.rings.number_field - sage: C.is_transverse(D, Q) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: K = QuadraticField(-1) + sage: P. = ProjectiveSpace(K, 2) + sage: C = Curve([y^2*z - K.0*x^3], P) + sage: D = Curve([z*x + y^2], P) + sage: Q = P([0,0,1]) + sage: C.is_transverse(D, Q) False :: @@ -1598,9 +1612,9 @@ def arithmetic_genus(self): :: - sage: P. = ProjectiveSpace(GF(7), 4) # optional - sage.rings.finite_rings - sage: C = P.curve([t^3 - x*y*w, x^3 + y^3 + z^3, z - w]) # optional - sage.rings.finite_rings - sage: C.arithmetic_genus() # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(7), 4) + sage: C = P.curve([t^3 - x*y*w, x^3 + y^3 + z^3, z - w]) + sage: C.arithmetic_genus() 10 """ if not self.is_irreducible(): @@ -1680,13 +1694,13 @@ def arithmetic_genus(self): EXAMPLES:: - sage: x,y,z = PolynomialRing(GF(5), 3, 'xyz').gens() # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8); C # optional - sage.rings.finite_rings + sage: x,y,z = PolynomialRing(GF(5), 3, 'xyz').gens() + sage: C = Curve(y^2*z^7 - x^9 - x*z^8); C Projective Plane Curve over Finite Field of size 5 defined by -x^9 + y^2*z^7 - x*z^8 - sage: C.arithmetic_genus() # optional - sage.rings.finite_rings + sage: C.arithmetic_genus() 28 - sage: C.genus() # optional - sage.rings.finite_rings + sage: C.genus() 4 :: @@ -1721,15 +1735,16 @@ def fundamental_group(self): In the case of number fields, they need to have an embedding into the algebraic field:: - sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] # optional - sage.rings.number_field - sage: a # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] + sage: a -2.236067977499790?*I - sage: F = NumberField(a.minpoly(), 'a', embedding=a) # optional - sage.rings.number_field - sage: P. = ProjectiveSpace(F, 2) # optional - sage.rings.number_field - sage: F.inject_variables() # optional - sage.rings.number_field + sage: F = NumberField(a.minpoly(), 'a', embedding=a) + sage: P. = ProjectiveSpace(F, 2) + sage: F.inject_variables() Defining a - sage: C = P.curve(x^2 + a * y^2) # optional - sage.rings.number_field - sage: C.fundamental_group() # optional - sirocco # optional - sage.rings.number_field + sage: C = P.curve(x^2 + a * y^2) + sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | > .. WARNING:: @@ -1738,13 +1753,18 @@ def fundamental_group(self): TESTS:: + sage: F. = FreeGroup() + sage: G = F / [x1^-1*(x1^-1*x0^-1*x1*x0^-1)^2, (x1^-1*x0^-1)^2*x1^-1*(x0*x1)^2*x0] + sage: G.order() + 320 sage: P. = ProjectiveSpace(QQ, 2) sage: C = P.curve(z^2*y^3 - z*(33*x*z+2*x^2+8*z^2)*y^2 ....: + (21*z^2+21*x*z-x^2)*(z^2+11*x*z-x^2)*y ....: + (x-18*z)*(z^2+11*x*z-x^2)^2) - sage: C.fundamental_group() # optional - sirocco - Finitely presented group < x1, x3 | (x3^-1*x1^-1*x3*x1^-1)^2*x3^-1, - x3*(x1^-1*x3^-1)^2*x1^-1*(x3*x1)^2 > + sage: G0 = C.fundamental_group() # optional - sirocco + sage: G.is_isomorphic(G0) # optional - sirocco + #I Forcing finiteness test + True """ from sage.schemes.curves.zariski_vankampen import fundamental_group @@ -1802,7 +1822,7 @@ def rational_parameterization(self): sage: P. = ProjectiveSpace(QQ, 2) sage: C = Curve([x^2 + y^2 + z^2], P) - sage: C.rational_parameterization() # optional - sage.rings.number_field + sage: C.rational_parameterization() Scheme morphism: From: Projective Space of dimension 1 over Number Field in a with defining polynomial a^2 + 1 @@ -1862,62 +1882,63 @@ def rational_points_iterator(self): EXAMPLES:: - sage: F = GF(37) # optional - sage.rings.finite_rings - sage: P2. = ProjectiveSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = Curve(X^7 + Y*X*Z^5*55 + Y^7*12) # optional - sage.rings.finite_rings - sage: len(list(C.rational_points_iterator())) # optional - sage.rings.finite_rings + sage: F = GF(37) + sage: P2. = ProjectiveSpace(F, 2) + sage: C = Curve(X^7 + Y*X*Z^5*55 + Y^7*12) + sage: len(list(C.rational_points_iterator())) 37 :: - sage: F = GF(2) # optional - sage.rings.finite_rings - sage: P2. = ProjectiveSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = Curve(X*Y*Z) # optional - sage.rings.finite_rings - sage: a = C.rational_points_iterator() # optional - sage.rings.finite_rings - sage: next(a) # optional - sage.rings.finite_rings + sage: F = GF(2) + sage: P2. = ProjectiveSpace(F, 2) + sage: C = Curve(X*Y*Z) + sage: a = C.rational_points_iterator() + sage: next(a) (1 : 0 : 0) - sage: next(a) # optional - sage.rings.finite_rings + sage: next(a) (0 : 1 : 0) - sage: next(a) # optional - sage.rings.finite_rings + sage: next(a) (1 : 1 : 0) - sage: next(a) # optional - sage.rings.finite_rings + sage: next(a) (0 : 0 : 1) - sage: next(a) # optional - sage.rings.finite_rings + sage: next(a) (1 : 0 : 1) - sage: next(a) # optional - sage.rings.finite_rings + sage: next(a) (0 : 1 : 1) - sage: next(a) # optional - sage.rings.finite_rings + sage: next(a) Traceback (most recent call last): ... StopIteration :: - sage: F = GF(3^2,'a') # optional - sage.rings.finite_rings - sage: P2. = ProjectiveSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = Curve(X^3 + 5*Y^2*Z - 33*X*Y*X) # optional - sage.rings.finite_rings - sage: b = C.rational_points_iterator() # optional - sage.rings.finite_rings - sage: next(b) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F = GF(3^2,'a') + sage: P2. = ProjectiveSpace(F, 2) + sage: C = Curve(X^3 + 5*Y^2*Z - 33*X*Y*X) + sage: b = C.rational_points_iterator() + sage: next(b) (0 : 1 : 0) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (0 : 0 : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (2*a + 2 : a : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (2 : a + 1 : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (a + 1 : 2*a + 1 : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (1 : 2 : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (2*a + 2 : 2*a : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (2 : 2*a + 2 : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (a + 1 : a + 2 : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) (1 : 1 : 1) - sage: next(b) # optional - sage.rings.finite_rings + sage: next(b) Traceback (most recent call last): ... StopIteration @@ -1972,15 +1993,15 @@ def _points_via_singular(self, sort=True): EXAMPLES:: - sage: x, y, z = PolynomialRing(GF(5), 3, 'xyz').gens() # optional - sage.rings.finite_rings - sage: f = y^2*z^7 - x^9 - x*z^8 # optional - sage.rings.finite_rings - sage: C = Curve(f); C # optional - sage.rings.finite_rings + sage: x, y, z = PolynomialRing(GF(5), 3, 'xyz').gens() + sage: f = y^2*z^7 - x^9 - x*z^8 + sage: C = Curve(f); C Projective Plane Curve over Finite Field of size 5 defined by -x^9 + y^2*z^7 - x*z^8 - sage: C._points_via_singular() # optional - sage.rings.finite_rings + sage: C._points_via_singular() [(0 : 0 : 1), (0 : 1 : 0), (2 : 2 : 1), (2 : 3 : 1), (3 : 1 : 1), (3 : 4 : 1)] - sage: C._points_via_singular(sort=False) # random # optional - sage.rings.finite_rings + sage: C._points_via_singular(sort=False) # random [(0 : 1 : 0), (3 : 1 : 1), (3 : 4 : 1), (2 : 2 : 1), (0 : 0 : 1), (2 : 3 : 1)] @@ -2042,20 +2063,20 @@ def riemann_roch_basis(self, D): EXAMPLES:: - sage: R. = GF(2)[] # optional - sage.rings.finite_rings - sage: f = x^3*y + y^3*z + x*z^3 # optional - sage.rings.finite_rings - sage: C = Curve(f); pts = C.rational_points() # optional - sage.rings.finite_rings - sage: D = C.divisor([ (4, pts[0]), (4, pts[2]) ]) # optional - sage.rings.finite_rings - sage: C.riemann_roch_basis(D) # optional - sage.rings.finite_rings + sage: R. = GF(2)[] + sage: f = x^3*y + y^3*z + x*z^3 + sage: C = Curve(f); pts = C.rational_points() + sage: D = C.divisor([ (4, pts[0]), (4, pts[2]) ]) + sage: C.riemann_roch_basis(D) [x/y, 1, z/y, z^2/y^2, z/x, z^2/(x*y)] :: - sage: R. = GF(5)[] # optional - sage.rings.finite_rings - sage: f = x^7 + y^7 + z^7 # optional - sage.rings.finite_rings - sage: C = Curve(f); pts = C.rational_points() # optional - sage.rings.finite_rings - sage: D = C.divisor([ (3, pts[0]), (-1,pts[1]), (10, pts[5]) ]) # optional - sage.rings.finite_rings - sage: C.riemann_roch_basis(D) # optional - sage.rings.finite_rings + sage: R. = GF(5)[] + sage: f = x^7 + y^7 + z^7 + sage: C = Curve(f); pts = C.rational_points() + sage: D = C.divisor([ (3, pts[0]), (-1,pts[1]), (10, pts[5]) ]) + sage: C.riemann_roch_basis(D) [(-2*x + y)/(x + y), (-x + z)/(x + y)] .. NOTE:: @@ -2133,55 +2154,56 @@ def rational_points(self, algorithm="enum", sort=True): EXAMPLES:: - sage: x, y, z = PolynomialRing(GF(5), 3, 'xyz').gens() # optional - sage.rings.finite_rings - sage: f = y^2*z^7 - x^9 - x*z^8 # optional - sage.rings.finite_rings - sage: C = Curve(f); C # optional - sage.rings.finite_rings + sage: x, y, z = PolynomialRing(GF(5), 3, 'xyz').gens() + sage: f = y^2*z^7 - x^9 - x*z^8 + sage: C = Curve(f); C Projective Plane Curve over Finite Field of size 5 defined by -x^9 + y^2*z^7 - x*z^8 - sage: C.rational_points() # optional - sage.rings.finite_rings + sage: C.rational_points() [(0 : 0 : 1), (0 : 1 : 0), (2 : 2 : 1), (2 : 3 : 1), (3 : 1 : 1), (3 : 4 : 1)] - sage: C = Curve(x - y + z) # optional - sage.rings.finite_rings - sage: C.rational_points() # optional - sage.rings.finite_rings + sage: C = Curve(x - y + z) + sage: C.rational_points() [(0 : 1 : 1), (1 : 1 : 0), (1 : 2 : 1), (2 : 3 : 1), (3 : 4 : 1), (4 : 0 : 1)] - sage: C = Curve(x*z + z^2) # optional - sage.rings.finite_rings - sage: C.rational_points('all') # optional - sage.rings.finite_rings + sage: C = Curve(x*z + z^2) + sage: C.rational_points('all') [(0 : 1 : 0), (1 : 0 : 0), (1 : 1 : 0), (2 : 1 : 0), (3 : 1 : 0), (4 : 0 : 1), (4 : 1 : 0), (4 : 1 : 1), (4 : 2 : 1), (4 : 3 : 1), (4 : 4 : 1)] :: - sage: F = GF(7) # optional - sage.rings.finite_rings - sage: P2. = ProjectiveSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = Curve(X^3 + Y^3 - Z^3) # optional - sage.rings.finite_rings - sage: C.rational_points() # optional - sage.rings.finite_rings + sage: F = GF(7) + sage: P2. = ProjectiveSpace(F, 2) + sage: C = Curve(X^3 + Y^3 - Z^3) + sage: C.rational_points() [(0 : 1 : 1), (0 : 2 : 1), (0 : 4 : 1), (1 : 0 : 1), (2 : 0 : 1), (3 : 1 : 0), (4 : 0 : 1), (5 : 1 : 0), (6 : 1 : 0)] :: - sage: F = GF(1237) # optional - sage.rings.finite_rings - sage: P2. = ProjectiveSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = Curve(X^7 + 7*Y^6*Z + Z^4*X^2*Y*89) # optional - sage.rings.finite_rings - sage: len(C.rational_points()) # optional - sage.rings.finite_rings + sage: F = GF(1237) + sage: P2. = ProjectiveSpace(F, 2) + sage: C = Curve(X^7 + 7*Y^6*Z + Z^4*X^2*Y*89) + sage: len(C.rational_points()) 1237 :: - sage: F = GF(2^6,'a') # optional - sage.rings.finite_rings - sage: P2. = ProjectiveSpace(F, 2) # optional - sage.rings.finite_rings - sage: C = Curve(X^5 + 11*X*Y*Z^3 + X^2*Y^3 - 13*Y^2*Z^3) # optional - sage.rings.finite_rings - sage: len(C.rational_points()) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F = GF(2^6,'a') + sage: P2. = ProjectiveSpace(F, 2) + sage: C = Curve(X^5 + 11*X*Y*Z^3 + X^2*Y^3 - 13*Y^2*Z^3) + sage: len(C.rational_points()) 104 :: - sage: R. = GF(2)[] # optional - sage.rings.finite_rings - sage: f = x^3*y + y^3*z + x*z^3 # optional - sage.rings.finite_rings - sage: C = Curve(f); pts = C.rational_points() # optional - sage.rings.finite_rings - sage: pts # optional - sage.rings.finite_rings + sage: R. = GF(2)[] + sage: f = x^3*y + y^3*z + x*z^3 + sage: C = Curve(f); pts = C.rational_points() + sage: pts [(0 : 0 : 1), (0 : 1 : 0), (1 : 0 : 0)] """ @@ -2223,9 +2245,9 @@ def __init__(self, A, f): TESTS:: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: loads(dumps(C)) == C # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: loads(dumps(C)) == C True """ super().__init__(A, f) @@ -2253,9 +2275,10 @@ def function_field(self): :: - sage: P. = ProjectiveSpace(GF(4), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) # optional - sage.rings.finite_rings - sage: C.function_field() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(4), 2) + sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) + sage: C.function_field() Function field in z defined by z^5 + y*z^3 + y^5 + 1 """ return self._function_field @@ -2267,9 +2290,9 @@ def _genus(self): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(4), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) # optional - sage.rings.finite_rings - sage: C.genus() # indirect doctest # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(4), 2) # needs sage.rings.finite_rings + sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) # needs sage.rings.finite_rings + sage: C.genus() # indirect doctest # needs sage.rings.finite_rings 1 """ return self._open_affine.genus() @@ -2280,13 +2303,14 @@ def __call__(self, *args): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(4), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) # optional - sage.rings.finite_rings - sage: C(1,1,1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(4), 2) + sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) + sage: C(1,1,1) (1 : 1 : 1) - sage: C(y/z) # optional - sage.rings.finite_rings + sage: C(y/z) (y/(y^5 + 1))*z^4 + (y^2/(y^5 + 1))*z^2 - sage: C(GF(4^2)) # optional - sage.rings.finite_rings + sage: C(GF(4^2)) Set of rational points of Closed subscheme of Projective Space of dimension 2 over Finite Field in z4 of size 2^4 defined by: x^5 + y^5 + x*y*z^3 + z^5 @@ -2305,11 +2329,12 @@ def function(self, f): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(4), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) # optional - sage.rings.finite_rings - sage: f = C.function(x/y); f # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(4), 2) + sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) + sage: f = C.function(x/y); f 1/y - sage: f.divisor() # optional - sage.rings.finite_rings + sage: f.divisor() Place (1/y, 1/y^2*z^2 + z2/y*z + 1) + Place (1/y, 1/y^2*z^2 + ((z2 + 1)/y)*z + 1) + Place (1/y, 1/y*z + 1) @@ -2334,11 +2359,12 @@ def coordinate_functions(self, i=None): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(4), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) # optional - sage.rings.finite_rings - sage: C.coordinate_functions(0) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(4), 2) + sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) + sage: C.coordinate_functions(0) (y, z) - sage: C.coordinate_functions(1) # optional - sage.rings.finite_rings + sage: C.coordinate_functions(1) (1/y, 1/y*z) """ coords = self._coordinate_functions @@ -2354,9 +2380,9 @@ def _function_field(self): TESTS:: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: C._function_field # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: C._function_field Function field in z defined by z^8 + 4*y^2*z^7 + 1 """ return self._open_affine._function_field @@ -2368,9 +2394,9 @@ def _lift_to_function_field(self): TESTS:: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: C._lift_to_function_field # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: C._lift_to_function_field Ring morphism: From: Multivariate Polynomial Ring in x, y, z over Finite Field of size 5 To: Function field in z defined by z^8 + 4*y^2*z^7 + 1 @@ -2389,9 +2415,9 @@ def _coordinate_functions(self): TESTS:: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: C._coordinate_functions # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: C._coordinate_functions (1, y, z) """ # homogeneous coordinate functions @@ -2406,12 +2432,12 @@ def _singularities(self): TESTS:: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: C._singularities # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: C._singularities [(Point (x, z), [Place (1/y, 1/y*z^5 + 4*y*z^4 + 1/y^2*z)])] - sage: D = Curve(x) # optional - sage.rings.finite_rings - sage: D._singularities # optional - sage.rings.finite_rings + sage: D = Curve(x) + sage: D._singularities [] """ @@ -2466,9 +2492,9 @@ def singular_closed_points(self): :: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: C.singular_closed_points() # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: C.singular_closed_points() [Point (x, z)] """ return [p[0] for p in self._singularities] @@ -2484,13 +2510,13 @@ def place_to_closed_point(self, place): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: pls = C.places() # optional - sage.rings.finite_rings - sage: C.place_to_closed_point(pls[-1]) # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: pls = C.places() + sage: C.place_to_closed_point(pls[-1]) Point (x - 2*z, y - 2*z) - sage: pls2 = C.places(2) # optional - sage.rings.finite_rings - sage: C.place_to_closed_point(pls2[0]) # optional - sage.rings.finite_rings + sage: pls2 = C.places(2) + sage: C.place_to_closed_point(pls2[0]) Point (y^2 + y*z + z^2, x + y) """ F = self.function_field() @@ -2584,9 +2610,9 @@ def places_on(self, point): :: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^2*z - y^3) # optional - sage.rings.finite_rings - sage: [C.places_on(p) for p in C.closed_points()] # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(x^2*z - y^3) + sage: [C.places_on(p) for p in C.closed_points()] [[Place (1/y)], [Place (y)], [Place (y + 1)], @@ -2626,11 +2652,11 @@ class IntegralProjectiveCurve_finite_field(IntegralProjectiveCurve): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2*z^7 - x^9 - x*z^8) # optional - sage.rings.finite_rings - sage: C.function_field() # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: C.function_field() Function field in z defined by z^8 + 4*y^2*z^7 + 1 - sage: C.closed_points() # optional - sage.rings.finite_rings + sage: C.closed_points() [Point (x, z), Point (x, y), Point (x - 2*z, y + 2*z), @@ -2650,16 +2676,16 @@ def places(self, degree=1): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(5), 2) # optional - sage.rings.finite_rings - sage: C = Curve(x^2*z - y^3) # optional - sage.rings.finite_rings - sage: C.places() # optional - sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(x^2*z - y^3) + sage: C.places() [Place (1/y), Place (y), Place (y + 1), Place (y + 2), Place (y + 3), Place (y + 4)] - sage: C.places(2) # optional - sage.rings.finite_rings + sage: C.places(2) [Place (y^2 + 2), Place (y^2 + 3), Place (y^2 + y + 1), @@ -2684,10 +2710,11 @@ def closed_points(self, degree=1): EXAMPLES:: - sage: A. = AffineSpace(GF(9),2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x-2) # optional - sage.rings.finite_rings - sage: Cp = C.projective_closure() # optional - sage.rings.finite_rings - sage: Cp.closed_points() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(9),2) + sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x-2) + sage: Cp = C.projective_closure() + sage: Cp.closed_points() [Point (x0, x1), Point (x0 + (-z2 - 1)*x2, x1), Point (x0 + (z2 + 1)*x2, x1), @@ -2731,10 +2758,10 @@ def L_polynomial(self, name='t'): EXAMPLES:: - sage: A. = AffineSpace(GF(3), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) # optional - sage.rings.finite_rings - sage: Cbar = C.projective_closure() # optional - sage.rings.finite_rings - sage: Cbar.L_polynomial() # optional - sage.rings.finite_rings + sage: A. = AffineSpace(GF(3), 2) + sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) + sage: Cbar = C.projective_closure() + sage: Cbar.L_polynomial() 9*t^4 - 3*t^3 + t^2 - t + 1 """ @@ -2763,15 +2790,16 @@ def number_of_rational_points(self, r=1): EXAMPLES:: - sage: A. = AffineSpace(GF(3), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) # optional - sage.rings.finite_rings - sage: Cbar = C.projective_closure() # optional - sage.rings.finite_rings - sage: Cbar.number_of_rational_points(3) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(3), 2) + sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) + sage: Cbar = C.projective_closure() + sage: Cbar.number_of_rational_points(3) 21 - sage: D = Cbar.change_ring(Cbar.base_ring().extension(3)) # optional - sage.rings.finite_rings - sage: D.base_ring() # optional - sage.rings.finite_rings + sage: D = Cbar.change_ring(Cbar.base_ring().extension(3)) + sage: D.base_ring() Finite Field in z3 of size 3^3 - sage: len(D.closed_points()) # optional - sage.rings.finite_rings + sage: len(D.closed_points()) 21 """ @@ -2806,12 +2834,13 @@ class IntegralProjectivePlaneCurve_finite_field(IntegralProjectiveCurve_finite_f EXAMPLES:: - sage: A. = AffineSpace(GF(9), 2) # optional - sage.rings.finite_rings - sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) # optional - sage.rings.finite_rings - sage: Cb = C.projective_closure() # optional - sage.rings.finite_rings - sage: Cb.singular_closed_points() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(9), 2) + sage: C = Curve(y^2 - x^5 - x^4 - 2*x^3 - 2*x - 2) + sage: Cb = C.projective_closure() + sage: Cb.singular_closed_points() [Point (x0, x1)] - sage: Cb.function_field() # optional - sage.rings.finite_rings + sage: Cb.function_field() Function field in y defined by y^2 + 2*x^5 + 2*x^4 + x^3 + x + 1 """ _point = IntegralProjectivePlaneCurvePoint_finite_field @@ -2835,7 +2864,7 @@ def Hasse_bounds(q, genus=1): sage: Hasse_bounds(2) (1, 5) - sage: Hasse_bounds(next_prime(10^30)) + sage: Hasse_bounds(next_prime(10^30)) # needs sage.libs.pari (999999999999998000000000000058, 1000000000000002000000000000058) """ if genus == 1: diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 2edb29f365a..0940825b9a5 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.geometry.polyhedron sage.graphs sage.groups sage.rings.number_field r""" Zariski-Van Kampen method implementation @@ -22,10 +23,13 @@ EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: R. = QQ[] + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy + sage: R. = QQ[] sage: f = y^3 + x^3 - 1 - sage: fundamental_group(f) # optional - sirocco + sage: braid_monodromy(f) + ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0}) + sage: fundamental_group(f) Finitely presented group < x0 | > """ # **************************************************************************** @@ -39,25 +43,32 @@ # **************************************************************************** import itertools from copy import copy - +from sage.combinat.combination import Combinations from sage.combinat.permutation import Permutation +from sage.functions.generalized import sign from sage.geometry.voronoi_diagram import VoronoiDiagram from sage.graphs.graph import Graph from sage.groups.braid import BraidGroup +from sage.groups.finitely_presented import wrap_FpGroup from sage.groups.free_group import FreeGroup from sage.groups.perm_gps.permgroup_named import SymmetricGroup +from sage.libs.braiding import leftnormalform, rightnormalform +from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_function from sage.misc.flatten import flatten from sage.misc.misc_c import prod from sage.parallel.decorate import parallel from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.complex_mpfr import ComplexField +from sage.rings.integer_ring import ZZ +from sage.rings.number_field.number_field import NumberField +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField +# from sage.sets.set import Set - -roots_interval_cache = {} +roots_interval_cache = dict() def braid_from_piecewise(strands): @@ -76,11 +87,12 @@ def braid_from_piecewise(strands): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import braid_from_piecewise # optional - sirocco + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import braid_from_piecewise sage: paths = [[(0, 0, 1), (0.2, -1, -0.5), (0.8, -1, 0), (1, 0, -1)], ....: [(0, -1, 0), (0.5, 0, -1), (1, 1, 0)], ....: [(0, 1, 0), (0.5, 1, 1), (1, 0, 1)]] - sage: braid_from_piecewise(paths) # optional - sirocco + sage: braid_from_piecewise(paths) s0*s1 """ L = strands @@ -153,43 +165,63 @@ def sgn(x, y): return B(braid) -def discrim(f): +def discrim(pols): r""" - Return the points in the discriminant of ``f``. + Return the points in the discriminant of the product of the polynomials + of a list or tuple ``pols``. The result is the set of values of the first variable for which two roots in the second variable coincide. INPUT: - - ``f`` -- a polynomial in two variables with coefficients in a - number field with a fixed embedding in `\QQbar` + - ``pols`` -- a list or tuple of polynomials in two variables with + coefficients in a number field with a fixed embedding in `\QQbar` OUTPUT: - A list with the values of the discriminant in `\QQbar`. + A tuple with the roots of the discriminant in `\QQbar`. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import discrim - sage: R. = QQ[] - sage: f = (y^3 + x^3 - 1) * (x + y) - sage: discrim(f) - [1, + sage: R. = QQ[] + sage: flist = (y^3 + x^3 - 1, 2 * x + y) + sage: discrim(flist) + (1, -0.500000000000000? - 0.866025403784439?*I, - -0.500000000000000? + 0.866025403784439?*I] + -0.500000000000000? + 0.866025403784439?*I, + -0.522757958574711?, + 0.2613789792873551? - 0.4527216721561923?*I, + 0.2613789792873551? + 0.4527216721561923?*I) """ - x, y = f.parent().gens() - F = f.base_ring() - poly = F[x](f.discriminant(y)).radical() - return poly.roots(QQbar, multiplicities=False) + flist = tuple(pols) + x, y = flist[0].parent().gens() + field = flist[0].base_ring() + pol_ring = PolynomialRing(field, (x,)) + + @parallel + def discrim_pairs(f, g): + if g is None: + return pol_ring(f.discriminant(y)) + return pol_ring(f.resultant(g, y)) + + pairs = [(f, None) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] + fdiscrim = discrim_pairs(pairs) + rts = () + poly = 1 + for u in fdiscrim: + h0 = u[1].radical() + h1 = h0 // h0.gcd(poly) + rts += tuple(h1.roots(QQbar, multiplicities=False)) + poly = poly * h1 + return rts @cached_function def corrected_voronoi_diagram(points): r""" Compute a Voronoi diagram of a set of points with rational coordinates. - The given points are granted to lie one in each bounded region. INPUT: @@ -198,7 +230,7 @@ def corrected_voronoi_diagram(points): OUTPUT: - A VoronoiDiagram constructed from rational approximations of the points, + A Voronoi diagram constructed from rational approximations of the points, with the guarantee that each bounded region contains exactly one of the input points. @@ -243,56 +275,193 @@ def corrected_voronoi_diagram(points): return V -def segments(points): +def orient_circuit(circuit, convex=False, precision=53, verbose=False): + r""" + Reverse a circuit if it goes clockwise; otherwise leave it unchanged. + + INPUT: + + - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given + by a list of edges + + - ``convex`` -- boolean (default: `False`), if set to ``True`` a simpler + computation is made + + - ``precision`` -- bits of precision (default: 53) + + - ``verbose`` -- boolean (default: ``False``) for testing purposes + + OUTPUT: + + The same circuit if it goes counterclockwise, and its reversed otherwise, + given as the ordered list of vertices with identic extremities. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import orient_circuit + sage: points = [(-4, 0), (4, 0), (0, 4), (0, -4), (0, 0)] + sage: V = VoronoiDiagram(points) + sage: E = Graph() + sage: for reg in V.regions().values(): + ....: if reg.rays() or reg.lines(): + ....: E = E.union(reg.vertex_graph()) + sage: E.vertices(sort=True) + [A vertex at (-2, -2), + A vertex at (-2, 2), + A vertex at (2, -2), + A vertex at (2, 2)] + sage: cir = E.eulerian_circuit() + sage: cir + [(A vertex at (-2, -2), A vertex at (2, -2), None), + (A vertex at (2, -2), A vertex at (2, 2), None), + (A vertex at (2, 2), A vertex at (-2, 2), None), + (A vertex at (-2, 2), A vertex at (-2, -2), None)] + sage: cir_oriented = orient_circuit(cir); cir_oriented + (A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), + A vertex at (-2, 2), A vertex at (-2, -2)) + sage: cirinv = list(reversed([(c[1],c[0],c[2]) for c in cir])) + sage: cirinv + [(A vertex at (-2, -2), A vertex at (-2, 2), None), + (A vertex at (-2, 2), A vertex at (2, 2), None), + (A vertex at (2, 2), A vertex at (2, -2), None), + (A vertex at (2, -2), A vertex at (-2, -2), None)] + sage: orient_circuit(cirinv) == cir_oriented + True + sage: cir_oriented == orient_circuit(cir, convex=True) + True + sage: P0=[(1,1/2),(0,1),(1,1)]; P1=[(0,3/2),(-1,0)] + sage: Q=Polyhedron(P0).vertices() + sage: Q = [Q[2], Q[0], Q[1]] + [_ for _ in reversed(Polyhedron(P1).vertices())] + sage: Q + [A vertex at (1, 1/2), A vertex at (0, 1), A vertex at (1, 1), + A vertex at (0, 3/2), A vertex at (-1, 0)] + sage: E = Graph() + sage: for v, w in zip(Q, Q[1:] + [Q[0]]): + ....: E.add_edge((v, w)) + sage: cir = orient_circuit(E.eulerian_circuit(), precision=1, verbose=True) + 2 + sage: cir + (A vertex at (1, 1/2), A vertex at (0, 1), A vertex at (1, 1), + A vertex at (0, 3/2), A vertex at (-1, 0), A vertex at (1, 1/2)) """ - Return the bounded segments of the Voronoi diagram of the given points. + vectors = [v[1].vector() - v[0].vector() for v in circuit] + circuit_vertex = (circuit[0][0],) + tuple(e[1] for e in circuit) + circuit_vertex = tuple(circuit_vertex) + if convex: + pr = matrix([vectors[0], vectors[1]]).determinant() + if pr > 0: + # return circuit + return circuit_vertex + elif pr < 0: + # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) + return tuple(reversed(circuit_vertex)) + prec = precision + while True: + CIF = ComplexIntervalField(prec) + totalangle = sum((CIF(*vectors[i]) / CIF(*vectors[i - 1])).argument() + for i in range(len(vectors))) + if totalangle < 0: + # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) + return tuple(reversed(circuit_vertex)) + if totalangle > 0: + # return circuit + return circuit_vertex + prec *= 2 + if verbose: + print(prec) + + +def voronoi_cells(V): + r""" + Compute the graph, the boundary graph, a base point, a positive orientation + of the boundary graph, and the dual graph of a corrected Voronoi diagram. INPUT: - - ``points`` -- a list of complex points + - ``V`` -- a corrected Voronoi diagram OUTPUT: - A list of pairs ``(p1, p2)``, where ``p1`` and ``p2`` are the - endpoints of the segments in the Voronoi diagram. + - ``G`` -- the graph of the 1-skeleton of ``V`` + - ``E`` -- the subgraph of the boundary + - ``p`` -- a vertex in ``E`` + - ``EC`` -- a list of vertices (representing a counterclockwise orientation + of ``E``) with identical first and last elements) + - ``DG`` -- the dual graph of ``V``, where the vertices are labelled + by the compact regions of ``V`` and the edges by their dual edges. EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import discrim, segments - sage: R. = 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)] + sage: from sage.schemes.curves.zariski_vankampen import corrected_voronoi_diagram, voronoi_cells + sage: points = (2, I, 0.000001, 0, 0.000001*I) + sage: V = corrected_voronoi_diagram(points) + sage: G, E, p, EC, DG = voronoi_cells(V) + sage: Gv = G.vertices(sort=True) + sage: Ge = G.edges(sort=True) + sage: len(Gv), len(Ge) + (12, 16) + sage: Ev = E.vertices(sort=True); Ev + [A vertex at (-4, 4), + A vertex at (-49000001/14000000, 1000001/2000000), + A vertex at (-7/2, -7/2), + A vertex at (-7/2, 1/2000000), + A vertex at (1/2000000, -7/2), + A vertex at (2000001/2000000, -24500001/7000000), + A vertex at (11/4, 4), + A vertex at (9/2, -9/2), + A vertex at (9/2, 9/2)] + sage: Ev.index(p) + 7 + sage: EC + (A vertex at (9/2, -9/2), + A vertex at (9/2, 9/2), + A vertex at (11/4, 4), + A vertex at (-4, 4), + A vertex at (-49000001/14000000, 1000001/2000000), + A vertex at (-7/2, 1/2000000), + A vertex at (-7/2, -7/2), + A vertex at (1/2000000, -7/2), + A vertex at (2000001/2000000, -24500001/7000000), + A vertex at (9/2, -9/2)) + sage: len(DG.vertices(sort=True)), len(DG.edges(sort=True)) + (5, 7) + sage: edg = DG.edges(sort=True)[0]; edg + ((0, + (A vertex at (9/2, -9/2), + A vertex at (9/2, 9/2), + A vertex at (11/4, 4), + A vertex at (2000001/2000000, 500001/1000000), + A vertex at (2000001/2000000, -24500001/7000000), + A vertex at (9/2, -9/2))), + (1, + (A vertex at (-49000001/14000000, 1000001/2000000), + A vertex at (1000001/2000000, 1000001/2000000), + A vertex at (2000001/2000000, 500001/1000000), + A vertex at (11/4, 4), + A vertex at (-4, 4), + A vertex at (-49000001/14000000, 1000001/2000000))), + (A vertex at (2000001/2000000, 500001/1000000), A vertex at (11/4, 4), None)) + sage: edg[-1] in Ge + True """ - V = corrected_voronoi_diagram(tuple(points)) - res = set() - for region in V.regions().values(): - if region.rays(): - continue - for s in region.facets(): - 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] + compact_regions = [_ for _ in V.regions().values() if _.is_compact()] + non_compact_regions = [_ for _ in V.regions().values() if not _.is_compact()] + G = Graph([u.vertices() for v in compact_regions for u in v.faces(1)], format='list_of_edges') + E = Graph([u.vertices() for v in non_compact_regions for u in v.faces(1) if u.is_compact()], format='list_of_edges') + p = next(E.vertex_iterator()) + EC = orient_circuit(E.eulerian_circuit()) + # EC = [EC0[0][0]] + [e[1] for e in EC0] + DG = Graph() + for i, reg in enumerate(compact_regions): + Greg0 = orient_circuit(reg.graph().eulerian_circuit(), convex=True) + # Greg = (Greg0[0][0],) + tuple(e[1] for e in Greg0) + DG.add_vertex((i, Greg0)) + for e in G.edges(sort=True): + a, b = e[:2] + regs = [v for v in DG.vertices(sort=True) if a in v[1] and b in v[1]] + if len(regs) == 2: + DG.add_edge(regs[0], regs[1], e) + return (G, E, p, EC, DG) def followstrand(f, factors, x0, x1, y0a, prec=53): @@ -323,18 +492,19 @@ def followstrand(f, factors, x0, x1, y0a, prec=53): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import followstrand # optional - sirocco - sage: R. = QQ[] + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import followstrand + sage: R. = 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 + sage: followstrand(f, [], x0, x1, -1.0) # abs tol 1e-15 [(0.0, -1.0, 0.0), (0.7500000000000001, -1.015090921153253, -0.24752813818386948), (1.0, -1.026166099551513, -0.32768940253604323)] - sage: fup = f.subs({y:y-1/10}) - sage: fdown = f.subs({y:y+1/10}) - sage: followstrand(f, [fup, fdown], x0, x1, -1.0) # optional - sirocco # abs tol 1e-15 + sage: fup = f.subs({y: y - 1/10}) + sage: fdown = f.subs({y: y + 1/10}) + sage: followstrand(f, [fup, fdown], x0, x1, -1.0) # abs tol 1e-15 [(0.0, -1.0, 0.0), (0.5303300858899107, -1.0076747107983448, -0.17588022709184917), (0.7651655429449553, -1.015686131039112, -0.25243563967299404), @@ -401,7 +571,7 @@ def newton(f, x0, i0): INPUT: - - ``f``` -- a univariate polynomial + - ``f`` -- a univariate polynomial - ``x0`` -- a number - ``I0`` -- an interval @@ -427,6 +597,56 @@ def newton(f, x0, i0): return x0 - f(x0) / f.derivative()(i0) +def fieldI(field): + r""" + Return the (either double or trivial) extension of a number field which contains ``I``. + + INPUT: + + - ``field`` -- a number field with an embedding in ``QQbar``. + + OUTPUT: + + The extension ``F`` of ``field`` containing ``I`` with an embedding in ``QQbar``. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import fieldI + sage: p = QQ[x](x^5 + 2 * x + 1) + sage: a0 = p.roots(QQbar, multiplicities=False)[0] + sage: F0. = NumberField(p, embedding=a0) + sage: fieldI(F0) + Number Field in b with defining polynomial + x^10 + 5*x^8 + 14*x^6 - 2*x^5 - 10*x^4 + 20*x^3 - 11*x^2 - 14*x + 10 + with b = 0.4863890359345430? + 1.000000000000000?*I + + If ``I`` is already in the field, the result is the field itself:: + + sage: from sage.schemes.curves.zariski_vankampen import fieldI + sage: p = QQ[x](x^4 + 1) + sage: a0 = p.roots(QQbar, multiplicities=False)[0] + sage: F0. = NumberField(p, embedding=a0) + sage: F1 = fieldI(F0) + sage: F0 == F1 + True + """ + I0 = QQbar.gen() + if I0 in field: + return field + field_a = field[I0] + field_b = field_a.absolute_field('b0') + b0 = field_b.gen() + q = b0.minpoly() + qembd = field_b.embeddings(QQbar) + for h1 in qembd: + b1 = h1(b0) + b2 = h1(field_b(field_a.gen(0))) + b3 = field.gen(0) + F1 = NumberField(q, 'b', embedding=b1) + if b3 in F1 and b2.imag() > 0: + return F1 + + @parallel def roots_interval(f, x0): """ @@ -436,10 +656,11 @@ def roots_interval(f, x0): INPUT: - ``f`` -- a bivariate squarefree polynomial - - ``x0`` -- a value where the first coordinate will be fixed + - ``x0`` -- a Gauss rational number corresponding to the first coordinate 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. + approximate roots of `f(x_0, y)` correspond to the same exact root, where + `f` is the product of the polynomials in `flist`. The result is given as a dictionary, where the keys are approximations to the roots with rational real and imaginary @@ -447,9 +668,11 @@ def roots_interval(f, x0): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import roots_interval - sage: R. = QQ[] + sage: from sage.schemes.curves.zariski_vankampen import roots_interval, fieldI + sage: R. = QQ[] + sage: K = fieldI(QQ) sage: f = y^3 - x^2 + sage: f = f.change_ring(K) sage: ri = roots_interval(f, 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, @@ -469,9 +692,9 @@ def roots_interval(f, x0): -0.933012701892219 + 1.29903810567666*I, -0.0669872981077806 + 0.433012701892219*I)] """ + F1 = f.base_ring() x, y = f.parent().gens() - I = QQbar.gen() - fx = QQbar[y](f.subs({x: QQ(x0.real()) + I * QQ(x0.imag())})) + fx = F1[y](f.subs({x: F1(x0)})) roots = fx.roots(QQbar, multiplicities=False) result = {} for i, r in enumerate(roots): @@ -503,9 +726,11 @@ def roots_interval_cached(f, x0): TESTS:: - sage: from sage.schemes.curves.zariski_vankampen import roots_interval, roots_interval_cached, roots_interval_cache - sage: R. = QQ[] + sage: from sage.schemes.curves.zariski_vankampen import roots_interval, roots_interval_cached, roots_interval_cache, fieldI + sage: R. = QQ[] + sage: K = fieldI(QQ) sage: f = y^3 - x^2 + sage: f = f.change_ring(K) sage: (f, 1) in roots_interval_cache False sage: ri = roots_interval_cached(f, 1) @@ -532,13 +757,15 @@ def populate_roots_interval_cache(inputs): INPUT: - - ``inputs`` -- a list of tuples (f, x0) + - ``inputs`` -- a list of tuples ``(f, x0)`` EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import populate_roots_interval_cache, roots_interval_cache + sage: from sage.schemes.curves.zariski_vankampen import populate_roots_interval_cache, roots_interval_cache, fieldI sage: R. = QQ[] + sage: K=fieldI(QQ) sage: f = y^5 - x^2 + sage: f = f.change_ring(K) sage: (f, 3) in roots_interval_cache False sage: populate_roots_interval_cache([(f, 3)]) @@ -554,22 +781,30 @@ def populate_roots_interval_cache(inputs): """ global roots_interval_cache tocompute = [inp for inp in inputs if inp not in roots_interval_cache] - result = roots_interval(tocompute) - for r in result: - roots_interval_cache[r[0][0]] = r[1] + problem_par = True + while problem_par: # hack to deal with random fails in parallelization + try: + result = roots_interval(tocompute) + for r in result: + roots_interval_cache[r[0][0]] = r[1] + problem_par = False + except TypeError: + pass @parallel -def braid_in_segment(g, x0, x1): +def braid_in_segment(glist, x0, x1, precision=dict()): """ 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 + - ``glist`` -- a tuple of polynomials in two variables + - ``x0`` -- a Gauss rational + - ``x1`` -- a Gauss rational + - ``precision`` -- a dictionary (default: `dict()`) which assigns a number + precision bits to each element of ``glist`` OUTPUT: @@ -577,182 +812,135 @@ def braid_in_segment(g, x0, x1): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # optional - sirocco - sage: R. = QQ[] + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment, fieldI + sage: R. = QQ[] + sage: K = fieldI(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 + sage: f = f.change_ring(K) + sage: x0 = 1 + sage: x1 = 1 + I / 2 + sage: braid_in_segment(tuple(_[0] for _ in f.factor()), x0, x1) s1 TESTS: Check that :trac:`26503` is fixed:: + sage: # needs sage.rings.real_mpfr sage.symbolic sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0] sage: Kw. = NumberField(wp.minpoly(), embedding=wp) sage: R. = 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: f = y * (y + z) * x * (x - 1) * (x - y) * (x + z * y - 1) * (x + z * y + wp) + sage: from sage.schemes.curves.zariski_vankampen import fieldI, braid_in_segment + sage: Kw1 = fieldI(Kw) + sage: g = f.subs({x: x + 2 * y}) + sage: g = g.change_ring(Kw1) 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 + sage: p1a = CC(p1) + sage: p1b = QQ(p1a.real()) + I*QQ(p1a.imag()) + sage: p2 = QQbar(1/2 + sqrt(-1/3)/2) + sage: p2a = CC(p2) + sage: p2b = QQ(p2a.real()) + I*QQ(p2a.imag()) + sage: glist = tuple([_[0] for _ in g.factor()]) + sage: B = braid_in_segment(glist, p1b, p2b); B # optional - sirocco s5*s3^-1 """ - _, y = g.value().parent().gens() - I = QQbar.gen() - X0 = QQ(x0.real()) + I * QQ(x0.imag()) - X1 = QQ(x1.real()) + I * QQ(x1.imag()) + precision1 = {_: precision[_] for _ in precision.keys()} + g = prod(glist) + F1 = g.base_ring() + x, y = g.parent().gens() + X0 = F1(x0) + X1 = F1(x1) intervals = {} - precision = {} + if not precision1: # new + precision1 = {f: 53 for f in glist} # new y0s = [] - for f, _ in g: + for f in glist: if f.variables() == (y,): - F0 = QQbar[y](f.base_ring()[y](f)) + f0 = F1[y](f) else: - F0 = QQbar[y](f(X0, y)) - y0sf = F0.roots(multiplicities=False) + f0 = F1[y](f.subs({x: X0})) + y0sf = f0.roots(QQbar, multiplicities=False) y0s += list(y0sf) - precision[f] = 53 while True: - CIFp = ComplexIntervalField(precision[f]) + CIFp = ComplexIntervalField(precision1[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]]] + precision1[f] *= 2 + strands = [] + for f in glist: + for i in intervals[f]: + aux = followstrand(f, [p for p in glist if p != f], x0, x1, i.center(), precision1[f]) + strands.append(aux) 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) + initialintervals = roots_interval_cached(g, X0) + finalintervals = roots_interval_cached(g, X1) + I1 = QQbar.gen() for cs in complexstrands: - ip = cs[0][1] + I * cs[0][2] - fp = cs[-1][1] + I * cs[-1][2] + ip = cs[0][1] + I1 * cs[0][2] + fp = cs[-1][1] + I1 * 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") + if matched != 1: + precision1 = {f: precision1[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision=precision1) # new + 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 matches more than one root") + if matched != 1: + precision1 = {f: precision1[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision=precision1) # new initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) return initialbraid * centralbraid * finalbraid -def orient_circuit(circuit): - r""" - Reverse a circuit if it goes clockwise; otherwise leave it unchanged. - - INPUT: - - - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given - by a list of edges - - OUTPUT: - - The same circuit if it goes counterclockwise, and its reverse otherwise - - EXAMPLES:: - - sage: from sage.schemes.curves.zariski_vankampen import orient_circuit - sage: points = [(-4, 0), (4, 0), (0, 4), (0, -4), (0, 0)] - sage: V = VoronoiDiagram(points) - sage: E = Graph() - sage: for reg in V.regions().values(): - ....: if reg.rays() or reg.lines(): - ....: E = E.union(reg.vertex_graph()) - sage: E.vertices(sort=True) - [A vertex at (-2, -2), - A vertex at (-2, 2), - A vertex at (2, -2), - A vertex at (2, 2)] - sage: cir = E.eulerian_circuit() - sage: cir - [(A vertex at (-2, -2), A vertex at (2, -2), None), - (A vertex at (2, -2), A vertex at (2, 2), None), - (A vertex at (2, 2), A vertex at (-2, 2), None), - (A vertex at (-2, 2), A vertex at (-2, -2), None)] - sage: orient_circuit(cir) - [(A vertex at (-2, -2), A vertex at (2, -2), None), - (A vertex at (2, -2), A vertex at (2, 2), None), - (A vertex at (2, 2), A vertex at (-2, 2), None), - (A vertex at (-2, 2), A vertex at (-2, -2), None)] - sage: cirinv = list(reversed([(c[1],c[0],c[2]) for c in cir])) - sage: cirinv - [(A vertex at (-2, -2), A vertex at (-2, 2), None), - (A vertex at (-2, 2), A vertex at (2, 2), None), - (A vertex at (2, 2), A vertex at (2, -2), None), - (A vertex at (2, -2), A vertex at (-2, -2), None)] - sage: orient_circuit(cirinv) - [(A vertex at (-2, -2), A vertex at (2, -2), None), - (A vertex at (2, -2), A vertex at (2, 2), None), - (A vertex at (2, 2), A vertex at (-2, 2), None), - (A vertex at (-2, 2), A vertex at (-2, -2), None)] - - """ - prec = 53 - vectors = [v[1].vector() - v[0].vector() for v in circuit] - while True: - CIF = ComplexIntervalField(prec) - totalangle = sum((CIF(*vectors[i]) / CIF(*vectors[i - 1])).argument() - for i in range(len(vectors))) - if totalangle < 0: - return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) - if totalangle > 0: - return circuit - prec *= 2 - - -def geometric_basis(G, E, p): +def geometric_basis(G, E, EC0, p, dual_graph): r""" Return a geometric basis, based on a vertex. INPUT: - - ``G`` -- the graph of the bounded regions of a Voronoi Diagram + - ``G`` -- a graph with the bounded edges of a Voronoi Diagram + + - ``E`` -- a subgraph of ``G`` which is a cycle containing the bounded + edges touching an unbounded region of a Voronoi Diagram - - ``E`` -- the subgraph of ``G`` formed by the edges that touch - an unbounded region + - ``EC0`` -- A counterclockwise orientation of the vertices of ``E`` - ``p`` -- a vertex of ``E`` + - ``dual_graph`` -- a dual graph for a plane embedding of ``G`` such that + ``E`` is the boundary of the non-bounded component of the complement. + The edges are labelled as the dual edges and the vertices are labelled + by a tuple whose first element is the an integer for the position and the + second one is the cyclic ordered list of vertices in the region. + OUTPUT: A geometric basis. It is formed by a list of sequences of paths. - Each path is a list of vertices, that form a closed path in `G`, based at - `p`, that goes to a region, surrounds it, and comes back by the same - path it came. The concatenation of all these paths is equivalent to `E`. + Each path is a list of vertices, that form a closed path in ``G``, based at + ``p``, that goes to a region, surrounds it, and comes back by the same + path it came. The concatenation of all these paths is equivalent to ``E``. EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import geometric_basis + sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, voronoi_cells sage: points = [(-3,0),(3,0),(0,3),(0,-3)]+ [(0,0),(0,-1),(0,1),(1,0),(-1,0)] sage: V = VoronoiDiagram(points) - sage: G = Graph() - sage: for reg in V.regions().values(): - ....: G = G.union(reg.vertex_graph()) - sage: E = Graph() - sage: for reg in V.regions().values(): - ....: if reg.rays() or reg.lines(): - ....: E = E.union(reg.vertex_graph()) - sage: p = E.vertices(sort=True)[0] - sage: geometric_basis(G, E, p) + sage: G, E, p, EC, DG = voronoi_cells(V) + sage: geometric_basis(G, E, EC, p, DG) [[A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), @@ -789,75 +977,97 @@ def geometric_basis(G, E, p): A vertex at (-2, 2), A vertex at (-2, -2)]] """ - EC = [v[0] for v in orient_circuit(E.eulerian_circuit())] - i = EC.index(p) - EC = EC[i:] + EC[:i + 1] # A counterclockwise eulerian circuit on the boundary, based at p + i = EC0.index(p) + EC = EC0[i:-1] + EC0[:i + 1] # A counterclockwise eulerian circuit on the boundary, starting and ending at p if G.size() == E.size(): if E.is_cycle(): return [EC] - I = Graph() - for e in G.edges(sort=False): - if not E.has_edge(e): - I.add_edge(e) # interior graph - # treat the case where I is empty - if not I: - for v in E: - if len(E.neighbors(v)) > 2: - I.add_vertex(v) - + InternalEdges = [_ for _ in G.edges(sort=True) if _ not in E.edges(sort=True)] + InternalVertices = [v for e in InternalEdges for v in e[:2]] + Internal = G.subgraph(vertices=InternalVertices, edges=InternalEdges) for i, ECi in enumerate(EC): # q and r are the points we will cut through - - if EC[i] in I: - q = EC[i] - connecting_path = EC[:i] - break - if EC[-i] in I: - q = EC[-i] - connecting_path = list(reversed(EC[-i:])) - break - I_cc_q = set(I.connected_component_containing_vertex(q, sort=False)) - distancequotients = [(E.distance(q, v)**2 / I.distance(q, v), v) for v in E - if v in I_cc_q and not v == q] + if ECi in Internal: + EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi, sort=True) and v != ECi] + if len(EI) > 0: + q = ECi + connecting_path = list(EC[:i]) + break + if EC[-i] in Internal: + EI = [v for v in E if v in Internal.connected_component_containing_vertex(EC[-i], sort=True) and v != EC[-i]] + if len(EI) > 0: + q = EC[-i] + connecting_path = list(reversed(EC[-i:])) + break + # Precompute distances from q in E and I + E_dist_q = E.shortest_path_lengths(q) + I_dist_q = Internal.shortest_path_lengths(q) + distancequotients = [(E_dist_q[v]**2 / I_dist_q[v], v) for v in EI] + # distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in EI] r = max(distancequotients)[1] - cutpath = I.shortest_path(q, r) - Gcut = copy(G) + cutpath = Internal.shortest_path(q, r) + for i, v in enumerate(cutpath): + if i > 0 and v in EC: + r = v + cutpath = cutpath[:i+1] + break + qi = EC.index(q) + ri = EC.index(r) Ecut = copy(E) Ecut.delete_vertices([q, r]) - Gcut.delete_vertices(cutpath) - # I think this cannot happen, but just in case, we check it to raise - # an error instead of giving a wrong answer - if Gcut.connected_components_number() != 2: - raise ValueError("unable to compute a correct path") - G1, G2 = Gcut.connected_components_subgraphs() - - for v in cutpath: - neighs = G.neighbors(v) - for n in neighs: - if n in G1 or n in cutpath: - G1.add_edge(v, n, None) - if n in G2 or n in cutpath: - G2.add_edge(v, n, None) - - if EC[EC.index(q) + 1] in G2: - G1, G2 = G2, G1 - - E1, E2 = Ecut.connected_components_subgraphs() - if EC[EC.index(q) + 1] in E2: - E1, E2 = E2, E1 + subgraphs = Ecut.connected_components_subgraphs() + if len(subgraphs) == 2: + E1, E2 = subgraphs + if EC[qi + 1] in E2: + E1, E2 = E2, E1 + elif len(subgraphs) == 1: + E1 = subgraphs[0] + E2 = Graph() + E2.add_edge(q, r, None) + if r == EC[qi + 1]: + E1, E2 = E2, E1 + for v in [q, r]: + for n in E.neighbor_iterator(v): + if n in E1 and n not in (q, r): + E1.add_edge(v, n, None) + if n in E2 and n not in (q, r): + E2.add_edge(v, n, None) for i in range(len(cutpath) - 1): E1.add_edge(cutpath[i], cutpath[i + 1], None) E2.add_edge(cutpath[i], cutpath[i + 1], None) + Gd = copy(dual_graph) + to_delete = [e for e in Gd.edges(sort=True) if e[2][0] in cutpath and e[2][1] in cutpath] + Gd.delete_edges(to_delete) + Gd1, Gd2 = Gd.connected_components_subgraphs() + edges_2 = [] + vertices_2 = [] + for reg in Gd2.vertices(sort=True): + vertices_2 += reg[1][:-1] + reg_circuit = reg[1] + edges_2 += [(v1, reg_circuit[i + 1]) for i, v1 in enumerate(reg_circuit[:-1])] + edges_2 += [(v1, reg_circuit[i - 1]) for i, v1 in enumerate(reg_circuit[1:])] + G2 = G.subgraph(vertices=vertices_2, edges=edges_2) + edges_1 = [] + vertices_1 = [] + for reg in Gd1.vertices(sort=True): + vertices_1 += reg[1] + reg_circuit = reg[1] + (reg[1][0],) + edges_1 += [(v1, reg_circuit[i + 1]) for i, v1 in enumerate(reg_circuit[:-1])] + edges_1 += [(v1, reg_circuit[i - 1]) for i, v1 in enumerate(reg_circuit[1:])] + G1 = G.subgraph(vertices=vertices_1, edges=edges_1) + if EC[qi + 1] in G2: + G1, G2 = G2, G1 + Gd1, Gd2 = Gd2, Gd1 - for v in [q, r]: - for n in E.neighbors(v): - if n in E1: - E1.add_edge(v, n, None) - if n in E2: - E2.add_edge(v, n, None) + if qi < ri: + EC1 = [EC[j] for j in range(qi, ri)] + list(reversed(cutpath)) + EC2 = cutpath + list(EC[ri + 1: -1] + EC[: qi + 1]) + else: + EC1 = list(EC[qi:] + EC[1:ri]) + list(reversed(cutpath)) + EC2 = cutpath + list(EC[ri + 1:qi + 1]) - gb1 = geometric_basis(G1, E1, q) - gb2 = geometric_basis(G2, E2, q) + gb1 = geometric_basis(G1, E1, EC1, q, Gd1) + gb2 = geometric_basis(G2, E2, EC2, q, Gd2) reverse_connecting = list(reversed(connecting_path)) resul = [connecting_path + path + reverse_connecting @@ -875,82 +1085,165 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f): +def strand_components(f, flist, p1): + r""" + Compute only the assignment from strands to elements of ``flist``. + + INPUT: + + - ``f`` -- a reduced polynomial with two variables, over a number field + with an embedding in the complex numbers + + - ``flist`` -- a list of polynomials with two variables whose + product equals ``f`` + + - ``p1`` -- a Gauss rational + + OUTPUT: + + - A list and a dictionary. The first one is an ordered list of pairs + consisting of ``(z,i)`` where ``z`` is a root of ``f(p_1,y)`` and `i` is the position + of the polynomial in the list whose root is ``z``. The second one attaches + a number `i` (strand) to a number `j` (a polynomial in the list). + + EXAMPLES:: + + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import strand_components + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3 * y - 5] + sage: strand_components(prod(flist), flist, 1) + ([(-0.500000000000000? - 0.866025403784439?*I, 0), + (-0.500000000000000? + 0.866025403784439?*I, 0), + (1, 0), (1.333333333333334?, 1)], {0: 0, 1: 0, 2: 0, 3: 1}) + """ + x, y = f.parent().gens() + F = flist[0].base_ring() + strands = {} + roots_base = [] + for i, h in enumerate(flist): + h0 = h.subs({x: p1}) + h1 = F[y](h0) + rt = h1.roots(QQbar, multiplicities=False) + roots_base += [(_, i) for _ in rt] + roots_base.sort() + strands = {i: par[1] for i, par in enumerate(roots_base)} # quitar +1 despues de revision + return (roots_base, strands) + + +def braid_monodromy(f, arrangement=()): r""" - Compute the braid monodromy of a projection of the curve defined by a polynomial. + 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 + - ``arrangement`` -- an optional tuple of polynomials whose product + equals ``f``. + 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``. + A list of braids and a dictionary. + The braids correspond to paths based in the same point; + each of these paths is the conjugated of a loop around one of the points + in the discriminant of the projection of ``f``. The dictionary assigns each + strand to the index of the corresponding factor in ``arrangement``. .. 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. + 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. + + .. TODO:: + + Create a class ``arrangements_of_curves`` with a ``braid_monodromy`` + method; it can be also a method for affine line arrangements. EXAMPLES:: + sage: # optional - sirocco sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy - sage: R. = 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] + sage: R. = QQ[] + sage: f = (x^2 - y^3) * (x + 3*y - 5) + sage: bm = braid_monodromy(f); bm + ([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], {0: 0, 1: 0, 2: 0, 3: 0}) + sage: flist = (x^2 - y^3, x + 3*y - 5) + sage: bm1 = braid_monodromy(f, arrangement=flist) + sage: bm1[0] == bm[0] + True + sage: bm1[1] + {0: 0, 1: 1, 2: 0, 3: 0} + sage: braid_monodromy(R(1)) + ([], {}) + sage: braid_monodromy(x*y^2 - 1) + ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0}) """ global roots_interval_cache + F = fieldI(f.base_ring()) + I1 = F(QQbar.gen()) + f = f.change_ring(F) + if arrangement == (): + arrangement1 = (f,) + else: + arrangement1 = tuple(_.change_ring(F) for _ in arrangement) x, y = f.parent().gens() - F = f.base_ring() - g = f.radical() + glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor()) + g = f.parent()(prod(glist)) 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) + arrangement1 = tuple(f1.subs({x: x + y}) for f1 in arrangement1) + glist = tuple(f1.subs({x: x + y}) for f1 in glist) + if d > 0: + disc = discrim(glist) + else: + disc = [] + if len(disc) == 0: + result = [] + p1 = F(0) + roots_base, strands = strand_components(g, arrangement1, p1) + return ([], strands) 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) + G, E, p, EC, DG = voronoi_cells(V) + p0 = (p[0], p[1]) + p1 = p0[0] + I1 * p0[1] + roots_base, strands = strand_components(g, arrangement1, p1) + geombasis = geometric_basis(G, E, EC, p, DG) 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] + I0 = QQbar.gen() + segs = [(a[0] + I0 * a[1], b[0] + I0 * b[1]) for a, b in segs] vertices = list(set(flatten(segs))) - tocacheverts = [(g, v) for v in vertices] + tocacheverts = tuple([(g, v) for v in vertices]) populate_roots_interval_cache(tocacheverts) - gfac = g.factor() - try: - braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) - for seg in segs])) - except ChildProcessError: # hack to deal with random fails first time - braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) - for seg in segs])) - segsbraids = {} - 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() + end_braid_computation = False + while not end_braid_computation: + try: + braidscomputed = (braid_in_segment([(glist, seg[0], seg[1]) + for seg in segs])) + segsbraids = {} + 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() + end_braid_computation = True + except ChildProcessError: # hack to deal with random fails first time + pass + B = BraidGroup(d) result = [] for path in geombasis: braidpath = B.one() @@ -959,10 +1252,258 @@ def braid_monodromy(f): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - return result + return (result, strands) + + +def conjugate_positive_form(braid): + r""" + For a ``braid`` which is conjugate to a product of *disjoint* positive + braids a list of such decompositions is given. + + INPUT: + + - ``braid`` -- a braid ``\sigma``. + + OUTPUT: + + A list of `r` lists. Each such list is another list with two elements, a + positive braid `\alpha_i` and a list of permutation braids + `\gamma_{1}^{i},\dots,\gamma_{r}^{n_i}` such that if + `\gamma_i=\prod_{j=1}^{n_i} \gamma_j^i` then the braids + `\tau_i=\gamma_i\alpha_i\gamma_i^{-1}` pairwise commute + and `\alpha=\prod_{i=1}^{r} \tau_i`. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import conjugate_positive_form + sage: B = BraidGroup(4) + sage: t = B((1, 3, 2, -3, 1, 1)) + sage: conjugate_positive_form(t) + [[(s1*s0)^2, [s2]]] + sage: B = BraidGroup(5) + sage: t = B((1, 2, 3, 4, -1, -2, 3, 3, 2, -4)) + sage: L = conjugate_positive_form(t); L + [[s1^2, [s3*s2]], [s1*s2, [s0]]] + sage: s = B.one() + sage: for a, l in L: + ....: b = prod(l) + ....: s *= b * a / b + sage: s == t + True + sage: s1 = B.gen(1)^3 + sage: conjugate_positive_form(s1) + [[s1^3, []]] + """ + B = braid.parent() + d = B.strands() + braid1 = braid.super_summit_set()[0] + L1 = braid1.Tietze() + sg0 = braid.conjugating_braid(braid1) + gns = set(L1) + cuts = [j for j in range(d + 1) if j not in gns] + blocks = [] + for i in range(len(cuts) - 1): + block = [j for j in L1 if j > cuts[i] and j < cuts[i + 1]] + if len(block) > 0: + blocks.append(block) + shorts = [] + for a in blocks: + A = B(a).super_summit_set() + res = None + for tau in A: + sg = (sg0 * B(a) / sg0).conjugating_braid(tau) + A1 = rightnormalform(sg) + par = A1[-1][0] % 2 + A1 = [B(a) for a in A1[:-1]] + if len(A1) == 0: + b = B.one() + else: + b = prod(A1) + b1 = len(b.Tietze()) / (len(A1) + 1) + if res is None or b1 < res[3]: + res = [tau, A1, par, b1] + if res[2] == 1: + r0 = res[0].Tietze() + res[0] = B([i.sign() * (d - abs(i)) for i in r0]) + res0 = res[:2] + shorts.append(res0) + return shorts -def fundamental_group(f, simplified=True, projective=False): +@parallel +def conjugate_positive_form_p(braid): + return conjugate_positive_form(braid) + + +def braid2rels(L): + r""" + Return a minimal set of relations of the group + ``F / [(b * F([j])) / F([j]) for j in (1..d)]`` where ``F = FreeGroup(d)`` + and ``b`` is a conjugate of a positive braid . One starts from the + non-trivial relations determined by the positive braid and transform + them in relations determined by ``b``. + + INPUT: + + - ``L`` -- a tuple whose first element is a positive braid and the second + element is a list of permutation braids. + + OUTPUT: + + A list of Tietze words for a minimal set of relations of + ``F / [(g * b) / g for g in F.gens()]``. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import braid2rels + sage: B. = BraidGroup(4) + sage: L = ((s1*s0)^2, [s2]) + sage: braid2rels(L) + [(4, 1, -2, -1), (2, -4, -2, 1)] + """ + br = L[0] + L1 = L[1] + B = br.parent() + d = B.strands() + F = FreeGroup(d) + T = br.Tietze() + T1 = set(T) + m = len(T1) + 1 + k = min(T1) - 1 + B0 = BraidGroup(m) + F0 = FreeGroup(m) + br0 = B0([_-k for _ in T]) + br0_left = leftnormalform(br0) + q, r = ZZ(br0_left[0][0]).quo_rem(2) + br1 = B0.delta()**r * B0(prod(B0(_) for _ in br0_left[1:])) + cox = prod(F0.gens()) + U0 = [cox**q * (f0 * br1) / cox**q / f0 for f0 in F0.gens()[:-1]] + U = [tuple(sign(k1)*(abs(k1) + k) for k1 in _.Tietze()) for _ in U0] + pasos = [B.one()] + [_ for _ in reversed(L1)] + for C in pasos: + U = [(F(a) * C.inverse()).Tietze() for a in U] + ga = F / U + P = ga.gap().PresentationFpGroup() + dic = P.TzOptions().sage() + dic['protected'] = d + dic['printLevel'] = 0 + P.SetTzOptions(dic) + P.TzGoGo() + P.TzGoGo() + gb = wrap_FpGroup(P.FpGroupPresentation()) + U = [_.Tietze() for _ in gb.relations()] + return U + + +@parallel +def braid2rels_p(L): + return braid2rels(L) + + +@parallel +def relation(x, b): + return x * b / x + + +def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projective=False, puiseux=False, vertical=[]): + r""" + Return a presentation of the fundamental group computed from + a braid monodromy. + + INPUT: + + - ``bm`` -- a list of braids + + - ``degree`` -- integer (default: ``None``); only needed if the braid + monodromy is an empty list. + + - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the + presentation will be simplified (see below) + + - ``projective`` -- boolean (default: ``False``); if set to ``True``, + the fundamental group of the complement of the projective completion + of the curve will be computed, otherwise, the fundamental group of + the complement in the affine plane will be computed + + - ``puiseux`` -- boolean (default: ``False``); if set to ``True``, + ``simplified`` is set to ``False``, and + a presentation of the fundamental group with the homotopy type + of the complement of the affine curve will be computed, adding + one relation if ``projective`` is set to ``True``. + + - ``vertical`` -- list of integers (default: ``[]``); the indices in + ``[1..r]`` of the braids that surround a vertical line + + If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is + ``True``, a Zariski-VanKampen presentation is returned. + + OUTPUT: + + A presentation of the fundamental group of the complement of the + union of the curve with some vertical lines from its braid monodromy. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group_from_braid_mon + sage: B. = BraidGroup(4) + sage: bm = [s1*s2*s0*s1*s0^-1*s1^-1*s0^-1, + ....: s0*s1^2*s0*s2*s1*(s0^-1*s1^-1)^2*s0^-1, + ....: (s0*s1)^2] + sage: g = fundamental_group_from_braid_mon(bm, projective=True); g + Finitely presented group < x0, x1 | x1*x0^2*x1, x0^-1*x1^-1*x0^-1*x1*x0^-1*x1^-1 > + sage: print (g.order(), g.abelian_invariants()) + 12 (4,) + sage: B2 = BraidGroup(2) + sage: bm = [B2(3 * [1])] + sage: g = fundamental_group_from_braid_mon(bm, vertical=[1]); g + Finitely presented group < x0, x1, x2 | x2*x0*x1*x2^-1*x1^-1*x0^-1, + x2*x0*x1*x0*x1^-1*x0^-1*x2^-1*x1^-1 > + sage: fundamental_group_from_braid_mon([]) is None # optional - sirocco + True + sage: fundamental_group_from_braid_mon([], degree=2) # optional - sirocco + Finitely presented group < x0, x1 | > + """ + vertical0 = sorted(vertical) + v = len(vertical0) + if bm == []: + d = degree + else: + d = bm[0].parent().strands() + if d is None: + return None + F = FreeGroup(d) + Fv = FreeGroup(d + v) + bmh = [br for j, br in enumerate(bm) if j + 1 not in vertical0] + if not puiseux: + relations_h = (relation([(x, b) for x in F.gens() for b in bmh])) + rel_h = [r[1] for r in relations_h] + simplified0 = simplified + else: + simplified0 = False + conjugate_desc = conjugate_positive_form_p(bmh) + trenzas_desc = [b1[-1] for b1 in conjugate_desc] + trenzas_desc_1 = flatten(trenzas_desc, max_level=1) + relations_h = braid2rels_p(trenzas_desc_1) + rel_h = [r[1] for r in relations_h] + rel_h = flatten(rel_h, max_level=1) + rel_v = [] + for j, k in enumerate(vertical0): + l1 = d + j + 1 + br = bm[k - 1] + for gen in F.gens(): + j0 = gen.Tietze()[0] + rl = (l1,) + (gen * br).Tietze() + (-l1, -j0) + rel_v.append(rl) + rel = rel_h + rel_v + if projective: + rel.append(prod(Fv.gens()).Tietze()) + G = Fv / rel + if simplified0: + return G.simplified() + return G + + +def fundamental_group(f, simplified=True, projective=False, puiseux=False): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. @@ -980,8 +1521,13 @@ def fundamental_group(f, simplified=True, projective=False): of the curve will be computed, otherwise, the fundamental group of the complement in the affine plane will be computed - If ``simplified`` is ``False``, a Zariski-VanKampen presentation - is returned. + - ``puiseux`` -- boolean (default: ``False``); if set to ``True``, + a presentation of the fundamental group with the homotopy type + of the complement of the affine curve is computed, ``simplified`` is + ignored. One relation is added if ``projective`` is set to ``True``. + + If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is + ``True``, a Zariski-VanKampen presentation is returned. OUTPUT: @@ -990,54 +1536,200 @@ def fundamental_group(f, simplified=True, projective=False): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: R. = QQ[] + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy + sage: R. = QQ[] sage: f = x^2 + y^3 - sage: fundamental_group(f) # optional - sirocco - Finitely presented group < ... > - sage: fundamental_group(f, simplified=False) # optional - sirocco - Finitely presented group < ... > + sage: fundamental_group(f) + Finitely presented group < x1, x2 | x1*x2*x1^-1*x2^-1*x1^-1*x2 > + sage: fundamental_group(f, simplified=False).sorted_presentation() + Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x0*x1, + x2^-1*x0*x1*x0^-1, + x1^-1*x0^-1*x1^-1*x0*x1*x0 > + sage: fundamental_group(f, projective=True) + Finitely presented group < x0 | x0^3 > + sage: fundamental_group(f).sorted_presentation() + Finitely presented group < x0, x1 | x1^-1*x0^-1*x1^-1*x0*x1*x0 > :: - sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: R. = QQ[] + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group + sage: R. = QQ[] sage: f = y^3 + x^3 - sage: fundamental_group(f) # optional - sirocco - Finitely presented group < ... > + sage: fundamental_group(f) + Finitely presented group < x0, x1, x2 | x0*x1*x2*x0^-1*x2^-1*x1^-1, x2*x0*x1*x2^-1*x1^-1*x0^-1 > It is also possible to have coefficients in a number field with a fixed embedding in `\QQbar`:: - sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: zeta = QQbar['x']('x^2+x+1').roots(multiplicities=False)[0] + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group + sage: zeta = QQbar['x']('x^2 + x+ 1').roots(multiplicities=False)[0] sage: zeta -0.50000000000000000? - 0.866025403784439?*I sage: F = NumberField(zeta.minpoly(), 'zeta', embedding=zeta) sage: F.inject_variables() Defining zeta - sage: R. = F[] - sage: f = y^3 + x^3 +zeta *x + 1 - sage: fundamental_group(f) # optional - sirocco + sage: R. = F[] + sage: f = y^3 + x^3 + zeta * x + 1 + sage: fundamental_group(f) Finitely presented group < x0 | > + + We compute the fundamental group of the complement of a quartic using the ``puiseux`` option:: + + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group + sage: R. = QQ[] + sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) + sage: g = fundamental_group(f, puiseux=True); g.sorted_presentation() + Finitely presented group + < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1*x2, + x3^-1*x2^-1*x1*x2, x2^-1*x1^-1*x0^-1*x1*x2*x1, x2^-1*x0 > + sage: g.simplified().sorted_presentation() + Finitely presented group < x0, x1 | x1^-2*x0^2, (x1^-1*x0)^3 > + sage: g = fundamental_group(f, puiseux=True, projective=True) + sage: g.order(), g.abelian_invariants() + (12, (4,)) + sage: fundamental_group(y * (y - 1)) + Finitely presented group < x0, x1 | > """ g = f + x, y = g.parent().gens() + F = g.parent().base_ring() + d = g.degree(y) + while not g.coefficient(y**d) in F: + g = g.subs({x: x + y}) + d = g.degree(y) if projective: - x, y = g.parent().gens() while g.degree(y) < g.degree(): g = g.subs({x: x + y}) - bm = braid_monodromy(g) - n = bm[0].parent().strands() - F = FreeGroup(n) + bm = braid_monodromy(g)[0] + if bm == []: + d = g.degree(y) + else: + d = bm[0].parent().strands() + return fundamental_group_from_braid_mon(bm, degree=d, simplified=simplified, projective=projective, puiseux=puiseux) - @parallel - def relation(x, b): - return x * b / x - relations = (relation([(x, b) for x in F.gens() for b in bm])) - R = [r[1] for r in relations] + +def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False): + r""" + Compute the fundamental group of the complement of a curve + defined by a list of polynomials with the extra information + about the correspondence of the generators + and meridians of the elements of the list. + + INPUT: + + - ``flist`` -- a tuple of polynomial with two variables, over a number + field with an embedding in the complex numbers + + - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the + presentation will be simplified (see below) + + - ``projective`` -- boolean (default: ``False``); if set to ``True``, + the fundamental group of the complement of the projective completion + of the curve will be computed, otherwise, the fundamental group of + the complement in the affine plane will be computed + + - ``puiseux`` -- boolean (default: ``False``); if set to ``True``, + ``simplified`` is set to ``False``, and + a presentation of the fundamental group with the homotopy type + of the complement of the affine curve will be computed, adding + one relation if ``projective`` is set to ``True``. + + 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``. + + - A dictionary attaching a tuple ``(i,)`` (generator) to a number ``j`` + (a polynomial in the list). If ``simplified`` is set to ``True``, + a longer key may appear for either the meridian of the line at infinity, + if ``projective`` is ``True``, or a simplified generator, + if ``projective`` is ``False`` + + EXAMPLES:: + + sage: # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3 * y - 5] + sage: g, dic = fundamental_group_arrangement(flist) + sage: g.sorted_presentation() + Finitely presented group + < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2^-1*x0*x2*x0, x1^-1*x0^-1*x1*x0 > + sage: dic + {0: [x0, x2, x0], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]} + sage: g, dic = fundamental_group_arrangement(flist, simplified=False) + sage: g.sorted_presentation(), dic + (Finitely presented group + < x0, x1, x2, x3 | 1, 1, 1, 1, 1, 1, 1, x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, + x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x3*x2, + x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1*x2, + x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, x3^-1*x1^-1*x0*x1, + x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0, + x1^-1*x0^-1*x1*x0 >, + {0: [x0, x2, x3], 1: [x1], 2: [x3^-1*x2^-1*x1^-1*x0^-1]}) + sage: fundamental_group_arrangement(flist, projective=True) + (Finitely presented group < x | >, {0: [x0, x0, x0], 1: [x0^-3]}) + sage: fundamental_group_arrangement([]) + (Finitely presented group < | >, {}) + sage: g, dic = fundamental_group_arrangement([x * y]) + sage: g.sorted_presentation(), dic + (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, + {0: [x0, x1], 1: [x1^-1*x0^-1]}) + sage: fundamental_group_arrangement([y + x^2], projective=True) + (Finitely presented group < x | x^2 >, {0: [x0, x0]}) + + .. TODO:: + + Create a class ``arrangements_of_curves`` with a ``fundamental_group`` + method it can be also a method for affine or projective line + arrangements, even for hyperplane arrangements defined over a number + subfield of ``QQbar`` after applying a generic line section. + """ + if len(flist) > 0: + f = prod(flist) + R = f.parent() + else: + R = PolynomialRing(QQ, ('x', 'y')) + f = R(1) + x, y = R.gens() + F = R.base_ring() + flist1 = [_ for _ in flist] + d = f.degree(y) + while not f.coefficient(y**d) in F: + flist1 = [g.subs({x: x + y}) for g in flist1] + f = prod(flist1) + d = f.degree(y) if projective: - R.append(prod(F.gens())) - G = F / R + while f.degree(y) < f.degree(): + flist1 = [g.subs({x: x + y}) for g in flist] + f = prod(flist1) + if len(flist1) == 0: + bm = [] + dic = dict() + else: + bm, dic = braid_monodromy(f, flist1) + g = fundamental_group_from_braid_mon(bm, degree=d, simplified=False, projective=projective, puiseux=puiseux) if simplified: - return G.simplified() - return G + hom = g.simplification_isomorphism() + else: + hom = g.hom(codomain=g, im_gens=list(g.gens()), check=False) + g1 = hom.codomain() + if len(flist) == 0: + return (g1, dict()) + dic1 = {} + for i in range(len(flist1)): + L = [j1 for j1 in dic.keys() if dic[j1] == i] + dic1[i] = [hom(g.gen(j)) for j in L] + if not projective and f.degree(y) == f.degree(): + t = prod(hom(x) for x in g.gens()).inverse() + dic1[len(flist1)] = [t] + n = g1.ngens() + rels = [_.Tietze() for _ in g1.relations()] + g1 = FreeGroup(n) / rels + return (g1, dic1)