From 4be6055f6255bb0e1799a437824ca63041faf469 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 23 Aug 2022 16:09:45 +0200 Subject: [PATCH 001/122] Compute the discriminant using the factorizaton --- src/sage/schemes/curves/zariski_vankampen.py | 32 ++++++++++++++------ 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 7a5b298ce07..4823f7e78c5 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -55,6 +55,8 @@ from sage.graphs.graph import Graph from sage.misc.cachefunc import cached_function from copy import copy +from sage.combinat.combination import Combinations + roots_interval_cache = dict() @@ -150,7 +152,7 @@ def sgn(x, y): return B(braid) -def discrim(f): +def discrim(flist): r""" Return the points in the discriminant of ``f``. @@ -159,7 +161,7 @@ def discrim(f): INPUT: - - ``f`` -- a polynomial in two variables with coefficients in a + - ``flist`` -- a list of polynomials in two variables with coefficients in a number field with a fixed embedding in `\QQbar` OUTPUT: @@ -170,15 +172,22 @@ def discrim(f): sage: from sage.schemes.curves.zariski_vankampen import discrim sage: R. = QQ[] - sage: f = (y^3 + x^3 - 1) * (x + y) + sage: f = [y^3 + x^3 - 1, x + y] sage: discrim(f) [1, -0.500000000000000? - 0.866025403784439?*I, -0.500000000000000? + 0.866025403784439?*I] """ - x, y = f.parent().gens() - F = f.base_ring() - poly = F[x](f.discriminant(y)).radical() + x, y = flist[0].parent().gens() + F = flist[0].base_ring() + poly = 1 + for f in flist: + aux = F[x](f.discriminant(y)) + poly = aux*poly + for f,g in Combinations(flist,2): + aux = F[x](f.resultant(g,y)) + poly = aux*poly + poly = poly.radical() return poly.roots(QQbar, multiplicities=False) @@ -907,14 +916,19 @@ def braid_monodromy(f): """ global roots_interval_cache + changes = 0 (x, y) = f.parent().gens() F = f.base_ring() - g = f.radical() + ffac = f.factor() + g = prod([_[0] for _ in ffac]) 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) + changes += 1 + gfac = g.factor() + glist = [_[0] for _ in gfac] + disc = discrim(glist) V = corrected_voronoi_diagram(tuple(disc)) G = Graph() for reg in V.regions().values(): @@ -935,7 +949,7 @@ def braid_monodromy(f): vertices = list(set(flatten(segs))) tocacheverts = [(g, v) for v in vertices] populate_roots_interval_cache(tocacheverts) - gfac = g.factor() + #gfac = g.factor() try: braidscomputed = list(braid_in_segment([(gfac, seg[0], seg[1]) for seg in segs])) except ChildProcessError: # hack to deal with random fails first time From 42c4b3e6432c21fd6ab2ab19c272f39ca44976dc Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 23 Aug 2022 17:34:43 +0200 Subject: [PATCH 002/122] Corrected info and added optional information --- src/sage/schemes/curves/affine_curve.py | 5 ++-- src/sage/schemes/curves/zariski_vankampen.py | 26 ++++++++++++++------ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index f09cde624eb..6ca42239d8d 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1733,7 +1733,7 @@ def fundamental_group(self): f = self.defining_polynomial() return fundamental_group(f, projective=False) - def braid_monodromy(self): + def braid_monodromy(self, change_info = False): r""" Compute the braid monodromy of a projection of the curve. @@ -1760,13 +1760,14 @@ def braid_monodromy(self): """ from sage.schemes.curves.zariski_vankampen import braid_monodromy + chinfo = change_info 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 braid_monodromy(f) + return braid_monodromy(f, change_info=chinfo) def riemann_surface(self, **kwargs): diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 4823f7e78c5..aacd5ba5c5a 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -55,6 +55,7 @@ from sage.graphs.graph import Graph from sage.misc.cachefunc import cached_function from copy import copy +# Added Combinations from sage.combinat.combination import Combinations @@ -152,9 +153,10 @@ def sgn(x, y): return B(braid) + def discrim(flist): r""" - Return the points in the discriminant of ``f``. + Return the points in the discriminant of the product of the polynomials of a list ``flist``. The result is the set of values of the first variable for which two roots in the second variable coincide. @@ -172,11 +174,14 @@ def discrim(flist): 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: flist = [y^3 + x^3 - 1, 2*x + y] + sage: discrim(flist) + [-0.522757958574711?, + 1, -0.500000000000000? - 0.866025403784439?*I, - -0.500000000000000? + 0.866025403784439?*I] + -0.500000000000000? + 0.866025403784439?*I, + 0.2613789792873551? - 0.4527216721561923?*I, + 0.2613789792873551? + 0.4527216721561923?*I] """ x, y = flist[0].parent().gens() F = flist[0].base_ring() @@ -883,7 +888,7 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f): +def braid_monodromy(f, change_info = False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial @@ -891,12 +896,15 @@ def braid_monodromy(f): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers. + + - ``change_info`` -- a boolean variable (default ``False``); 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``. + If ``change_info`` is ``True`` the number of changes of variables and the ``x``-coordinate of the base point are part of the output. .. NOTE:: @@ -938,6 +946,7 @@ def braid_monodromy(f): if reg.rays() or reg.lines(): E = E.union(reg.vertex_graph()) p = next(E.vertex_iterator()) + p0 = p.vector() geombasis = geometric_basis(G, E, p) segs = set([]) for p in geombasis: @@ -971,7 +980,10 @@ def braid_monodromy(f): x1 = tuple(path[i+1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - return result + if change_info: + return (result,changes,p0) + else: + return result def fundamental_group(f, simplified=True, projective=False): From ffe76f0ae415a72ea7089d3ee6b73179baba7c4f Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 31 Aug 2022 13:50:45 +0200 Subject: [PATCH 003/122] Define braid monodromy for an arrangement --- src/sage/schemes/curves/affine_curve.py | 5 +- src/sage/schemes/curves/zariski_vankampen.py | 208 ++++++++++++++++++- 2 files changed, 207 insertions(+), 6 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 1ba75223e82..bcdced80990 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1733,7 +1733,7 @@ def fundamental_group(self): f = self.defining_polynomial() return fundamental_group(f, projective=False) - def braid_monodromy(self, change_info = False): + def braid_monodromy(self): r""" Compute the braid monodromy of a projection of the curve. @@ -1760,14 +1760,13 @@ def braid_monodromy(self, change_info = False): """ from sage.schemes.curves.zariski_vankampen import braid_monodromy - chinfo = change_info 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 braid_monodromy(f, change_info=chinfo) + return braid_monodromy(f) def riemann_surface(self, **kwargs): diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 0ebec4a5302..6446495c004 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -888,7 +888,7 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f, change_info = False): +def braid_monodromy(f): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -896,8 +896,7 @@ def braid_monodromy(f, change_info = False): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers - - - ``change_info`` -- a boolean variable (default ``False``); + OUTPUT: @@ -986,6 +985,102 @@ def braid_monodromy(f, change_info = False): else: return result +def braid_monodromy(f): + r""" + Compute the braid monodromy of a projection of the curve defined by a polynomial. + + INPUT: + + - ``f`` -- a polynomial with two variables, over a number field + with an embedding in the complex numbers + + + OUTPUT: + + A list of braids. The braids correspond to paths based in the same point; + each of this paths is the conjugated of a loop around one of the points + in the discriminant of the projection of ``f``. + + + .. NOTE:: + + The projection over the `x` axis is used if there are no vertical asymptotes. + Otherwise, a linear change of variables is done to fall into the previous case. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy + sage: R. = QQ[] + sage: f = (x^2-y^3)*(x+3*y-5) + sage: braid_monodromy(f) # optional - sirocco + [s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1, + s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1, + s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s2*s0^-1*s2*s1^-1] + """ + global roots_interval_cache + changes = 0 + x, y = f.parent().gens() + F = f.base_ring() + ffac = f.factor() + g = prod([_[0] for _ in ffac]) + d = g.degree(y) + while not g.coefficient(y**d) in F: + g = g.subs({x: x + y}) + d = g.degree(y) + changes += 1 + gfac = g.factor() + glist = [_[0] for _ in gfac] + disc = discrim(glist) + 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()) + p0 = p.vector() + geombasis = geometric_basis(G, E, p) + segs = set() + for p in geombasis: + for s in zip(p[:-1], p[1:]): + if (s[1], s[0]) not in segs: + segs.add((s[0], s[1])) + I = QQbar.gen() + segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] + vertices = list(set(flatten(segs))) + tocacheverts = [(g, v) for v in vertices] + populate_roots_interval_cache(tocacheverts) + #gfac = g.factor() + try: + braidscomputed = (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() + result = [] + for path in geombasis: + braidpath = B.one() + for i in range(len(path) - 1): + x0 = tuple(path[i].vector()) + x1 = tuple(path[i + 1].vector()) + braidpath = braidpath * segsbraids[(x0, x1)] + result.append(braidpath) + if change_info: + return (result,changes,p0) + else: + return result def fundamental_group(f, simplified=True, projective=False): r""" @@ -1066,3 +1161,110 @@ def relation(x, b): if simplified: return G.simplified() return G + + +def braid_monodromy_arrangement_arrangement(flist): + r""" + Compute the braid monodromy of a projection of the curve + defined by a list of polynomials with the extra information about the correspondence of strands + and elements of the list. + + INPUT: + + - ``f`` -- a list of polynomial with two variables, over a number field + with an embedding in the complex numbers + + + OUTPUT: + + - A list of braids. The braids correspond to paths based in the same point; + each of this paths is the conjugated of a loop around one of the points + in the discriminant of the projection of ``f``. + + - A dictionary attaching a number `i` (strand) to a number `j` (a polynomial in the list). + + .. NOTE:: + + The projection over the `x` axis is used if there are no vertical asymptotes. + Otherwise, a linear change of variables is done to fall into the previous case. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy + sage: R. = QQ[] + sage: f = (x^2-y^3)*(x+3*y-5) + sage: braid_monodromy(f) # optional - sirocco + [s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1, + s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1, + s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s2*s0^-1*s2*s1^-1] + """ + global roots_interval_cache + changes = 0 + (x, y) = flist[0].parent().gens() + F = flist[0].base_ring() + Ft.=F[] + g = prod([_ for _ in flist]) + d = g.degree(y) + while not g.coefficient(y**d) in F: + flist=[f.subs({x: x + y}) for f in flist] + g = g.subs({x: x + y}) + d = g.degree(y) + changes += 1 + gfac = g.factor() + glist = [_[0] for _ in gfac] + disc = discrim(glist) + 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()) + I = QQbar.gen() + p0 = p[0] + I*p[1] + geombasis = geometric_basis(G, E, p) + segs = set() + for p in geombasis: + for s in zip(p[:-1], p[1:]): + if (s[1], s[0]) not in segs: + segs.add((s[0], s[1])) + segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] + vertices = list(set(flatten(segs))) + tocacheverts = [(g, v) for v in vertices] + populate_roots_interval_cache(tocacheverts) + #gfac = g.factor() + try: + braidscomputed = (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() + result = [] + for path in geombasis: + braidpath = B.one() + for i in range(len(path) - 1): + x0 = tuple(path[i].vector()) + x1 = tuple(path[i + 1].vector()) + braidpath = braidpath * segsbraids[(x0, x1)] + result.append(braidpath) + roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots_base.sort() + strands = {} + for i, val in enumerate(flist): + roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + for j in roots: + k = roots_base.index(j) + strands[k + 1] = i + 1 + return (result, strands) From 083ff14a23eaae2f60d7f6bd95da8161c6c8505b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 31 Aug 2022 16:39:00 +0200 Subject: [PATCH 004/122] Check tests --- src/sage/schemes/curves/affine_curve.py | 5 +- src/sage/schemes/curves/zariski_vankampen.py | 220 ++++++++++++++++++- 2 files changed, 212 insertions(+), 13 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 1ba75223e82..bcdced80990 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1733,7 +1733,7 @@ def fundamental_group(self): f = self.defining_polynomial() return fundamental_group(f, projective=False) - def braid_monodromy(self, change_info = False): + def braid_monodromy(self): r""" Compute the braid monodromy of a projection of the curve. @@ -1760,14 +1760,13 @@ def braid_monodromy(self, change_info = False): """ from sage.schemes.curves.zariski_vankampen import braid_monodromy - chinfo = change_info 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 braid_monodromy(f, change_info=chinfo) + return braid_monodromy(f) def riemann_surface(self, **kwargs): diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 0ebec4a5302..59c80669a57 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -58,6 +58,7 @@ from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing roots_interval_cache = {} @@ -275,7 +276,7 @@ def segments(points): 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: disc = discrim([f]) sage: sorted(segments(disc)) [(-192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316, -192951821525958031/90044183378780414), @@ -888,7 +889,7 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f, change_info = False): +def braid_monodromy(f): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -896,15 +897,13 @@ def braid_monodromy(f, change_info = False): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers - - - ``change_info`` -- a boolean variable (default ``False``); + 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``. - If ``change_info`` is ``True`` the number of changes of variables and the ``x``-coordinate of the base point are part of the output. .. NOTE:: @@ -915,7 +914,7 @@ def braid_monodromy(f, change_info = False): sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy sage: R. = QQ[] - sage: f = (x^2-y^3)*(x+3*y-5) + 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, @@ -981,11 +980,101 @@ def braid_monodromy(f, change_info = False): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - if change_info: - return (result,changes,p0) - else: - return result + return result + +def braid_monodromy(f): + r""" + Compute the braid monodromy of a projection of the curve defined by a polynomial. + + INPUT: + + - ``f`` -- a polynomial with two variables, over a number field + with an embedding in the complex numbers + + + OUTPUT: + + A list of braids. The braids correspond to paths based in the same point; + each of this paths is the conjugated of a loop around one of the points + in the discriminant of the projection of ``f``. + + + .. NOTE:: + The projection over the `x` axis is used if there are no vertical asymptotes. + Otherwise, a linear change of variables is done to fall into the previous case. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy + sage: R. = QQ[] + sage: f = (x^2-y^3)*(x+3*y-5) + sage: braid_monodromy(f) # optional - sirocco + [s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1, + s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1, + s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s2*s0^-1*s2*s1^-1] + """ + global roots_interval_cache + changes = 0 + x, y = f.parent().gens() + F = f.base_ring() + ffac = f.factor() + g = prod([_[0] for _ in ffac]) + d = g.degree(y) + while not g.coefficient(y**d) in F: + g = g.subs({x: x + y}) + d = g.degree(y) + changes += 1 + gfac = g.factor() + glist = [_[0] for _ in gfac] + disc = discrim(glist) + 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()) + p0 = p.vector() + geombasis = geometric_basis(G, E, p) + segs = set() + for p in geombasis: + for s in zip(p[:-1], p[1:]): + if (s[1], s[0]) not in segs: + segs.add((s[0], s[1])) + I = QQbar.gen() + segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] + vertices = list(set(flatten(segs))) + tocacheverts = [(g, v) for v in vertices] + populate_roots_interval_cache(tocacheverts) + #gfac = g.factor() + try: + braidscomputed = (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() + result = [] + for path in geombasis: + braidpath = B.one() + for i in range(len(path) - 1): + x0 = tuple(path[i].vector()) + x1 = tuple(path[i + 1].vector()) + braidpath = braidpath * segsbraids[(x0, x1)] + result.append(braidpath) + return result def fundamental_group(f, simplified=True, projective=False): r""" @@ -1066,3 +1155,114 @@ def relation(x, b): if simplified: return G.simplified() return G + + +def braid_monodromy_arrangement(flist): + r""" + Compute the braid monodromy of a projection of the curve + defined by a list of polynomials with the extra information about the correspondence of strands + and elements of the list. + + INPUT: + + - ``flist`` -- a list of polynomial with two variables, over a number field + with an embedding in the complex numbers + + + OUTPUT: + + - A list of braids. The braids correspond to paths based in the same point; + each of this paths is the conjugated of a loop around one of the points + in the discriminant of the projection of ``f``. + + - A dictionary attaching a number `i` (strand) to a number `j` (a polynomial in the list). + + .. NOTE:: + + The projection over the `x` axis is used if there are no vertical asymptotes. + Otherwise, a linear change of variables is done to fall into the previous case. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy_arrangement + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3*y - 5] + sage: braid_monodromy_arrangement(flist) # 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], + {1: 1, 2: 2, 3: 1, 4: 1}) + + """ + global roots_interval_cache + changes = 0 + (x, y) = flist[0].parent().gens() + F = flist[0].base_ring() + Ft = PolynomialRing(F, 't') + Ft.inject_variables(verbose = False) + g = prod([_ for _ in flist]) + d = g.degree(y) + while not g.coefficient(y**d) in F: + flist=[f.subs({x: x + y}) for f in flist] + g = g.subs({x: x + y}) + d = g.degree(y) + changes += 1 + gfac = g.factor() + glist = [_[0] for _ in gfac] + disc = discrim(glist) + 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()) + I = QQbar.gen() + p0 = p[0] + I*p[1] + geombasis = geometric_basis(G, E, p) + segs = set() + for p in geombasis: + for s in zip(p[:-1], p[1:]): + if (s[1], s[0]) not in segs: + segs.add((s[0], s[1])) + segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] + vertices = list(set(flatten(segs))) + tocacheverts = [(g, v) for v in vertices] + populate_roots_interval_cache(tocacheverts) + #gfac = g.factor() + try: + braidscomputed = (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() + result = [] + for path in geombasis: + braidpath = B.one() + for i in range(len(path) - 1): + x0 = tuple(path[i].vector()) + x1 = tuple(path[i + 1].vector()) + braidpath = braidpath * segsbraids[(x0, x1)] + result.append(braidpath) + roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots_base.sort() + strands = {} + for i, val in enumerate(flist): + roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots.sort() + for j in roots: + k = roots_base.index(j) + strands[k + 1] = i + 1 + return (result, strands) From 361d6aaa32d9bb67d09c243cb73cb8844db2efa7 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 1 Sep 2022 13:25:15 +0200 Subject: [PATCH 005/122] Simplifying the functions --- src/sage/schemes/curves/zariski_vankampen.py | 204 ++++--------------- 1 file changed, 34 insertions(+), 170 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 59c80669a57..e2d8b601864 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -889,7 +889,7 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f): +def braid_monodromy(f, arrangement = []): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -897,106 +897,19 @@ def braid_monodromy(f): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers + + - ``arrangement`` -- an optional list of polynomials whose product equals ``f``, + in order to provide information for `braid_monodromy_arrangement`. OUTPUT: - A list of braids. The braids correspond to paths based in the same point; - each of this paths is the conjugated of a loop around one of the points - in the discriminant of the projection of ``f``. - - .. NOTE:: - - The projection over the `x` axis is used if there are no vertical asymptotes. - Otherwise, a linear change of variables is done to fall into the previous case. - - EXAMPLES:: - - sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy - sage: R. = QQ[] - sage: f = (x^2 - y^3)*(x + 3*y - 5) - sage: braid_monodromy(f) # optional - sirocco - [s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1, - s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1, - s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - s1*s0*s2*s0^-1*s2*s1^-1] - """ - global roots_interval_cache - changes = 0 - x, y = f.parent().gens() - F = f.base_ring() - ffac = f.factor() - g = prod([_[0] for _ in ffac]) - d = g.degree(y) - while not g.coefficient(y**d) in F: - g = g.subs({x: x + y}) - d = g.degree(y) - changes += 1 - gfac = g.factor() - glist = [_[0] for _ in gfac] - disc = discrim(glist) - 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()) - p0 = p.vector() - geombasis = geometric_basis(G, E, p) - segs = set() - for p in geombasis: - for s in zip(p[:-1], p[1:]): - if (s[1], s[0]) not in segs: - segs.add((s[0], s[1])) - I = QQbar.gen() - segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] - vertices = list(set(flatten(segs))) - tocacheverts = [(g, v) for v in vertices] - populate_roots_interval_cache(tocacheverts) - #gfac = g.factor() - try: - braidscomputed = (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() - result = [] - for path in geombasis: - braidpath = B.one() - for i in range(len(path) - 1): - x0 = tuple(path[i].vector()) - x1 = tuple(path[i + 1].vector()) - braidpath = braidpath * segsbraids[(x0, x1)] - result.append(braidpath) - return result - -def braid_monodromy(f): - r""" - Compute the braid monodromy of a projection of the curve defined by a polynomial. - - INPUT: - - - ``f`` -- a polynomial with two variables, over a number field - with an embedding in the complex numbers - - - OUTPUT: - - A list of braids. The braids correspond to paths based in the same point; + If ``arrangement`` contains only one element, 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``. + + If ``arrangement`` contains more than one element, some information to be use by + `braid_monodromy_arrangement` is provided. .. NOTE:: @@ -1016,16 +929,19 @@ def braid_monodromy(f): s1*s0*s2*s0^-1*s2*s1^-1] """ global roots_interval_cache - changes = 0 + if arrangement == []: + arrangement = [f] x, y = f.parent().gens() F = f.base_ring() + Ft = PolynomialRing(F, 't') + Ft.inject_variables(verbose = False) ffac = f.factor() g = prod([_[0] for _ in ffac]) d = g.degree(y) while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - changes += 1 + arrangement = [f1.subs({x: x + y}) for f1 in arrangement] gfac = g.factor() glist = [_[0] for _ in gfac] disc = discrim(glist) @@ -1038,7 +954,10 @@ def braid_monodromy(f): if reg.rays() or reg.lines(): E = E.union(reg.vertex_graph()) p = next(E.vertex_iterator()) - p0 = p.vector() + I = QQbar.gen() + p0 = p[0] + I*p[1] + roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots_base.sort() geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -1074,7 +993,17 @@ def braid_monodromy(f): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - return result + if len(arrangement) == 1: + return result + else: + strands = {} + for i, val in enumerate(arrangement): + roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots.sort() + for j in roots: + k = roots_base.index(j) + strands[k + 1] = i + 1 + return (result, strands) def fundamental_group(f, simplified=True, projective=False): r""" @@ -1195,74 +1124,9 @@ def braid_monodromy_arrangement(flist): {1: 1, 2: 2, 3: 1, 4: 1}) """ - global roots_interval_cache - changes = 0 - (x, y) = flist[0].parent().gens() - F = flist[0].base_ring() - Ft = PolynomialRing(F, 't') - Ft.inject_variables(verbose = False) - g = prod([_ for _ in flist]) - d = g.degree(y) - while not g.coefficient(y**d) in F: - flist=[f.subs({x: x + y}) for f in flist] - g = g.subs({x: x + y}) - d = g.degree(y) - changes += 1 - gfac = g.factor() - glist = [_[0] for _ in gfac] - disc = discrim(glist) - 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()) - I = QQbar.gen() - p0 = p[0] + I*p[1] - geombasis = geometric_basis(G, E, p) - segs = set() - for p in geombasis: - for s in zip(p[:-1], p[1:]): - if (s[1], s[0]) not in segs: - segs.add((s[0], s[1])) - segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] - vertices = list(set(flatten(segs))) - tocacheverts = [(g, v) for v in vertices] - populate_roots_interval_cache(tocacheverts) - #gfac = g.factor() - try: - braidscomputed = (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() - result = [] - for path in geombasis: - braidpath = B.one() - for i in range(len(path) - 1): - x0 = tuple(path[i].vector()) - x1 = tuple(path[i + 1].vector()) - braidpath = braidpath * segsbraids[(x0, x1)] - result.append(braidpath) - roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) - roots_base.sort() - strands = {} - for i, val in enumerate(flist): - roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) - roots.sort() - for j in roots: - k = roots_base.index(j) - strands[k + 1] = i + 1 - return (result, strands) + f = prod(flist) + if len(flist) == 1: + d = f.degree() + dic ={j + 1 : 1 for j in range(d)} + return (braid_monodromy(f), dic) + return braid_monodromy(f,arrangement = flist) From 6731ba7999e104d7a602af0b6c68639333897adb Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 1 Sep 2022 20:13:34 +0200 Subject: [PATCH 006/122] parallel --- src/sage/schemes/curves/zariski_vankampen.py | 289 ++++++------------- 1 file changed, 84 insertions(+), 205 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 59c80669a57..6abd41bf6f1 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1,3 +1,4 @@ + r""" Zariski-Van Kampen method implementation @@ -23,8 +24,8 @@ EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: R. = QQ[] - sage: f = y^3 + x^3 -1 + sage: R. = QQ[] + sage: f = y^3 + x^3 - 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ @@ -39,7 +40,6 @@ # **************************************************************************** import itertools from copy import copy -# Added Combinations from sage.combinat.combination import Combinations @@ -177,9 +177,9 @@ def discrim(flist): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import discrim - sage: R. = QQ[] - sage: flist = [y^3 + x^3 - 1, 2*x + y] - sage: discrim(flist) + sage: R. = QQ[] + sage: flist = [y^3 + x^3 - 1, 2 * x + y] + sage: discrim(flist) # optional - sirocco [-0.522757958574711?, 1, -0.500000000000000? - 0.866025403784439?*I, @@ -189,15 +189,28 @@ def discrim(flist): """ x, y = flist[0].parent().gens() F = flist[0].base_ring() + + #@parallel + #def discrim_pairs(ftuple): + #if len(ftuple) == 1: + #f = ftuple[0] + #return F[x](f.discriminant(y)) + #elif len(ftuple) == 2: + #f, g = ftuple + #return F[x](f.resultant(g, y)) + + #pairs = [(f,) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] + #fdiscrim = discrim_pairs(pairs) poly = 1 + #for u in fdiscrim: + #poly = poly * u[1] for f in flist: aux = F[x](f.discriminant(y)) - poly = aux*poly - for f,g in Combinations(flist,2): - aux = F[x](f.resultant(g,y)) - poly = aux*poly - poly = poly.radical() - return poly.roots(QQbar, multiplicities=False) + poly = aux * poly + for f, g in Combinations(flist, 2): + aux = F[x](f.resultant(g, y)) + poly = aux * poly + return poly.roots(QQbar, multiplicities = False) @cached_function @@ -274,7 +287,7 @@ def segments(points): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import discrim, segments - sage: R. = QQ[] + sage: R. = QQ[] sage: f = y^3 + x^3 - 1 sage: disc = discrim([f]) sage: sorted(segments(disc)) @@ -339,7 +352,7 @@ 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: R. = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1, 0) sage: x1 = CC(1, 0.5) @@ -593,7 +606,7 @@ 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: R. = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1,0) sage: x1 = CC(1, 0.5) @@ -889,100 +902,7 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f): - r""" - Compute the braid monodromy of a projection of the curve defined by a polynomial. - - INPUT: - - - ``f`` -- a polynomial with two variables, over a number field - with an embedding in the complex numbers - - - OUTPUT: - - A list of braids. The braids correspond to paths based in the same point; - each of this paths is the conjugated of a loop around one of the points - in the discriminant of the projection of ``f``. - - .. NOTE:: - - The projection over the `x` axis is used if there are no vertical asymptotes. - Otherwise, a linear change of variables is done to fall into the previous case. - - EXAMPLES:: - - sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy - sage: R. = QQ[] - sage: f = (x^2 - y^3)*(x + 3*y - 5) - sage: braid_monodromy(f) # optional - sirocco - [s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1, - s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1, - s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - s1*s0*s2*s0^-1*s2*s1^-1] - """ - global roots_interval_cache - changes = 0 - x, y = f.parent().gens() - F = f.base_ring() - ffac = f.factor() - g = prod([_[0] for _ in ffac]) - d = g.degree(y) - while not g.coefficient(y**d) in F: - g = g.subs({x: x + y}) - d = g.degree(y) - changes += 1 - gfac = g.factor() - glist = [_[0] for _ in gfac] - disc = discrim(glist) - 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()) - p0 = p.vector() - geombasis = geometric_basis(G, E, p) - segs = set() - for p in geombasis: - for s in zip(p[:-1], p[1:]): - if (s[1], s[0]) not in segs: - segs.add((s[0], s[1])) - I = QQbar.gen() - segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] - vertices = list(set(flatten(segs))) - tocacheverts = [(g, v) for v in vertices] - populate_roots_interval_cache(tocacheverts) - #gfac = g.factor() - try: - braidscomputed = (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() - result = [] - for path in geombasis: - braidpath = B.one() - for i in range(len(path) - 1): - x0 = tuple(path[i].vector()) - x1 = tuple(path[i + 1].vector()) - braidpath = braidpath * segsbraids[(x0, x1)] - result.append(braidpath) - return result - -def braid_monodromy(f): +def braid_monodromy(f, arrangement = []): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -990,13 +910,19 @@ def braid_monodromy(f): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers + + - ``arrangement`` -- an optional list of polynomials whose product equals ``f``, + in order to provide information for `braid_monodromy_arrangement`. OUTPUT: - A list of braids. The braids correspond to paths based in the same point; + If ``arrangement`` contains only one element, 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``. + + If ``arrangement`` contains more than one element, some information to be use by + `braid_monodromy_arrangement` is provided. .. NOTE:: @@ -1007,8 +933,8 @@ def braid_monodromy(f): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy - sage: R. = QQ[] - sage: f = (x^2-y^3)*(x+3*y-5) + 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, @@ -1016,16 +942,19 @@ def braid_monodromy(f): s1*s0*s2*s0^-1*s2*s1^-1] """ global roots_interval_cache - changes = 0 + if arrangement == []: + arrangement = [f] x, y = f.parent().gens() F = f.base_ring() + Ft = PolynomialRing(F, 't') + Ft.inject_variables(verbose = False) ffac = f.factor() g = prod([_[0] for _ in ffac]) d = g.degree(y) while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - changes += 1 + arrangement = [f1.subs({x: x + y}) for f1 in arrangement] gfac = g.factor() glist = [_[0] for _ in gfac] disc = discrim(glist) @@ -1038,25 +967,30 @@ def braid_monodromy(f): if reg.rays() or reg.lines(): E = E.union(reg.vertex_graph()) p = next(E.vertex_iterator()) - p0 = p.vector() + I = QQbar.gen() + p0 = p[0] + I * p[1] + roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots_base.sort() geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: for s in zip(p[:-1], p[1:]): if (s[1], s[0]) not in segs: segs.add((s[0], s[1])) - I = QQbar.gen() segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] vertices = list(set(flatten(segs))) tocacheverts = [(g, v) for v in vertices] populate_roots_interval_cache(tocacheverts) - #gfac = g.factor() - try: - braidscomputed = (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])) + end_braid_computation = False + while not end_braid_computation: + try: + braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) + for seg in segs])) + end_braid_computation = True + except ChildProcessError: # hack to deal with random fails first time + #braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) + # for seg in segs])) + print ("retrying braid computation") segsbraids = {} for braidcomputed in braidscomputed: seg = (braidcomputed[0][0][1], braidcomputed[0][0][2]) @@ -1074,9 +1008,19 @@ def braid_monodromy(f): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - return result - -def fundamental_group(f, simplified=True, projective=False): + if len(arrangement) == 1: + return result + else: + strands = {} + for i, val in enumerate(arrangement): + roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots.sort() + for j in roots: + k = roots_base.index(j) + strands[k + 1] = i + 1 + return (result, strands) + +def fundamental_group(f, simplified = True, projective = False): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. @@ -1105,7 +1049,7 @@ 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: R. = QQ[] sage: f = x^2 + y^3 sage: fundamental_group(f) # optional - sirocco Finitely presented group < ... > @@ -1115,7 +1059,7 @@ def fundamental_group(f, simplified=True, projective=False): :: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: R. = QQ[] + sage: R. = QQ[] sage: f = y^3 + x^3 sage: fundamental_group(f) # optional - sirocco Finitely presented group < ... > @@ -1124,14 +1068,14 @@ def fundamental_group(f, simplified=True, projective=False): 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: 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: R. = F[] + sage: f = y^3 + x^3 + zeta * x + 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ @@ -1184,9 +1128,9 @@ def braid_monodromy_arrangement(flist): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy_arrangement - sage: R. = QQ[] - sage: flist = [x^2 - y^3, x + 3*y - 5] + sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy_arrangement # optional - sirocco + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3 * y - 5] sage: braid_monodromy_arrangement(flist) # 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, @@ -1195,74 +1139,9 @@ def braid_monodromy_arrangement(flist): {1: 1, 2: 2, 3: 1, 4: 1}) """ - global roots_interval_cache - changes = 0 - (x, y) = flist[0].parent().gens() - F = flist[0].base_ring() - Ft = PolynomialRing(F, 't') - Ft.inject_variables(verbose = False) - g = prod([_ for _ in flist]) - d = g.degree(y) - while not g.coefficient(y**d) in F: - flist=[f.subs({x: x + y}) for f in flist] - g = g.subs({x: x + y}) - d = g.degree(y) - changes += 1 - gfac = g.factor() - glist = [_[0] for _ in gfac] - disc = discrim(glist) - 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()) - I = QQbar.gen() - p0 = p[0] + I*p[1] - geombasis = geometric_basis(G, E, p) - segs = set() - for p in geombasis: - for s in zip(p[:-1], p[1:]): - if (s[1], s[0]) not in segs: - segs.add((s[0], s[1])) - segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] - vertices = list(set(flatten(segs))) - tocacheverts = [(g, v) for v in vertices] - populate_roots_interval_cache(tocacheverts) - #gfac = g.factor() - try: - braidscomputed = (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() - result = [] - for path in geombasis: - braidpath = B.one() - for i in range(len(path) - 1): - x0 = tuple(path[i].vector()) - x1 = tuple(path[i + 1].vector()) - braidpath = braidpath * segsbraids[(x0, x1)] - result.append(braidpath) - roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) - roots_base.sort() - strands = {} - for i, val in enumerate(flist): - roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) - roots.sort() - for j in roots: - k = roots_base.index(j) - strands[k + 1] = i + 1 - return (result, strands) + f = prod(flist) + if len(flist) == 1: + d = f.degree() + dic ={j + 1 : 1 for j in range(d)} + return (braid_monodromy(f), dic) + return braid_monodromy(f,arrangement = flist) From d7b13c4c1255f1b12a1aaa79b364e2d17c1aed2a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 1 Sep 2022 21:26:28 +0200 Subject: [PATCH 007/122] parallel discrim --- src/sage/schemes/curves/zariski_vankampen.py | 89 ++++++++++++-------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index e2d8b601864..27b5aa60093 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1,3 +1,4 @@ + r""" Zariski-Van Kampen method implementation @@ -23,8 +24,8 @@ EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: R. = QQ[] - sage: f = y^3 + x^3 -1 + sage: R. = QQ[] + sage: f = y^3 + x^3 - 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ @@ -39,7 +40,6 @@ # **************************************************************************** import itertools from copy import copy -# Added Combinations from sage.combinat.combination import Combinations @@ -177,9 +177,9 @@ def discrim(flist): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import discrim - sage: R. = QQ[] - sage: flist = [y^3 + x^3 - 1, 2*x + y] - sage: discrim(flist) + sage: R. = QQ[] + sage: flist = [y^3 + x^3 - 1, 2 * x + y] + sage: discrim(flist) # optional - sirocco [-0.522757958574711?, 1, -0.500000000000000? - 0.866025403784439?*I, @@ -189,15 +189,28 @@ def discrim(flist): """ x, y = flist[0].parent().gens() F = flist[0].base_ring() + + @parallel + def discrim_pairs(ftuple): + if len(ftuple) == 1: + f = ftuple[0] + return F[x](f.discriminant(y)) + elif len(ftuple) == 2: + f, g = ftuple + return F[x](f.resultant(g, y)) + + pairs = [((f,),) for f in flist] + [((f, g),) for f, g in Combinations(flist, 2)] + fdiscrim = discrim_pairs(pairs) poly = 1 - for f in flist: - aux = F[x](f.discriminant(y)) - poly = aux*poly - for f,g in Combinations(flist,2): - aux = F[x](f.resultant(g,y)) - poly = aux*poly - poly = poly.radical() - return poly.roots(QQbar, multiplicities=False) + for u in fdiscrim: + poly = poly * u[1] + #for f in flist: + #aux = F[x](f.discriminant(y)) + #poly = aux * poly + #for f, g in Combinations(flist, 2): + #aux = F[x](f.resultant(g, y)) + #poly = aux * poly + return poly.roots(QQbar, multiplicities = False) @cached_function @@ -274,7 +287,7 @@ def segments(points): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import discrim, segments - sage: R. = QQ[] + sage: R. = QQ[] sage: f = y^3 + x^3 - 1 sage: disc = discrim([f]) sage: sorted(segments(disc)) @@ -339,7 +352,7 @@ 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: R. = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1, 0) sage: x1 = CC(1, 0.5) @@ -593,7 +606,7 @@ 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: R. = QQ[] sage: f = x^2 + y^3 sage: x0 = CC(1,0) sage: x1 = CC(1, 0.5) @@ -920,8 +933,8 @@ def braid_monodromy(f, arrangement = []): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy - sage: R. = QQ[] - sage: f = (x^2-y^3)*(x+3*y-5) + 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, @@ -955,7 +968,7 @@ def braid_monodromy(f, arrangement = []): E = E.union(reg.vertex_graph()) p = next(E.vertex_iterator()) I = QQbar.gen() - p0 = p[0] + I*p[1] + p0 = p[0] + I * p[1] roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) roots_base.sort() geombasis = geometric_basis(G, E, p) @@ -964,18 +977,20 @@ def braid_monodromy(f, arrangement = []): for s in zip(p[:-1], p[1:]): if (s[1], s[0]) not in segs: segs.add((s[0], s[1])) - I = QQbar.gen() segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] vertices = list(set(flatten(segs))) tocacheverts = [(g, v) for v in vertices] populate_roots_interval_cache(tocacheverts) - #gfac = g.factor() - try: - braidscomputed = (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])) + end_braid_computation = False + while not end_braid_computation: + try: + braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) + for seg in segs])) + end_braid_computation = True + except ChildProcessError: # hack to deal with random fails first time + #braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) + # for seg in segs])) + print ("retrying braid computation") segsbraids = {} for braidcomputed in braidscomputed: seg = (braidcomputed[0][0][1], braidcomputed[0][0][2]) @@ -1005,7 +1020,7 @@ def braid_monodromy(f, arrangement = []): strands[k + 1] = i + 1 return (result, strands) -def fundamental_group(f, simplified=True, projective=False): +def fundamental_group(f, simplified = True, projective = False): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. @@ -1034,7 +1049,7 @@ 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: R. = QQ[] sage: f = x^2 + y^3 sage: fundamental_group(f) # optional - sirocco Finitely presented group < ... > @@ -1044,7 +1059,7 @@ def fundamental_group(f, simplified=True, projective=False): :: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco - sage: R. = QQ[] + sage: R. = QQ[] sage: f = y^3 + x^3 sage: fundamental_group(f) # optional - sirocco Finitely presented group < ... > @@ -1053,14 +1068,14 @@ def fundamental_group(f, simplified=True, projective=False): 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: 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: R. = F[] + sage: f = y^3 + x^3 + zeta * x + 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ @@ -1113,9 +1128,9 @@ def braid_monodromy_arrangement(flist): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy_arrangement - sage: R. = QQ[] - sage: flist = [x^2 - y^3, x + 3*y - 5] + sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy_arrangement # optional - sirocco + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3 * y - 5] sage: braid_monodromy_arrangement(flist) # 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, From bb436909c4042a0c17d6024b753ec4d3086e7dcb Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 12:35:57 +0200 Subject: [PATCH 008/122] New try with parallelism --- src/sage/schemes/curves/zariski_vankampen.py | 22 +++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 27b5aa60093..f4b529f1623 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -986,20 +986,22 @@ def braid_monodromy(f, arrangement = []): try: 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() end_braid_computation = True - except ChildProcessError: # hack to deal with random fails first time +# except ChildProcessError: # hack to deal with random fails first time + except: # hack to deal with random fails first time #braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) # for seg in segs])) print ("retrying braid computation") - 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() + #B = b.parent() + B=BraidGroup(d) result = [] for path in geombasis: braidpath = B.one() From 0d0fc48a5142ebb554642f53bf4dfb25f05399df Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 19:50:13 +0200 Subject: [PATCH 009/122] Starting Puiseux ... --- src/sage/schemes/curves/zariski_vankampen.py | 114 ++++++++++++++++--- 1 file changed, 99 insertions(+), 15 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9e7a8b16001..58d4b4527d1 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -58,7 +58,6 @@ from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing roots_interval_cache = {} @@ -943,18 +942,18 @@ def braid_monodromy(f, arrangement = []): """ global roots_interval_cache if arrangement == []: - arrangement = [f] + arrangement1 = [f] + else: + arrangement1 = arrangement x, y = f.parent().gens() F = f.base_ring() - Ft = PolynomialRing(F, 't') - Ft.inject_variables(verbose = False) ffac = f.factor() g = prod([_[0] for _ in ffac]) d = g.degree(y) while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - arrangement = [f1.subs({x: x + y}) for f1 in arrangement] + arrangement1 = [f1.subs({x: x + y}) for f1 in arrangement1] gfac = g.factor() glist = [_[0] for _ in gfac] disc = discrim(glist) @@ -969,8 +968,9 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots_base = F[y](g.subs(x = p0)).roots(QQbar, multiplicities = False) roots_base.sort() + bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -995,11 +995,8 @@ def braid_monodromy(f, arrangement = []): segsbraids[(beginseg, endseg)] = b segsbraids[(endseg, beginseg)] = b.inverse() end_braid_computation = True -# except ChildProcessError: # hack to deal with random fails first time - except: # hack to deal with random fails first time - #braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) - # for seg in segs])) - print ("retrying braid computation") + except ChildProcessError: # hack to deal with random fails first time + pass #B = b.parent() B=BraidGroup(d) result = [] @@ -1010,18 +1007,105 @@ def braid_monodromy(f, arrangement = []): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - if len(arrangement) == 1: + if len(arrangement1) == 1: return result else: strands = {} - for i, val in enumerate(arrangement): - roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + for i, val in enumerate(arrangement1): + roots = F[y](val.subs(x = p0).roots(QQbar, multiplicities = False) roots.sort() for j in roots: - k = roots_base.index(j) + L = [abs(j - j1) for j1 in roots_base] + k = next([k1 for k1 in range(d) if L[k1] <= bound]) strands[k + 1] = i + 1 return (result, strands) + +def braid2rels(L, d): + r""" + Return a minimal set of elements of ``F = FreeGroup(d)`` for a quasi-positive braid ``b=BraidGroup(d)(L)`` as relations + of the group ``F / [(b * F([j])) / F([j]) for j in (1..d)]``. One starts from the non-trivial relations determined by the + positive braid and transform them in relations determined by ``b``. + + INPUT: + + - ``L`` -- a list of integers in ``[-d..-1] + [1..d]`` which is the Tietze word of a quasi-positive braid. + + - ``d`` -- a positive integer + + OUTPUT: + + A list of Tietze words for a minimal set of relations of ``F / [(b * F([j])) / F([j]) for j in (1..d)]``. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import braid2rels # optional - sirocco + sage: L = [-3, 5, 2, -5, 3] + sage: braid2rels(L, 6) # optional - sirocco + [(-3, 2)] + """ + from sage.groups.finitely_presented import wrap_FpGroup + B = BraidGroup(d) + F = FreeGroup(d) + L1 = deepcopy(L) + low = True + while low: + L2 = deepcopy(L1) + j = 0 + l = L[j] + other = False + while j < len(L2) - 1 and not other: + try: + k = L2.index(-l) + A = L2[j + 1: k] + Bn = next((_ for _ in A if 0 < (_^2 - l^2)^2 < 2), None) + if B == None: + other = True + L2 = L2[:j] + B + L2[k + 1:] + print (L2) + else: + j += 1 + except ValueError: + j += 1 + low = len(L2) < len(L1) + L1 = deepcopy(L2) + c0 = () + b0 = L1 + while b0[0] + b0[-1] == 0: + c0= c0 + (b0[0],) + b0 = b0[1: -1] + A = B(b0).super_summit_set() + res = None + for tau in A: + sg = B(c0) * B(b0).conjugating_braid(tau) + A1 = sg.right_normal_form() + b = prod(A1[:-1]) + b1 = len(b.Tietze()) / len(A1) + par = (A1[-1].exponent_sum() / d / (d - 1) * 2)%2 + if res == None or b1 < res[3]: + res = [tau, A1[:-1], par, b1] + if res[2] == 1: + res[0] = B([sign(i) * (d - abs(i)) for i in res[0].Tietze()]) + U0 = list(Set(res[0].Tietze())) + U0.sort() + U1 = list(Set(b0)) + U = [(F([j]) * B(b0)) / F([j]) for j in U1] + U = [_.Tietze() for _ in U] + pasos = [B.one()] + [_ for _ in reversed(res[1])] + for C in pasos: + U = [(F(a) * C^-1).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 + def fundamental_group(f, simplified = True, projective = False): r""" Return a presentation of the fundamental group of the complement of From 50b571391c94020085c483bcc31881fec5989d58 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 19:55:31 +0200 Subject: [PATCH 010/122] Some improvements including fast comparing algebraic numbers --- src/sage/schemes/curves/zariski_vankampen.py | 30 ++++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9e7a8b16001..099568ae1bb 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -58,7 +58,6 @@ from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing roots_interval_cache = {} @@ -943,18 +942,18 @@ def braid_monodromy(f, arrangement = []): """ global roots_interval_cache if arrangement == []: - arrangement = [f] + arrangement1 = [f] + else: + arrangement1 = arrangement x, y = f.parent().gens() F = f.base_ring() - Ft = PolynomialRing(F, 't') - Ft.inject_variables(verbose = False) ffac = f.factor() g = prod([_[0] for _ in ffac]) d = g.degree(y) while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - arrangement = [f1.subs({x: x + y}) for f1 in arrangement] + arrangement1 = [f1.subs({x: x + y}) for f1 in arrangement1] gfac = g.factor() glist = [_[0] for _ in gfac] disc = discrim(glist) @@ -969,8 +968,9 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots_base = F[y](g.subs(x = p0)).roots(QQbar, multiplicities = False) roots_base.sort() + bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -995,11 +995,8 @@ def braid_monodromy(f, arrangement = []): segsbraids[(beginseg, endseg)] = b segsbraids[(endseg, beginseg)] = b.inverse() end_braid_computation = True -# except ChildProcessError: # hack to deal with random fails first time - except: # hack to deal with random fails first time - #braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) - # for seg in segs])) - print ("retrying braid computation") + except ChildProcessError: # hack to deal with random fails first time + pass #B = b.parent() B=BraidGroup(d) result = [] @@ -1010,18 +1007,21 @@ def braid_monodromy(f, arrangement = []): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - if len(arrangement) == 1: + if len(arrangement1) == 1: return result else: strands = {} - for i, val in enumerate(arrangement): - roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + for i, val in enumerate(arrangement1): + roots = F[y](val.subs(x = p0).roots(QQbar, multiplicities = False) roots.sort() for j in roots: - k = roots_base.index(j) + L = [abs(j - j1) for j1 in roots_base] + k = next([k1 for k1 in range(d) if L[k1] <= bound]) strands[k + 1] = i + 1 return (result, strands) + + def fundamental_group(f, simplified = True, projective = False): r""" Return a presentation of the fundamental group of the complement of From 5de0b06628e9713b5e99a7708512b6cd410c3a35 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 20:48:26 +0200 Subject: [PATCH 011/122] cleaning --- src/sage/schemes/curves/zariski_vankampen.py | 34 ++++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index f4b529f1623..099568ae1bb 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -58,7 +58,6 @@ from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing roots_interval_cache = {} @@ -202,14 +201,14 @@ def discrim_pairs(ftuple): pairs = [((f,),) for f in flist] + [((f, g),) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) poly = 1 - for u in fdiscrim: - poly = poly * u[1] #for f in flist: #aux = F[x](f.discriminant(y)) #poly = aux * poly #for f, g in Combinations(flist, 2): #aux = F[x](f.resultant(g, y)) #poly = aux * poly + for u in fdiscrim: + poly = poly * u[1] return poly.roots(QQbar, multiplicities = False) @@ -943,18 +942,18 @@ def braid_monodromy(f, arrangement = []): """ global roots_interval_cache if arrangement == []: - arrangement = [f] + arrangement1 = [f] + else: + arrangement1 = arrangement x, y = f.parent().gens() F = f.base_ring() - Ft = PolynomialRing(F, 't') - Ft.inject_variables(verbose = False) ffac = f.factor() g = prod([_[0] for _ in ffac]) d = g.degree(y) while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - arrangement = [f1.subs({x: x + y}) for f1 in arrangement] + arrangement1 = [f1.subs({x: x + y}) for f1 in arrangement1] gfac = g.factor() glist = [_[0] for _ in gfac] disc = discrim(glist) @@ -969,8 +968,9 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - roots_base = g.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + roots_base = F[y](g.subs(x = p0)).roots(QQbar, multiplicities = False) roots_base.sort() + bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -995,11 +995,8 @@ def braid_monodromy(f, arrangement = []): segsbraids[(beginseg, endseg)] = b segsbraids[(endseg, beginseg)] = b.inverse() end_braid_computation = True -# except ChildProcessError: # hack to deal with random fails first time - except: # hack to deal with random fails first time - #braidscomputed = (braid_in_segment([(gfac, seg[0], seg[1]) - # for seg in segs])) - print ("retrying braid computation") + except ChildProcessError: # hack to deal with random fails first time + pass #B = b.parent() B=BraidGroup(d) result = [] @@ -1010,18 +1007,21 @@ def braid_monodromy(f, arrangement = []): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - if len(arrangement) == 1: + if len(arrangement1) == 1: return result else: strands = {} - for i, val in enumerate(arrangement): - roots = val.subs(x = p0, y = t).roots(QQbar, multiplicities = False) + for i, val in enumerate(arrangement1): + roots = F[y](val.subs(x = p0).roots(QQbar, multiplicities = False) roots.sort() for j in roots: - k = roots_base.index(j) + L = [abs(j - j1) for j1 in roots_base] + k = next([k1 for k1 in range(d) if L[k1] <= bound]) strands[k + 1] = i + 1 return (result, strands) + + def fundamental_group(f, simplified = True, projective = False): r""" Return a presentation of the fundamental group of the complement of From a6282e57f2ea5e381e7b129176849c800f5673f0 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 21:20:59 +0200 Subject: [PATCH 012/122] going back to compare algebraic numbers --- src/sage/schemes/curves/zariski_vankampen.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 099568ae1bb..4571b634388 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -968,9 +968,9 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - roots_base = F[y](g.subs(x = p0)).roots(QQbar, multiplicities = False) + roots_base = QQbar[y](g.subs({x : p0})).roots(QQbar, multiplicities = False) roots_base.sort() - bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 + #bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -1012,11 +1012,12 @@ def braid_monodromy(f, arrangement = []): else: strands = {} for i, val in enumerate(arrangement1): - roots = F[y](val.subs(x = p0).roots(QQbar, multiplicities = False) + roots = QQbar[y](val.subs({x : p0})).roots(QQbar, multiplicities = False) roots.sort() for j in roots: - L = [abs(j - j1) for j1 in roots_base] - k = next([k1 for k1 in range(d) if L[k1] <= bound]) + #L = [abs(j - j1) for j1 in roots_base] + #k = next((k1 for k1 in range(d) if L[k1] <= bound)) + k = roots_base.index(j) strands[k + 1] = i + 1 return (result, strands) From 3e8531e72eb987255967b87e0b8d3f26084c880d Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 21:22:41 +0200 Subject: [PATCH 013/122] going back to compare algebraic numbers --- src/sage/schemes/curves/zariski_vankampen.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 099568ae1bb..4571b634388 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -968,9 +968,9 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - roots_base = F[y](g.subs(x = p0)).roots(QQbar, multiplicities = False) + roots_base = QQbar[y](g.subs({x : p0})).roots(QQbar, multiplicities = False) roots_base.sort() - bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 + #bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -1012,11 +1012,12 @@ def braid_monodromy(f, arrangement = []): else: strands = {} for i, val in enumerate(arrangement1): - roots = F[y](val.subs(x = p0).roots(QQbar, multiplicities = False) + roots = QQbar[y](val.subs({x : p0})).roots(QQbar, multiplicities = False) roots.sort() for j in roots: - L = [abs(j - j1) for j1 in roots_base] - k = next([k1 for k1 in range(d) if L[k1] <= bound]) + #L = [abs(j - j1) for j1 in roots_base] + #k = next((k1 for k1 in range(d) if L[k1] <= bound)) + k = roots_base.index(j) strands[k + 1] = i + 1 return (result, strands) From 764d1d21ffd5f87d5138e13674fe1e66d3fc2532 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 21:25:41 +0200 Subject: [PATCH 014/122] going back to compare algebraic numbers --- src/sage/schemes/curves/zariski_vankampen.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 58d4b4527d1..f693285deea 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -968,9 +968,9 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - roots_base = F[y](g.subs(x = p0)).roots(QQbar, multiplicities = False) + roots_base = QQbar[y](g.subs({x : p0})).roots(QQbar, multiplicities = False) roots_base.sort() - bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 + #bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -1012,11 +1012,12 @@ def braid_monodromy(f, arrangement = []): else: strands = {} for i, val in enumerate(arrangement1): - roots = F[y](val.subs(x = p0).roots(QQbar, multiplicities = False) + roots = QQbar[y](val.subs({x : p0})).roots(QQbar, multiplicities = False) roots.sort() for j in roots: - L = [abs(j - j1) for j1 in roots_base] - k = next([k1 for k1 in range(d) if L[k1] <= bound]) + #L = [abs(j - j1) for j1 in roots_base] + #k = next((k1 for k1 in range(d) if L[k1] <= bound)) + k = roots_base.index(j) strands[k + 1] = i + 1 return (result, strands) From c38214b675f7171f2f5c51b28e1975a44cce3302 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 21:27:48 +0200 Subject: [PATCH 015/122] branch puiseux --- src/sage/schemes/curves/zariski_vankampen.py | 84 ++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 4571b634388..f693285deea 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1022,6 +1022,90 @@ def braid_monodromy(f, arrangement = []): return (result, strands) +def braid2rels(L, d): + r""" + Return a minimal set of elements of ``F = FreeGroup(d)`` for a quasi-positive braid ``b=BraidGroup(d)(L)`` as relations + of the group ``F / [(b * F([j])) / F([j]) for j in (1..d)]``. One starts from the non-trivial relations determined by the + positive braid and transform them in relations determined by ``b``. + + INPUT: + + - ``L`` -- a list of integers in ``[-d..-1] + [1..d]`` which is the Tietze word of a quasi-positive braid. + + - ``d`` -- a positive integer + + OUTPUT: + + A list of Tietze words for a minimal set of relations of ``F / [(b * F([j])) / F([j]) for j in (1..d)]``. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import braid2rels # optional - sirocco + sage: L = [-3, 5, 2, -5, 3] + sage: braid2rels(L, 6) # optional - sirocco + [(-3, 2)] + """ + from sage.groups.finitely_presented import wrap_FpGroup + B = BraidGroup(d) + F = FreeGroup(d) + L1 = deepcopy(L) + low = True + while low: + L2 = deepcopy(L1) + j = 0 + l = L[j] + other = False + while j < len(L2) - 1 and not other: + try: + k = L2.index(-l) + A = L2[j + 1: k] + Bn = next((_ for _ in A if 0 < (_^2 - l^2)^2 < 2), None) + if B == None: + other = True + L2 = L2[:j] + B + L2[k + 1:] + print (L2) + else: + j += 1 + except ValueError: + j += 1 + low = len(L2) < len(L1) + L1 = deepcopy(L2) + c0 = () + b0 = L1 + while b0[0] + b0[-1] == 0: + c0= c0 + (b0[0],) + b0 = b0[1: -1] + A = B(b0).super_summit_set() + res = None + for tau in A: + sg = B(c0) * B(b0).conjugating_braid(tau) + A1 = sg.right_normal_form() + b = prod(A1[:-1]) + b1 = len(b.Tietze()) / len(A1) + par = (A1[-1].exponent_sum() / d / (d - 1) * 2)%2 + if res == None or b1 < res[3]: + res = [tau, A1[:-1], par, b1] + if res[2] == 1: + res[0] = B([sign(i) * (d - abs(i)) for i in res[0].Tietze()]) + U0 = list(Set(res[0].Tietze())) + U0.sort() + U1 = list(Set(b0)) + U = [(F([j]) * B(b0)) / F([j]) for j in U1] + U = [_.Tietze() for _ in U] + pasos = [B.one()] + [_ for _ in reversed(res[1])] + for C in pasos: + U = [(F(a) * C^-1).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 def fundamental_group(f, simplified = True, projective = False): r""" From 709ca9a5b30eb7226b8b756f5d748d01fa18bd4a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 21:36:44 +0200 Subject: [PATCH 016/122] typos for test --- src/sage/schemes/curves/zariski_vankampen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index f693285deea..9df6ed97b7a 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -39,9 +39,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** import itertools -from copy import copy +from copy import copy, deepcopy from sage.combinat.combination import Combinations - +from sage.sets.set import Set from sage.combinat.permutation import Permutation from sage.geometry.voronoi_diagram import VoronoiDiagram @@ -1094,7 +1094,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in U] pasos = [B.one()] + [_ for _ in reversed(res[1])] for C in pasos: - U = [(F(a) * C^-1).Tietze() for a in U] + U = [(F(a) * C**(-1)).Tietze() for a in U] ga = F / U P = ga.gap().PresentationFpGroup() dic = P.TzOptions().sage() From 3ca1c98389304bef946180ffb0eb4fb323346e17 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 2 Sep 2022 21:38:06 +0200 Subject: [PATCH 017/122] tests --- src/sage/schemes/curves/zariski_vankampen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index f693285deea..9df6ed97b7a 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -39,9 +39,9 @@ # https://www.gnu.org/licenses/ # **************************************************************************** import itertools -from copy import copy +from copy import copy, deepcopy from sage.combinat.combination import Combinations - +from sage.sets.set import Set from sage.combinat.permutation import Permutation from sage.geometry.voronoi_diagram import VoronoiDiagram @@ -1094,7 +1094,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in U] pasos = [B.one()] + [_ for _ in reversed(res[1])] for C in pasos: - U = [(F(a) * C^-1).Tietze() for a in U] + U = [(F(a) * C**(-1)).Tietze() for a in U] ga = F / U P = ga.gap().PresentationFpGroup() dic = P.TzOptions().sage() From f4895733f1c62fe8249bf401b38387ee99b78a61 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 3 Sep 2022 17:59:38 +0200 Subject: [PATCH 018/122] improving relationship of strands and components --- src/sage/schemes/curves/zariski_vankampen.py | 71 +++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9df6ed97b7a..2724e4075a7 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -968,8 +968,6 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - roots_base = QQbar[y](g.subs({x : p0})).roots(QQbar, multiplicities = False) - roots_base.sort() #bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 geombasis = geometric_basis(G, E, p) segs = set() @@ -1011,17 +1009,12 @@ def braid_monodromy(f, arrangement = []): return result else: strands = {} - for i, val in enumerate(arrangement1): - roots = QQbar[y](val.subs({x : p0})).roots(QQbar, multiplicities = False) - roots.sort() - for j in roots: - #L = [abs(j - j1) for j1 in roots_base] - #k = next((k1 for k1 in range(d) if L[k1] <= bound)) - k = roots_base.index(j) - strands[k + 1] = i + 1 + roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] + roots_base.sort() + strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} return (result, strands) - +@parallel def braid2rels(L, d): r""" Return a minimal set of elements of ``F = FreeGroup(d)`` for a quasi-positive braid ``b=BraidGroup(d)(L)`` as relations @@ -1041,9 +1034,9 @@ def braid2rels(L, d): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid2rels # optional - sirocco - sage: L = [-3, 5, 2, -5, 3] - sage: braid2rels(L, 6) # optional - sirocco - [(-3, 2)] + sage: L = (1, 3, 2, -3, 1, 1) + sage: braid2rels(L, 4) # optional - sirocco + [(4, 1, -2, -1), (2, -4, -2, 1)] """ from sage.groups.finitely_presented import wrap_FpGroup B = BraidGroup(d) @@ -1063,7 +1056,6 @@ def braid2rels(L, d): if B == None: other = True L2 = L2[:j] + B + L2[k + 1:] - print (L2) else: j += 1 except ValueError: @@ -1076,21 +1068,25 @@ def braid2rels(L, d): c0= c0 + (b0[0],) b0 = b0[1: -1] A = B(b0).super_summit_set() + A.sort() res = None for tau in A: sg = B(c0) * B(b0).conjugating_braid(tau) A1 = sg.right_normal_form() - b = prod(A1[:-1]) + if len(A1)==1: + b = B.one() + else: + b = prod(A1[:-1]) b1 = len(b.Tietze()) / len(A1) par = (A1[-1].exponent_sum() / d / (d - 1) * 2)%2 if res == None or b1 < res[3]: res = [tau, A1[:-1], par, b1] if res[2] == 1: - res[0] = B([sign(i) * (d - abs(i)) for i in res[0].Tietze()]) + r0 = res[0].Tietze() + res[0] = B([i.sign() * (d - abs(i)) for i in r0]) U0 = list(Set(res[0].Tietze())) U0.sort() - U1 = list(Set(b0)) - U = [(F([j]) * B(b0)) / F([j]) for j in U1] + U = [(F([j]) * B(res[0])) / F([j]) for j in U0] U = [_.Tietze() for _ in U] pasos = [B.one()] + [_ for _ in reversed(res[1])] for C in pasos: @@ -1107,7 +1103,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in gb.relations()] return U -def fundamental_group(f, simplified = True, projective = False): +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``. @@ -1125,7 +1121,12 @@ 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 + - ``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 will be computed, adding + one relation if ``projective`` is set to ``True``. + + If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. OUTPUT: @@ -1165,6 +1166,18 @@ def fundamental_group(f, simplified = True, projective = False): sage: f = y^3 + x^3 + zeta * x + 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > + + We compute the fundamental group of the complement of a quartic with ``Puiseux = True``:: + + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco + 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); print (g) # optional - sirocco + Finitely presented group < x0, x1, x2, x3 | x3^-1*x2^-1*x0*x2, x1*x2*x1^-1*x0^-1, x0*x2^-1*x1^-1*x2, x3*x1^-1 > + sage: g.simplified() # optional - sirocco + Finitely presented group < x0, x1 | x1^-1*x0^2*x1^-1, (x1*x0^-1)^3 > + sage: g = fundamental_group(f, puiseux = True, projective = True); print (g.order(), g.abelian_invariants()) # optional - sirocco + 12 (4,) """ g = f if projective: @@ -1172,14 +1185,22 @@ def fundamental_group(f, simplified = True, projective = False): while g.degree(y) < g.degree(): g = g.subs({x: x + y}) bm = braid_monodromy(g) - n = bm[0].parent().strands() - F = FreeGroup(n) + d = bm[0].parent().strands() + F = FreeGroup(d) @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] + + if not puiseux: + relations = (relation([(x, b) for x in F.gens() for b in bm])) + R = [r[1] for r in relations] + else: + simplified = False + relations = (braid2rels([(b.Tietze(),d) for b in bm])) + R = [] + for r in relations: + R += r[1] if projective: R.append(prod(F.gens())) G = F / R From 2767a2204ab3c51a707f0e7e323346b0b219870e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 3 Sep 2022 18:01:19 +0200 Subject: [PATCH 019/122] improving relationship of strands and components --- src/sage/schemes/curves/zariski_vankampen.py | 71 +++++++++++++------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9df6ed97b7a..2724e4075a7 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -968,8 +968,6 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - roots_base = QQbar[y](g.subs({x : p0})).roots(QQbar, multiplicities = False) - roots_base.sort() #bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 geombasis = geometric_basis(G, E, p) segs = set() @@ -1011,17 +1009,12 @@ def braid_monodromy(f, arrangement = []): return result else: strands = {} - for i, val in enumerate(arrangement1): - roots = QQbar[y](val.subs({x : p0})).roots(QQbar, multiplicities = False) - roots.sort() - for j in roots: - #L = [abs(j - j1) for j1 in roots_base] - #k = next((k1 for k1 in range(d) if L[k1] <= bound)) - k = roots_base.index(j) - strands[k + 1] = i + 1 + roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] + roots_base.sort() + strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} return (result, strands) - +@parallel def braid2rels(L, d): r""" Return a minimal set of elements of ``F = FreeGroup(d)`` for a quasi-positive braid ``b=BraidGroup(d)(L)`` as relations @@ -1041,9 +1034,9 @@ def braid2rels(L, d): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import braid2rels # optional - sirocco - sage: L = [-3, 5, 2, -5, 3] - sage: braid2rels(L, 6) # optional - sirocco - [(-3, 2)] + sage: L = (1, 3, 2, -3, 1, 1) + sage: braid2rels(L, 4) # optional - sirocco + [(4, 1, -2, -1), (2, -4, -2, 1)] """ from sage.groups.finitely_presented import wrap_FpGroup B = BraidGroup(d) @@ -1063,7 +1056,6 @@ def braid2rels(L, d): if B == None: other = True L2 = L2[:j] + B + L2[k + 1:] - print (L2) else: j += 1 except ValueError: @@ -1076,21 +1068,25 @@ def braid2rels(L, d): c0= c0 + (b0[0],) b0 = b0[1: -1] A = B(b0).super_summit_set() + A.sort() res = None for tau in A: sg = B(c0) * B(b0).conjugating_braid(tau) A1 = sg.right_normal_form() - b = prod(A1[:-1]) + if len(A1)==1: + b = B.one() + else: + b = prod(A1[:-1]) b1 = len(b.Tietze()) / len(A1) par = (A1[-1].exponent_sum() / d / (d - 1) * 2)%2 if res == None or b1 < res[3]: res = [tau, A1[:-1], par, b1] if res[2] == 1: - res[0] = B([sign(i) * (d - abs(i)) for i in res[0].Tietze()]) + r0 = res[0].Tietze() + res[0] = B([i.sign() * (d - abs(i)) for i in r0]) U0 = list(Set(res[0].Tietze())) U0.sort() - U1 = list(Set(b0)) - U = [(F([j]) * B(b0)) / F([j]) for j in U1] + U = [(F([j]) * B(res[0])) / F([j]) for j in U0] U = [_.Tietze() for _ in U] pasos = [B.one()] + [_ for _ in reversed(res[1])] for C in pasos: @@ -1107,7 +1103,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in gb.relations()] return U -def fundamental_group(f, simplified = True, projective = False): +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``. @@ -1125,7 +1121,12 @@ 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 + - ``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 will be computed, adding + one relation if ``projective`` is set to ``True``. + + If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. OUTPUT: @@ -1165,6 +1166,18 @@ def fundamental_group(f, simplified = True, projective = False): sage: f = y^3 + x^3 + zeta * x + 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > + + We compute the fundamental group of the complement of a quartic with ``Puiseux = True``:: + + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco + 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); print (g) # optional - sirocco + Finitely presented group < x0, x1, x2, x3 | x3^-1*x2^-1*x0*x2, x1*x2*x1^-1*x0^-1, x0*x2^-1*x1^-1*x2, x3*x1^-1 > + sage: g.simplified() # optional - sirocco + Finitely presented group < x0, x1 | x1^-1*x0^2*x1^-1, (x1*x0^-1)^3 > + sage: g = fundamental_group(f, puiseux = True, projective = True); print (g.order(), g.abelian_invariants()) # optional - sirocco + 12 (4,) """ g = f if projective: @@ -1172,14 +1185,22 @@ def fundamental_group(f, simplified = True, projective = False): while g.degree(y) < g.degree(): g = g.subs({x: x + y}) bm = braid_monodromy(g) - n = bm[0].parent().strands() - F = FreeGroup(n) + d = bm[0].parent().strands() + F = FreeGroup(d) @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] + + if not puiseux: + relations = (relation([(x, b) for x in F.gens() for b in bm])) + R = [r[1] for r in relations] + else: + simplified = False + relations = (braid2rels([(b.Tietze(),d) for b in bm])) + R = [] + for r in relations: + R += r[1] if projective: R.append(prod(F.gens())) G = F / R From 89e80eefa6735dd9c126f2a9cc23f2392229a68a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 3 Sep 2022 18:18:10 +0200 Subject: [PATCH 020/122] lint corrections --- src/sage/schemes/curves/zariski_vankampen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 2724e4075a7..9c1e7d5c244 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1053,7 +1053,7 @@ def braid2rels(L, d): k = L2.index(-l) A = L2[j + 1: k] Bn = next((_ for _ in A if 0 < (_^2 - l^2)^2 < 2), None) - if B == None: + if B is None: other = True L2 = L2[:j] + B + L2[k + 1:] else: @@ -1079,7 +1079,7 @@ def braid2rels(L, d): b = prod(A1[:-1]) b1 = len(b.Tietze()) / len(A1) par = (A1[-1].exponent_sum() / d / (d - 1) * 2)%2 - if res == None or b1 < res[3]: + if res is None or b1 < res[3]: res = [tau, A1[:-1], par, b1] if res[2] == 1: r0 = res[0].Tietze() From 598ce7f43e8d335a8adbc18926436ddd2fde28f5 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 3 Sep 2022 18:18:55 +0200 Subject: [PATCH 021/122] lint corrections --- src/sage/schemes/curves/zariski_vankampen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 2724e4075a7..9c1e7d5c244 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1053,7 +1053,7 @@ def braid2rels(L, d): k = L2.index(-l) A = L2[j + 1: k] Bn = next((_ for _ in A if 0 < (_^2 - l^2)^2 < 2), None) - if B == None: + if B is None: other = True L2 = L2[:j] + B + L2[k + 1:] else: @@ -1079,7 +1079,7 @@ def braid2rels(L, d): b = prod(A1[:-1]) b1 = len(b.Tietze()) / len(A1) par = (A1[-1].exponent_sum() / d / (d - 1) * 2)%2 - if res == None or b1 < res[3]: + if res is None or b1 < res[3]: res = [tau, A1[:-1], par, b1] if res[2] == 1: r0 = res[0].Tietze() From 73a555b8f5c5364ccd60633fdbf676c843ed8e6c Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 4 Sep 2022 16:10:38 +0200 Subject: [PATCH 022/122] adding optional arguments --- src/sage/schemes/curves/affine_curve.py | 4 +- src/sage/schemes/curves/zariski_vankampen.py | 84 +++++++++++++++++--- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index bcdced80990..57657963665 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1691,7 +1691,7 @@ class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): """ _point = AffinePlaneCurvePoint_field - def fundamental_group(self): + def fundamental_group(self, puiseux = False, braidmonodromy = None): r""" Return a presentation of the fundamental group of the complement of ``self``. @@ -1731,7 +1731,7 @@ def fundamental_group(self): raise NotImplementedError("the base field must have an embedding" " to the algebraic field") f = self.defining_polynomial() - return fundamental_group(f, projective=False) + return fundamental_group(f, projective=False, puiseux = puiseux, braidmonodromy = braidmonodromy) def braid_monodromy(self): r""" diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9c1e7d5c244..e0ab4d7931d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -901,7 +901,7 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f, arrangement = []): +def braid_monodromy(f, arrangement = [], computebm = True): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -912,6 +912,11 @@ def braid_monodromy(f, arrangement = []): - ``arrangement`` -- an optional list of polynomials whose product equals ``f``, in order to provide information for `braid_monodromy_arrangement`. + + - ``computebm`` -- an optional boolean variable (default ``True``). It is set to False, only the + string assignment is given. t makes only sense if arrangement has + more than one polynomial + OUTPUT: @@ -920,9 +925,10 @@ def braid_monodromy(f, arrangement = []): each of this paths is the conjugated of a loop around one of the points in the discriminant of the projection of ``f``. - If ``arrangement`` contains more than one element, some information to be use by - `braid_monodromy_arrangement` is provided. - + If ``arrangement`` contains more than one element, some information to be used by + `braid_monodromy_arrangement` is provided. + + If ``computebm`` is set to ``False`` some information to be used by ``strand_components`` is given. .. NOTE:: @@ -968,7 +974,13 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - #bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 + if len(arrangement1) > 1: + strands = {} + roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] + roots_base.sort() + strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} + if not computebm: + return strands geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -1008,10 +1020,6 @@ def braid_monodromy(f, arrangement = []): if len(arrangement1) == 1: return result else: - strands = {} - roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] - roots_base.sort() - strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} return (result, strands) @parallel @@ -1103,7 +1111,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in gb.relations()] return U -def fundamental_group(f, simplified = True, projective = False, puiseux = False): +def fundamental_group(f, simplified = True, projective = False, puiseux = False, braidmonodromy = None): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. @@ -1125,6 +1133,9 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False) 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``. + + - ``braidmonodromy`` -- (default: ``None``); if it is set to a list of braids + braid monodromy is not computed ad this list is used instead. If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. @@ -1178,13 +1189,28 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False) Finitely presented group < x0, x1 | x1^-1*x0^2*x1^-1, (x1*x0^-1)^3 > sage: g = fundamental_group(f, puiseux = True, projective = True); print (g.order(), g.abelian_invariants()) # optional - sirocco 12 (4,) + + We compute first a braid monodromy and use it for the computation of the fundamental group:: + + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy # optional - sirocco + sage: R. = QQ[] + sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) + sage: bm = braid_monodromy(f); print(bm) # optional - sirocco + [s0^-1*s1*s2*s0*s1*s0^-1*s1^-1, (s1*s0)^2, s0^-1*s1^-1*s0^-1*s1*s0*s2*s1] + sage: g = fundamental_group(f, projective = True, braidmonodromy = bm); print (g) # optional - sirocco + Finitely presented group < x0, x2 | x0^2*x2^2, x0^2*x2^-2, x2*(x0^-1*x2^-1)^2*x0^-1 > + sage: print (g.order(), g.abelian_invariants()) # optional - sirocco + 12 (4,) """ g = f if projective: x, y = g.parent().gens() while g.degree(y) < g.degree(): g = g.subs({x: x + y}) - bm = braid_monodromy(g) + if braidmonodromy is None: + bm = braid_monodromy(g) + else: + bm =braidmonodromy d = bm[0].parent().strands() F = FreeGroup(d) @@ -1253,3 +1279,39 @@ def braid_monodromy_arrangement(flist): dic ={j + 1 : 1 for j in range(d)} return (braid_monodromy(f), dic) return braid_monodromy(f,arrangement = flist) + +def strand_components(flist): + r""" + Compute the braid monodromy of a projection of the curve + defined by a list of polynomials with the extra information about the correspondence of strands + and elements of the list. + + INPUT: + + - ``flist`` -- a list of polynomial with two variables, over a number field + with an embedding in the complex numbers + + + OUTPUT: + + - A dictionary attaching a number `i` (strand) to a number `j` (a polynomial in the list). + + .. NOTE:: + + The projection over the `x` axis is used if there are no vertical asymptotes. + Otherwise, a linear change of variables is done to fall into the previous case. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import strand_components # optional - sirocco + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3 * y - 5] + sage: strand_components(flist) + {1: 1, 2: 2, 3: 1, 4: 1} + """ + f = prod(flist) + if len(flist) == 1: + d = f.degree() + dic ={j + 1 : 1 for j in range(d)} + return dic + return braid_monodromy(f,arrangement = flist, computebm = False) From bd51861a08244739c93e05522b98b28bf9c4f6a0 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 4 Sep 2022 16:15:11 +0200 Subject: [PATCH 023/122] adding optional arguments --- src/sage/schemes/curves/affine_curve.py | 4 +- src/sage/schemes/curves/zariski_vankampen.py | 84 +++++++++++++++++--- 2 files changed, 75 insertions(+), 13 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index bcdced80990..57657963665 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1691,7 +1691,7 @@ class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): """ _point = AffinePlaneCurvePoint_field - def fundamental_group(self): + def fundamental_group(self, puiseux = False, braidmonodromy = None): r""" Return a presentation of the fundamental group of the complement of ``self``. @@ -1731,7 +1731,7 @@ def fundamental_group(self): raise NotImplementedError("the base field must have an embedding" " to the algebraic field") f = self.defining_polynomial() - return fundamental_group(f, projective=False) + return fundamental_group(f, projective=False, puiseux = puiseux, braidmonodromy = braidmonodromy) def braid_monodromy(self): r""" diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9c1e7d5c244..e0ab4d7931d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -901,7 +901,7 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f, arrangement = []): +def braid_monodromy(f, arrangement = [], computebm = True): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -912,6 +912,11 @@ def braid_monodromy(f, arrangement = []): - ``arrangement`` -- an optional list of polynomials whose product equals ``f``, in order to provide information for `braid_monodromy_arrangement`. + + - ``computebm`` -- an optional boolean variable (default ``True``). It is set to False, only the + string assignment is given. t makes only sense if arrangement has + more than one polynomial + OUTPUT: @@ -920,9 +925,10 @@ def braid_monodromy(f, arrangement = []): each of this paths is the conjugated of a loop around one of the points in the discriminant of the projection of ``f``. - If ``arrangement`` contains more than one element, some information to be use by - `braid_monodromy_arrangement` is provided. - + If ``arrangement`` contains more than one element, some information to be used by + `braid_monodromy_arrangement` is provided. + + If ``computebm`` is set to ``False`` some information to be used by ``strand_components`` is given. .. NOTE:: @@ -968,7 +974,13 @@ def braid_monodromy(f, arrangement = []): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - #bound = min([(i - j).norm() for i, j in Combinations(roots_base,2)])/2 + if len(arrangement1) > 1: + strands = {} + roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] + roots_base.sort() + strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} + if not computebm: + return strands geombasis = geometric_basis(G, E, p) segs = set() for p in geombasis: @@ -1008,10 +1020,6 @@ def braid_monodromy(f, arrangement = []): if len(arrangement1) == 1: return result else: - strands = {} - roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] - roots_base.sort() - strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} return (result, strands) @parallel @@ -1103,7 +1111,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in gb.relations()] return U -def fundamental_group(f, simplified = True, projective = False, puiseux = False): +def fundamental_group(f, simplified = True, projective = False, puiseux = False, braidmonodromy = None): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. @@ -1125,6 +1133,9 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False) 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``. + + - ``braidmonodromy`` -- (default: ``None``); if it is set to a list of braids + braid monodromy is not computed ad this list is used instead. If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. @@ -1178,13 +1189,28 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False) Finitely presented group < x0, x1 | x1^-1*x0^2*x1^-1, (x1*x0^-1)^3 > sage: g = fundamental_group(f, puiseux = True, projective = True); print (g.order(), g.abelian_invariants()) # optional - sirocco 12 (4,) + + We compute first a braid monodromy and use it for the computation of the fundamental group:: + + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy # optional - sirocco + sage: R. = QQ[] + sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) + sage: bm = braid_monodromy(f); print(bm) # optional - sirocco + [s0^-1*s1*s2*s0*s1*s0^-1*s1^-1, (s1*s0)^2, s0^-1*s1^-1*s0^-1*s1*s0*s2*s1] + sage: g = fundamental_group(f, projective = True, braidmonodromy = bm); print (g) # optional - sirocco + Finitely presented group < x0, x2 | x0^2*x2^2, x0^2*x2^-2, x2*(x0^-1*x2^-1)^2*x0^-1 > + sage: print (g.order(), g.abelian_invariants()) # optional - sirocco + 12 (4,) """ g = f if projective: x, y = g.parent().gens() while g.degree(y) < g.degree(): g = g.subs({x: x + y}) - bm = braid_monodromy(g) + if braidmonodromy is None: + bm = braid_monodromy(g) + else: + bm =braidmonodromy d = bm[0].parent().strands() F = FreeGroup(d) @@ -1253,3 +1279,39 @@ def braid_monodromy_arrangement(flist): dic ={j + 1 : 1 for j in range(d)} return (braid_monodromy(f), dic) return braid_monodromy(f,arrangement = flist) + +def strand_components(flist): + r""" + Compute the braid monodromy of a projection of the curve + defined by a list of polynomials with the extra information about the correspondence of strands + and elements of the list. + + INPUT: + + - ``flist`` -- a list of polynomial with two variables, over a number field + with an embedding in the complex numbers + + + OUTPUT: + + - A dictionary attaching a number `i` (strand) to a number `j` (a polynomial in the list). + + .. NOTE:: + + The projection over the `x` axis is used if there are no vertical asymptotes. + Otherwise, a linear change of variables is done to fall into the previous case. + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import strand_components # optional - sirocco + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3 * y - 5] + sage: strand_components(flist) + {1: 1, 2: 2, 3: 1, 4: 1} + """ + f = prod(flist) + if len(flist) == 1: + d = f.degree() + dic ={j + 1 : 1 for j in range(d)} + return dic + return braid_monodromy(f,arrangement = flist, computebm = False) From 052a2c292323bc592b4d9fed70eb7e0dbcb3493a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 5 Sep 2022 11:19:41 +0200 Subject: [PATCH 024/122] Add more functions and methods --- src/sage/schemes/curves/zariski_vankampen.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index f8bfa82459d..e0ab4d7931d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -974,7 +974,6 @@ def braid_monodromy(f, arrangement = [], computebm = True): p = next(E.vertex_iterator()) I = QQbar.gen() p0 = p[0] + I * p[1] - if len(arrangement1) > 1: strands = {} roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] @@ -1017,7 +1016,6 @@ def braid_monodromy(f, arrangement = [], computebm = True): x0 = tuple(path[i].vector()) x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] - result.append(braidpath) if len(arrangement1) == 1: return result @@ -1135,12 +1133,10 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, 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``. - - ``braidmonodromy`` -- (default: ``None``); if it is set to a list of braids braid monodromy is not computed ad this list is used instead. - If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. @@ -1211,7 +1207,6 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, x, y = g.parent().gens() while g.degree(y) < g.degree(): g = g.subs({x: x + y}) - if braidmonodromy is None: bm = braid_monodromy(g) else: From 3ab76f9289ff2639471c21902344e5b01bc9c202 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 6 Sep 2022 12:43:04 +0200 Subject: [PATCH 025/122] discrim was too long --- src/sage/schemes/curves/zariski_vankampen.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index e0ab4d7931d..e4aadcb524d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -200,6 +200,7 @@ def discrim_pairs(ftuple): pairs = [((f,),) for f in flist] + [((f, g),) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) + rts = [] poly = 1 #for f in flist: #aux = F[x](f.discriminant(y)) @@ -208,8 +209,11 @@ def discrim_pairs(ftuple): #aux = F[x](f.resultant(g, y)) #poly = aux * poly for u in fdiscrim: - poly = poly * u[1] - return poly.roots(QQbar, multiplicities = False) + h0 = u[1].radical() + h1 = F[x](h0 / h0.gcd(poly)) + rts += h1.roots(QQbar, multiplicities = False) + poly = poly * h1 + return rts @cached_function From 7b05103f1c9f0edecf1638f8db6488eccee3451e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 6 Sep 2022 18:20:23 +0200 Subject: [PATCH 026/122] discrim was too long --- src/sage/schemes/curves/zariski_vankampen.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index e0ab4d7931d..90fd4b845ee 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -200,6 +200,7 @@ def discrim_pairs(ftuple): pairs = [((f,),) for f in flist] + [((f, g),) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) + rts = [] poly = 1 #for f in flist: #aux = F[x](f.discriminant(y)) @@ -208,8 +209,11 @@ def discrim_pairs(ftuple): #aux = F[x](f.resultant(g, y)) #poly = aux * poly for u in fdiscrim: - poly = poly * u[1] - return poly.roots(QQbar, multiplicities = False) + h0 = u[1].radical() + h1 = F[x](h0 / h0.gcd(poly)) + rts += h1.roots(QQbar, multiplicities = False) + poly = poly * h1 + return rts @cached_function @@ -1275,6 +1279,7 @@ def braid_monodromy_arrangement(flist): """ f = prod(flist) if len(flist) == 1: + print ("hola") d = f.degree() dic ={j + 1 : 1 for j in range(d)} return (braid_monodromy(f), dic) From 889daf95c1535c19c9a1a3b6207646c8f6af9e6f Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 6 Sep 2022 23:39:27 +0200 Subject: [PATCH 027/122] delete some print --- src/sage/schemes/curves/zariski_vankampen.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 90fd4b845ee..e4aadcb524d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1279,7 +1279,6 @@ def braid_monodromy_arrangement(flist): """ f = prod(flist) if len(flist) == 1: - print ("hola") d = f.degree() dic ={j + 1 : 1 for j in range(d)} return (braid_monodromy(f), dic) From c0ed8577e7381346fea9af0d6a5ac60c016a9560 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 13 Sep 2022 17:27:54 +0200 Subject: [PATCH 028/122] Changes using more algebraic numbers; some tests do not hold --- src/sage/schemes/curves/zariski_vankampen.py | 370 ++++++++++++------- 1 file changed, 244 insertions(+), 126 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index e4aadcb524d..999bccc4c54 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -53,6 +53,7 @@ from sage.misc.flatten import flatten from sage.misc.misc_c import prod from sage.parallel.decorate import parallel +from sage.rings.all import NumberField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.complex_mpfr import ComplexField from sage.rings.qqbar import QQbar @@ -166,7 +167,7 @@ def discrim(flist): INPUT: - - ``flist`` -- a list of polynomials in two variables with coefficients in a + - ``flist`` -- a tuple of polynomials in two variables with coefficients in a number field with a fixed embedding in `\QQbar` OUTPUT: @@ -177,14 +178,14 @@ def discrim(flist): sage: from sage.schemes.curves.zariski_vankampen import discrim sage: R. = QQ[] - sage: flist = [y^3 + x^3 - 1, 2 * x + y] + sage: flist = (y^3 + x^3 - 1, 2 * x + y) sage: discrim(flist) # optional - sirocco - [-0.522757958574711?, - 1, + [1, -0.500000000000000? - 0.866025403784439?*I, -0.500000000000000? + 0.866025403784439?*I, + -0.522757958574711?, 0.2613789792873551? - 0.4527216721561923?*I, - 0.2613789792873551? + 0.4527216721561923?*I] + 0.2613789792873551? + 0.4527216721561923?*I] """ x, y = flist[0].parent().gens() F = flist[0].base_ring() @@ -457,20 +458,66 @@ def newton(f, x0, i0): """ return x0 - f(x0) / f.derivative()(i0) +def fieldI(F0): + r""" + Return the (either double or trivial) extension of a number field which contains `I`. + + INPUT: + + - ``F0`` -- a number field with an embedding in `QQbar`. + + OUTPUT: + + The embedding in `QQbar` of an extension `F` of `F0` containing `I`. + + 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 + + + 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 F0: + return F0 + else: + F0a = F0[I0] + F1a = F0a.absolute_field('b0') + b0 = F1a.gen() + q = b0.minpoly() + qroots = q.roots(QQbar, multiplicities = False) + for b1 in qroots: + F1 = NumberField(q, 'b', embedding = b1) + b = F1.gen() + if (F0.is_subring(F1)): + break + return F1 @parallel -def roots_interval(f, x0): +def roots_interval(flist, x0): """ Find disjoint intervals that isolate the roots of a polynomial for a fixed value of the first variable. INPUT: - - ``f`` -- a bivariate squarefree polynomial + - ``flist`` -- a tuple of bivariate squarefree polynomials - ``x0`` -- a value where the first coordinate will be fixed The intervals are taken as big as possible to be able to detect when two - approximate roots of `f(x_0, y)` correspond to the same exact root. + 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 @@ -479,9 +526,9 @@ def roots_interval(f, x0): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import roots_interval - sage: R. = QQ[] - sage: f = y^3 - x^2 - sage: ri = roots_interval(f, 1) + sage: R. = QQ[] + sage: flist = (y^3 - x^2, ) + sage: ri = roots_interval(flist, 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, 138907099/160396102*I - 1/2: -1.? + 1.?*I, @@ -500,10 +547,18 @@ def roots_interval(f, x0): -0.933012701892219 + 1.29903810567666*I, -0.0669872981077806 + 0.433012701892219*I)] """ - x, y = f.parent().gens() - I = QQbar.gen() - fx = QQbar[y](f.subs({x: QQ(x0.real()) + I * QQ(x0.imag())})) - roots = fx.roots(QQbar, multiplicities=False) + f = prod(flist) + F0 = f.base_ring() + F1 = fieldI(F0) + I1 = F1(QQbar.gen()) + U1 = [_.change_ring(F1) for _ in flist] + f1 = f.change_ring(F1) + x, y = f1.parent().gens() + Ux = [F1[y](_.subs({x: F1(x0)})) for _ in U1] + fx = prod(Ux) + roots = [] + for _ in Ux: + roots += _.roots(QQbar, multiplicities=False) result = {} for i, r in enumerate(roots): prec = 53 @@ -528,7 +583,7 @@ def roots_interval(f, x0): return result -def roots_interval_cached(f, x0): +def roots_interval_cached(flist, x0): r""" Cached version of :func:`roots_interval`. @@ -537,22 +592,22 @@ def roots_interval_cached(f, x0): sage: from sage.schemes.curves.zariski_vankampen import roots_interval, roots_interval_cached, roots_interval_cache sage: R. = QQ[] sage: f = y^3 - x^2 - sage: (f, 1) in roots_interval_cache + sage: ((f, ), 1) in roots_interval_cache False - sage: ri = roots_interval_cached(f, 1) + sage: ri = roots_interval_cached((f, ), 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, 138907099/160396102*I - 1/2: -1.? + 1.?*I, 1: 1.? + 0.?*I} - sage: (f, 1) in roots_interval_cache + sage: ((f, ), 1) in roots_interval_cache True """ global roots_interval_cache try: - return roots_interval_cache[(f, x0)] + return roots_interval_cache[(flist, x0)] except KeyError: - result = roots_interval(f, x0) - roots_interval_cache[(f, x0)] = result + result = roots_interval(flist, x0) + roots_interval_cache[(flist, x0)] = result return result @@ -563,19 +618,19 @@ def populate_roots_interval_cache(inputs): INPUT: - - ``inputs`` -- a list of tuples (f, x0) + - ``inputs`` -- a list of tuples (flist, x0) EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import populate_roots_interval_cache, roots_interval_cache sage: R. = QQ[] sage: f = y^5 - x^2 - sage: (f, 3) in roots_interval_cache + sage: ((f, ), 3) in roots_interval_cache False - sage: populate_roots_interval_cache([(f, 3)]) - sage: (f, 3) in roots_interval_cache + sage: populate_roots_interval_cache([((f, ), 3)]) + sage: ((f, ), 3) in roots_interval_cache True - sage: roots_interval_cache[(f, 3)] + sage: roots_interval_cache[((f,), 3)] {-1.255469441943070? - 0.9121519421827974?*I: -2.? - 1.?*I, -1.255469441943070? + 0.9121519421827974?*I: -2.? + 1.?*I, 0.4795466549853897? - 1.475892845355996?*I: 1.? - 2.?*I, @@ -585,22 +640,36 @@ 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 = False + while not problem_par: + try: + #with open("debug.txt","a") as fd: + #print("Entra en populate",file=fd) + result = roots_interval(tocompute) + #with open("debug.txt","a") as fd: + #print("Sale en populate",file=fd) + for r in result: + roots_interval_cache[r[0][0]] = r[1] + problem_par = True + #with open("debug.txt","a") as fd: + #print("Fin populate",file=fd) + except TypeError: + #with open("debug.txt","a") as fd: + #print("Falla en populate",file=fd) + pass @parallel -def braid_in_segment(g, x0, x1): +def braid_in_segment(glist, x0, x1, precision = {}): """ 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 complex number, usually a Gauss rational + - ``x1`` -- a complex number, usually a Gauss rational OUTPUT: @@ -611,9 +680,9 @@ def braid_in_segment(g, x0, x1): sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # optional - sirocco sage: R. = 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: x0 = 1 + sage: x1 = 1 + I / 2 + sage: braid_in_segment(tuple(_[0] for _ in f.factor()), x0, x1) # optional - sirocco s1 TESTS: @@ -621,46 +690,61 @@ def braid_in_segment(g, x0, x1): Check that :trac:`26503` is fixed:: sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0] - sage: Kw. = NumberField(wp.minpoly(), embedding=wp) + 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: f = y * (y + z) * x * (x - 1) * (x - y) * (x + z * y - 1) * (x + z * y + wp) sage: from sage.schemes.curves import zariski_vankampen as zvk - sage: g = f.subs({x: x + 2*y}) - sage: p1 = QQbar(sqrt(-1/3)) - sage: p2 = QQbar(1/2+sqrt(-1/3)/2) - sage: B = zvk.braid_in_segment(g.factor(),CC(p1),CC(p2)) # optional - sirocco + sage: g = f.subs({x: x + 2 * y}) + sage: p1 = QQbar(sqrt(- 1 / 3)) + sage: p1 = QQ(p1.real()) + I * QQ(p1.imag()) + sage: glist = tuple([_[0] for _ in g.factor()]) + sage: B = zvk.braid_in_segment(glist, CC(p1), CC(p2)) # optional - sirocco sage: 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()) + g = prod(glist) + F0 = g.base_ring() + F1 = fieldI(F0) + I1 = F1(QQbar.gen()) + U1 = tuple(_.change_ring(F1) for _ in glist) + g1 = g.change_ring(F1) + x, y = g.parent().gens() + X0 = QQ(x0.real()) + I1 * QQ(x0.imag()) + X1 = QQ(x1.real()) + I1 * QQ(x1.imag()) + #X0 = F1(x0) + #X1 = F1(x1) intervals = {} - precision = {} +# precision = {} + if precision == {}: # new + precision ={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 + #precision[f] = 53 while True: CIFp = ComplexIntervalField(precision[f]) intervals[f] = [r.interval(CIFp) for r in y0sf] if not any(a.overlaps(b) for a, b in itertools.combinations(intervals[f], 2)): break precision[f] *= 2 - strands = [followstrand(f[0], [p[0] for p in g if p[0] != f[0]], x0, x1, i.center(), precision[f[0]]) for f in g for i in intervals[f[0]]] + 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(), precision[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(glist, X0) + finalintervals = roots_interval_cached(glist, X1) + I = QQbar.gen() for cs in complexstrands: ip = cs[0][1] + I * cs[0][2] fp = cs[-1][1] + I * cs[-1][2] @@ -669,19 +753,25 @@ def braid_in_segment(g, x0, x1): 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: +# if matched == 0: +# raise ValueError("unable to match braid endpoint with root") + precision = {f: precision[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision = precision) # new +# if matched > 1: +# raise ValueError("braid endpoint mathes more than one root") matched = 0 for center, interval in finalintervals.items(): if fp in interval: finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())]) matched += 1 - if matched == 0: - raise ValueError("unable to match braid endpoint with root") - if matched > 1: - raise ValueError("braid endpoint matches more than one root") + if matched != 1: + precision = {f: precision[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision = precision) # new + #if matched == 0: + #raise ValueError("unable to match braid endpoint with root") + #if matched > 1: + #raise ValueError("braid endpoint matches more than one root") initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) @@ -905,7 +995,10 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f, arrangement = [], computebm = True): + + + +def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -914,7 +1007,7 @@ def braid_monodromy(f, arrangement = [], computebm = True): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers - - ``arrangement`` -- an optional list of polynomials whose product equals ``f``, + - ``arrangement`` -- an optional tuple of polynomials whose product equals ``f``, in order to provide information for `braid_monodromy_arrangement`. - ``computebm`` -- an optional boolean variable (default ``True``). It is set to False, only the @@ -932,7 +1025,7 @@ def braid_monodromy(f, arrangement = [], computebm = True): If ``arrangement`` contains more than one element, some information to be used by `braid_monodromy_arrangement` is provided. - If ``computebm`` is set to ``False`` some information to be used by ``strand_components`` is given. + If ``computebm`` is set to ``False`` only some information to be used by ``strand_components`` is given. .. NOTE:: @@ -951,22 +1044,23 @@ def braid_monodromy(f, arrangement = [], computebm = True): s1*s0*s2*s0^-1*s2*s1^-1] """ global roots_interval_cache - if arrangement == []: - arrangement1 = [f] + if arrangement == (): + arrangement1 = (f, ) else: arrangement1 = arrangement x, y = f.parent().gens() F = f.base_ring() - ffac = f.factor() - g = prod([_[0] for _ in ffac]) + glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor()) + g = prod(glist) d = g.degree(y) while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - arrangement1 = [f1.subs({x: x + y}) for f1 in arrangement1] - gfac = g.factor() - glist = [_[0] for _ in gfac] + arrangement1 = (f1.subs({x: x + y}) for f1 in arrangement1) + glist = tuple(f1.subs({x: x + y}) for f1 in glist) disc = discrim(glist) + #with open("debug.txt","a") as fd: + #print('discriminante: ' + str(len(disc)), file = fd) V = corrected_voronoi_diagram(tuple(disc)) G = Graph() for reg in V.regions().values(): @@ -976,55 +1070,79 @@ def braid_monodromy(f, arrangement = [], computebm = True): if reg.rays() or reg.lines(): E = E.union(reg.vertex_graph()) p = next(E.vertex_iterator()) - I = QQbar.gen() - p0 = p[0] + I * p[1] - if len(arrangement1) > 1: - strands = {} - roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] - roots_base.sort() - strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} - if not computebm: - return strands - geombasis = geometric_basis(G, E, p) - segs = set() - for p in geombasis: - for s in zip(p[:-1], p[1:]): - if (s[1], s[0]) not in segs: - segs.add((s[0], s[1])) - segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] - vertices = list(set(flatten(segs))) - tocacheverts = [(g, v) for v in vertices] - populate_roots_interval_cache(tocacheverts) - end_braid_computation = False - while not end_braid_computation: - try: - 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() - end_braid_computation = True - except ChildProcessError: # hack to deal with random fails first time - pass - #B = b.parent() - B=BraidGroup(d) - result = [] - for path in geombasis: - braidpath = B.one() - for i in range(len(path) - 1): - x0 = tuple(path[i].vector()) - x1 = tuple(path[i + 1].vector()) - braidpath = braidpath * segsbraids[(x0, x1)] - result.append(braidpath) + p0 = (p[0], p[1]) + if computebm: + geombasis = geometric_basis(G, E, p) + segs = set() + for p in geombasis: + for s in zip(p[:-1], p[1:]): + if (s[1], s[0]) not in segs: + segs.add((s[0], s[1])) + I = QQbar.gen() + segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] + vertices = list(set(flatten(segs))) + #with open("debug.txt","a") as fd: + #print('vertices-aristas Voronoi: ' + str(len(vertices)) + ', ' + str(len(segs)), file = fd) + #with open("debug.txt","a") as fd: + #print('intervalos vertices empezado', file = fd) + tocacheverts = tuple([(glist, v) for v in vertices]) + populate_roots_interval_cache(tocacheverts) + #with open("debug.txt","a") as fd: + #print('intervalos vertices hecho', file = fd) + end_braid_computation = False + while not end_braid_computation: + try: + braidscomputed = (braid_in_segment([(glist, seg[0], seg[1]) + for seg in segs])) + segsbraids = {} + #with open("debug.txt","a") as fd: + #print('Antes de paralelizar', file = fd) + 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 = b.parent() + B=BraidGroup(d) + result = [] + for path in geombasis: + braidpath = B.one() + for i in range(len(path) - 1): + x0 = tuple(path[i].vector()) + x1 = tuple(path[i + 1].vector()) + braidpath = braidpath * segsbraids[(x0, x1)] + result.append(braidpath) if len(arrangement1) == 1: return result else: - return (result, strands) + F1 = fieldI(f.base_ring()) + I1 = F1(QQbar.gen()) + p1 = p0[0] + I1 * p0[1] + U1 = [_.change_ring(F1) for _ in arrangement1] + x, y = U1[0].parent().gens() + strands = {} + roots_base = [] + for i, h in enumerate(U1): + h0 = h.subs({x: p1}) + h1 = F1[y](h0) + rt = h1.roots(QQbar, multiplicities = False) + roots_base += [(_,i) for _ in rt] + if not holdstrand: + roots_base.sort() + strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} + #with open("debug.txt","a") as fd: + # print('diccionario hecho: ' + str(dic1), file = fd) + if computebm and not holdstrand: + return (result, strands) + elif computebm and holdstrand: + return (result,roots_base) + else: + return strands @parallel def braid2rels(L, d): @@ -1227,7 +1345,7 @@ def relation(x, b): R = [r[1] for r in relations] else: simplified = False - relations = (braid2rels([(b.Tietze(),d) for b in bm])) + relations = (braid2rels([(b.Tietze(), d) for b in bm])) R = [] for r in relations: R += r[1] @@ -1239,7 +1357,7 @@ def relation(x, b): return G -def braid_monodromy_arrangement(flist): +def braid_monodromy_arrangement(flist, nodic = False): r""" Compute the braid monodromy of a projection of the curve defined by a list of polynomials with the extra information about the correspondence of strands @@ -1247,7 +1365,7 @@ def braid_monodromy_arrangement(flist): INPUT: - - ``flist`` -- a list of polynomial with two variables, over a number field + - ``flist`` -- a tuple of polynomial with two variables, over a number field with an embedding in the complex numbers @@ -1282,12 +1400,12 @@ def braid_monodromy_arrangement(flist): d = f.degree() dic ={j + 1 : 1 for j in range(d)} return (braid_monodromy(f), dic) - return braid_monodromy(f,arrangement = flist) + return braid_monodromy(f, arrangement = flist, holdstrand = nodic) def strand_components(flist): r""" Compute the braid monodromy of a projection of the curve - defined by a list of polynomials with the extra information about the correspondence of strands + defined by a tuple of polynomials with the extra information about the correspondence of strands and elements of the list. INPUT: @@ -1318,4 +1436,4 @@ def strand_components(flist): d = f.degree() dic ={j + 1 : 1 for j in range(d)} return dic - return braid_monodromy(f,arrangement = flist, computebm = False) + return braid_monodromy(f, arrangement = flist, computebm = False) From 7cbaf0ff3dcfb8b6fa7bb280d51e258d351f1a6a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 17 Sep 2022 15:04:55 +0200 Subject: [PATCH 029/122] modification of functions --- src/sage/schemes/curves/zariski_vankampen.py | 370 ++++++++++++------- 1 file changed, 244 insertions(+), 126 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index e4aadcb524d..999bccc4c54 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -53,6 +53,7 @@ from sage.misc.flatten import flatten from sage.misc.misc_c import prod from sage.parallel.decorate import parallel +from sage.rings.all import NumberField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.complex_mpfr import ComplexField from sage.rings.qqbar import QQbar @@ -166,7 +167,7 @@ def discrim(flist): INPUT: - - ``flist`` -- a list of polynomials in two variables with coefficients in a + - ``flist`` -- a tuple of polynomials in two variables with coefficients in a number field with a fixed embedding in `\QQbar` OUTPUT: @@ -177,14 +178,14 @@ def discrim(flist): sage: from sage.schemes.curves.zariski_vankampen import discrim sage: R. = QQ[] - sage: flist = [y^3 + x^3 - 1, 2 * x + y] + sage: flist = (y^3 + x^3 - 1, 2 * x + y) sage: discrim(flist) # optional - sirocco - [-0.522757958574711?, - 1, + [1, -0.500000000000000? - 0.866025403784439?*I, -0.500000000000000? + 0.866025403784439?*I, + -0.522757958574711?, 0.2613789792873551? - 0.4527216721561923?*I, - 0.2613789792873551? + 0.4527216721561923?*I] + 0.2613789792873551? + 0.4527216721561923?*I] """ x, y = flist[0].parent().gens() F = flist[0].base_ring() @@ -457,20 +458,66 @@ def newton(f, x0, i0): """ return x0 - f(x0) / f.derivative()(i0) +def fieldI(F0): + r""" + Return the (either double or trivial) extension of a number field which contains `I`. + + INPUT: + + - ``F0`` -- a number field with an embedding in `QQbar`. + + OUTPUT: + + The embedding in `QQbar` of an extension `F` of `F0` containing `I`. + + 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 + + + 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 F0: + return F0 + else: + F0a = F0[I0] + F1a = F0a.absolute_field('b0') + b0 = F1a.gen() + q = b0.minpoly() + qroots = q.roots(QQbar, multiplicities = False) + for b1 in qroots: + F1 = NumberField(q, 'b', embedding = b1) + b = F1.gen() + if (F0.is_subring(F1)): + break + return F1 @parallel -def roots_interval(f, x0): +def roots_interval(flist, x0): """ Find disjoint intervals that isolate the roots of a polynomial for a fixed value of the first variable. INPUT: - - ``f`` -- a bivariate squarefree polynomial + - ``flist`` -- a tuple of bivariate squarefree polynomials - ``x0`` -- a value where the first coordinate will be fixed The intervals are taken as big as possible to be able to detect when two - approximate roots of `f(x_0, y)` correspond to the same exact root. + 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 @@ -479,9 +526,9 @@ def roots_interval(f, x0): EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import roots_interval - sage: R. = QQ[] - sage: f = y^3 - x^2 - sage: ri = roots_interval(f, 1) + sage: R. = QQ[] + sage: flist = (y^3 - x^2, ) + sage: ri = roots_interval(flist, 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, 138907099/160396102*I - 1/2: -1.? + 1.?*I, @@ -500,10 +547,18 @@ def roots_interval(f, x0): -0.933012701892219 + 1.29903810567666*I, -0.0669872981077806 + 0.433012701892219*I)] """ - x, y = f.parent().gens() - I = QQbar.gen() - fx = QQbar[y](f.subs({x: QQ(x0.real()) + I * QQ(x0.imag())})) - roots = fx.roots(QQbar, multiplicities=False) + f = prod(flist) + F0 = f.base_ring() + F1 = fieldI(F0) + I1 = F1(QQbar.gen()) + U1 = [_.change_ring(F1) for _ in flist] + f1 = f.change_ring(F1) + x, y = f1.parent().gens() + Ux = [F1[y](_.subs({x: F1(x0)})) for _ in U1] + fx = prod(Ux) + roots = [] + for _ in Ux: + roots += _.roots(QQbar, multiplicities=False) result = {} for i, r in enumerate(roots): prec = 53 @@ -528,7 +583,7 @@ def roots_interval(f, x0): return result -def roots_interval_cached(f, x0): +def roots_interval_cached(flist, x0): r""" Cached version of :func:`roots_interval`. @@ -537,22 +592,22 @@ def roots_interval_cached(f, x0): sage: from sage.schemes.curves.zariski_vankampen import roots_interval, roots_interval_cached, roots_interval_cache sage: R. = QQ[] sage: f = y^3 - x^2 - sage: (f, 1) in roots_interval_cache + sage: ((f, ), 1) in roots_interval_cache False - sage: ri = roots_interval_cached(f, 1) + sage: ri = roots_interval_cached((f, ), 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, 138907099/160396102*I - 1/2: -1.? + 1.?*I, 1: 1.? + 0.?*I} - sage: (f, 1) in roots_interval_cache + sage: ((f, ), 1) in roots_interval_cache True """ global roots_interval_cache try: - return roots_interval_cache[(f, x0)] + return roots_interval_cache[(flist, x0)] except KeyError: - result = roots_interval(f, x0) - roots_interval_cache[(f, x0)] = result + result = roots_interval(flist, x0) + roots_interval_cache[(flist, x0)] = result return result @@ -563,19 +618,19 @@ def populate_roots_interval_cache(inputs): INPUT: - - ``inputs`` -- a list of tuples (f, x0) + - ``inputs`` -- a list of tuples (flist, x0) EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import populate_roots_interval_cache, roots_interval_cache sage: R. = QQ[] sage: f = y^5 - x^2 - sage: (f, 3) in roots_interval_cache + sage: ((f, ), 3) in roots_interval_cache False - sage: populate_roots_interval_cache([(f, 3)]) - sage: (f, 3) in roots_interval_cache + sage: populate_roots_interval_cache([((f, ), 3)]) + sage: ((f, ), 3) in roots_interval_cache True - sage: roots_interval_cache[(f, 3)] + sage: roots_interval_cache[((f,), 3)] {-1.255469441943070? - 0.9121519421827974?*I: -2.? - 1.?*I, -1.255469441943070? + 0.9121519421827974?*I: -2.? + 1.?*I, 0.4795466549853897? - 1.475892845355996?*I: 1.? - 2.?*I, @@ -585,22 +640,36 @@ 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 = False + while not problem_par: + try: + #with open("debug.txt","a") as fd: + #print("Entra en populate",file=fd) + result = roots_interval(tocompute) + #with open("debug.txt","a") as fd: + #print("Sale en populate",file=fd) + for r in result: + roots_interval_cache[r[0][0]] = r[1] + problem_par = True + #with open("debug.txt","a") as fd: + #print("Fin populate",file=fd) + except TypeError: + #with open("debug.txt","a") as fd: + #print("Falla en populate",file=fd) + pass @parallel -def braid_in_segment(g, x0, x1): +def braid_in_segment(glist, x0, x1, precision = {}): """ 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 complex number, usually a Gauss rational + - ``x1`` -- a complex number, usually a Gauss rational OUTPUT: @@ -611,9 +680,9 @@ def braid_in_segment(g, x0, x1): sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # optional - sirocco sage: R. = 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: x0 = 1 + sage: x1 = 1 + I / 2 + sage: braid_in_segment(tuple(_[0] for _ in f.factor()), x0, x1) # optional - sirocco s1 TESTS: @@ -621,46 +690,61 @@ def braid_in_segment(g, x0, x1): Check that :trac:`26503` is fixed:: sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0] - sage: Kw. = NumberField(wp.minpoly(), embedding=wp) + 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: f = y * (y + z) * x * (x - 1) * (x - y) * (x + z * y - 1) * (x + z * y + wp) sage: from sage.schemes.curves import zariski_vankampen as zvk - sage: g = f.subs({x: x + 2*y}) - sage: p1 = QQbar(sqrt(-1/3)) - sage: p2 = QQbar(1/2+sqrt(-1/3)/2) - sage: B = zvk.braid_in_segment(g.factor(),CC(p1),CC(p2)) # optional - sirocco + sage: g = f.subs({x: x + 2 * y}) + sage: p1 = QQbar(sqrt(- 1 / 3)) + sage: p1 = QQ(p1.real()) + I * QQ(p1.imag()) + sage: glist = tuple([_[0] for _ in g.factor()]) + sage: B = zvk.braid_in_segment(glist, CC(p1), CC(p2)) # optional - sirocco sage: 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()) + g = prod(glist) + F0 = g.base_ring() + F1 = fieldI(F0) + I1 = F1(QQbar.gen()) + U1 = tuple(_.change_ring(F1) for _ in glist) + g1 = g.change_ring(F1) + x, y = g.parent().gens() + X0 = QQ(x0.real()) + I1 * QQ(x0.imag()) + X1 = QQ(x1.real()) + I1 * QQ(x1.imag()) + #X0 = F1(x0) + #X1 = F1(x1) intervals = {} - precision = {} +# precision = {} + if precision == {}: # new + precision ={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 + #precision[f] = 53 while True: CIFp = ComplexIntervalField(precision[f]) intervals[f] = [r.interval(CIFp) for r in y0sf] if not any(a.overlaps(b) for a, b in itertools.combinations(intervals[f], 2)): break precision[f] *= 2 - strands = [followstrand(f[0], [p[0] for p in g if p[0] != f[0]], x0, x1, i.center(), precision[f[0]]) for f in g for i in intervals[f[0]]] + 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(), precision[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(glist, X0) + finalintervals = roots_interval_cached(glist, X1) + I = QQbar.gen() for cs in complexstrands: ip = cs[0][1] + I * cs[0][2] fp = cs[-1][1] + I * cs[-1][2] @@ -669,19 +753,25 @@ def braid_in_segment(g, x0, x1): 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: +# if matched == 0: +# raise ValueError("unable to match braid endpoint with root") + precision = {f: precision[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision = precision) # new +# if matched > 1: +# raise ValueError("braid endpoint mathes more than one root") matched = 0 for center, interval in finalintervals.items(): if fp in interval: finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())]) matched += 1 - if matched == 0: - raise ValueError("unable to match braid endpoint with root") - if matched > 1: - raise ValueError("braid endpoint matches more than one root") + if matched != 1: + precision = {f: precision[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision = precision) # new + #if matched == 0: + #raise ValueError("unable to match braid endpoint with root") + #if matched > 1: + #raise ValueError("braid endpoint matches more than one root") initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) @@ -905,7 +995,10 @@ def geometric_basis(G, E, p): return resul -def braid_monodromy(f, arrangement = [], computebm = True): + + + +def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -914,7 +1007,7 @@ def braid_monodromy(f, arrangement = [], computebm = True): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers - - ``arrangement`` -- an optional list of polynomials whose product equals ``f``, + - ``arrangement`` -- an optional tuple of polynomials whose product equals ``f``, in order to provide information for `braid_monodromy_arrangement`. - ``computebm`` -- an optional boolean variable (default ``True``). It is set to False, only the @@ -932,7 +1025,7 @@ def braid_monodromy(f, arrangement = [], computebm = True): If ``arrangement`` contains more than one element, some information to be used by `braid_monodromy_arrangement` is provided. - If ``computebm`` is set to ``False`` some information to be used by ``strand_components`` is given. + If ``computebm`` is set to ``False`` only some information to be used by ``strand_components`` is given. .. NOTE:: @@ -951,22 +1044,23 @@ def braid_monodromy(f, arrangement = [], computebm = True): s1*s0*s2*s0^-1*s2*s1^-1] """ global roots_interval_cache - if arrangement == []: - arrangement1 = [f] + if arrangement == (): + arrangement1 = (f, ) else: arrangement1 = arrangement x, y = f.parent().gens() F = f.base_ring() - ffac = f.factor() - g = prod([_[0] for _ in ffac]) + glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor()) + g = prod(glist) d = g.degree(y) while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - arrangement1 = [f1.subs({x: x + y}) for f1 in arrangement1] - gfac = g.factor() - glist = [_[0] for _ in gfac] + arrangement1 = (f1.subs({x: x + y}) for f1 in arrangement1) + glist = tuple(f1.subs({x: x + y}) for f1 in glist) disc = discrim(glist) + #with open("debug.txt","a") as fd: + #print('discriminante: ' + str(len(disc)), file = fd) V = corrected_voronoi_diagram(tuple(disc)) G = Graph() for reg in V.regions().values(): @@ -976,55 +1070,79 @@ def braid_monodromy(f, arrangement = [], computebm = True): if reg.rays() or reg.lines(): E = E.union(reg.vertex_graph()) p = next(E.vertex_iterator()) - I = QQbar.gen() - p0 = p[0] + I * p[1] - if len(arrangement1) > 1: - strands = {} - roots_base = [(q,i) for i, h in enumerate(arrangement1) for q in QQbar[y](h.subs({x : p0})).roots(QQbar, multiplicities = False)] - roots_base.sort() - strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} - if not computebm: - return strands - geombasis = geometric_basis(G, E, p) - segs = set() - for p in geombasis: - for s in zip(p[:-1], p[1:]): - if (s[1], s[0]) not in segs: - segs.add((s[0], s[1])) - segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] - vertices = list(set(flatten(segs))) - tocacheverts = [(g, v) for v in vertices] - populate_roots_interval_cache(tocacheverts) - end_braid_computation = False - while not end_braid_computation: - try: - 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() - end_braid_computation = True - except ChildProcessError: # hack to deal with random fails first time - pass - #B = b.parent() - B=BraidGroup(d) - result = [] - for path in geombasis: - braidpath = B.one() - for i in range(len(path) - 1): - x0 = tuple(path[i].vector()) - x1 = tuple(path[i + 1].vector()) - braidpath = braidpath * segsbraids[(x0, x1)] - result.append(braidpath) + p0 = (p[0], p[1]) + if computebm: + geombasis = geometric_basis(G, E, p) + segs = set() + for p in geombasis: + for s in zip(p[:-1], p[1:]): + if (s[1], s[0]) not in segs: + segs.add((s[0], s[1])) + I = QQbar.gen() + segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] + vertices = list(set(flatten(segs))) + #with open("debug.txt","a") as fd: + #print('vertices-aristas Voronoi: ' + str(len(vertices)) + ', ' + str(len(segs)), file = fd) + #with open("debug.txt","a") as fd: + #print('intervalos vertices empezado', file = fd) + tocacheverts = tuple([(glist, v) for v in vertices]) + populate_roots_interval_cache(tocacheverts) + #with open("debug.txt","a") as fd: + #print('intervalos vertices hecho', file = fd) + end_braid_computation = False + while not end_braid_computation: + try: + braidscomputed = (braid_in_segment([(glist, seg[0], seg[1]) + for seg in segs])) + segsbraids = {} + #with open("debug.txt","a") as fd: + #print('Antes de paralelizar', file = fd) + 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 = b.parent() + B=BraidGroup(d) + result = [] + for path in geombasis: + braidpath = B.one() + for i in range(len(path) - 1): + x0 = tuple(path[i].vector()) + x1 = tuple(path[i + 1].vector()) + braidpath = braidpath * segsbraids[(x0, x1)] + result.append(braidpath) if len(arrangement1) == 1: return result else: - return (result, strands) + F1 = fieldI(f.base_ring()) + I1 = F1(QQbar.gen()) + p1 = p0[0] + I1 * p0[1] + U1 = [_.change_ring(F1) for _ in arrangement1] + x, y = U1[0].parent().gens() + strands = {} + roots_base = [] + for i, h in enumerate(U1): + h0 = h.subs({x: p1}) + h1 = F1[y](h0) + rt = h1.roots(QQbar, multiplicities = False) + roots_base += [(_,i) for _ in rt] + if not holdstrand: + roots_base.sort() + strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} + #with open("debug.txt","a") as fd: + # print('diccionario hecho: ' + str(dic1), file = fd) + if computebm and not holdstrand: + return (result, strands) + elif computebm and holdstrand: + return (result,roots_base) + else: + return strands @parallel def braid2rels(L, d): @@ -1227,7 +1345,7 @@ def relation(x, b): R = [r[1] for r in relations] else: simplified = False - relations = (braid2rels([(b.Tietze(),d) for b in bm])) + relations = (braid2rels([(b.Tietze(), d) for b in bm])) R = [] for r in relations: R += r[1] @@ -1239,7 +1357,7 @@ def relation(x, b): return G -def braid_monodromy_arrangement(flist): +def braid_monodromy_arrangement(flist, nodic = False): r""" Compute the braid monodromy of a projection of the curve defined by a list of polynomials with the extra information about the correspondence of strands @@ -1247,7 +1365,7 @@ def braid_monodromy_arrangement(flist): INPUT: - - ``flist`` -- a list of polynomial with two variables, over a number field + - ``flist`` -- a tuple of polynomial with two variables, over a number field with an embedding in the complex numbers @@ -1282,12 +1400,12 @@ def braid_monodromy_arrangement(flist): d = f.degree() dic ={j + 1 : 1 for j in range(d)} return (braid_monodromy(f), dic) - return braid_monodromy(f,arrangement = flist) + return braid_monodromy(f, arrangement = flist, holdstrand = nodic) def strand_components(flist): r""" Compute the braid monodromy of a projection of the curve - defined by a list of polynomials with the extra information about the correspondence of strands + defined by a tuple of polynomials with the extra information about the correspondence of strands and elements of the list. INPUT: @@ -1318,4 +1436,4 @@ def strand_components(flist): d = f.degree() dic ={j + 1 : 1 for j in range(d)} return dic - return braid_monodromy(f,arrangement = flist, computebm = False) + return braid_monodromy(f, arrangement = flist, computebm = False) From 7a2556b4e6f0aa01350ca5e287779d0e9537f49a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 24 Sep 2022 11:38:15 +0200 Subject: [PATCH 030/122] Test passed. Check trac 26503 --- src/sage/schemes/curves/zariski_vankampen.py | 38 ++++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 999bccc4c54..fdbe86fb0cf 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -697,11 +697,11 @@ def braid_in_segment(glist, x0, x1, precision = {}): sage: from sage.schemes.curves import zariski_vankampen as zvk sage: g = f.subs({x: x + 2 * y}) sage: p1 = QQbar(sqrt(- 1 / 3)) - sage: p1 = QQ(p1.real()) + I * QQ(p1.imag()) + sage: p2 = QQbar(1/2+sqrt(-1/3)/2) sage: glist = tuple([_[0] for _ in g.factor()]) - sage: B = zvk.braid_in_segment(glist, CC(p1), CC(p2)) # optional - sirocco + sage: B = zvk.braid_in_segment(glist, p1, p2) # optional - sirocco sage: B # optional - sirocco - s5*s3^-1 + s3*s0^-1*s1^-1*s0^-1*s5*s3^-1 """ g = prod(glist) F0 = g.base_ring() @@ -709,17 +709,17 @@ def braid_in_segment(glist, x0, x1, precision = {}): I1 = F1(QQbar.gen()) U1 = tuple(_.change_ring(F1) for _ in glist) g1 = g.change_ring(F1) - x, y = g.parent().gens() - X0 = QQ(x0.real()) + I1 * QQ(x0.imag()) - X1 = QQ(x1.real()) + I1 * QQ(x1.imag()) - #X0 = F1(x0) - #X1 = F1(x1) + x, y = g1.parent().gens() + #X0 = QQ(x0.real()) + I1 * QQ(x0.imag()) + #X1 = QQ(x1.real()) + I1 * QQ(x1.imag()) + X0 = F1(x0) + X1 = F1(x1) intervals = {} # precision = {} if precision == {}: # new - precision ={f: 53 for f in glist} # new + precision ={f: 53 for f in U1} # new y0s = [] - for f in glist: + for f in U1: if f.variables() == (y,): f0 = F1[y](f) else: @@ -734,16 +734,16 @@ def braid_in_segment(glist, x0, x1, precision = {}): break precision[f] *= 2 strands = [] - for f in glist: + for f in U1: for i in intervals[f]: - aux = followstrand(f, [p for p in glist if p != f], x0, x1, i.center(), precision[f]) + aux = followstrand(f, [p for p in U1 if p != f], x0, x1, i.center(), precision[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(glist, X0) - finalintervals = roots_interval_cached(glist, X1) + initialintervals = roots_interval_cached(U1, X0) + finalintervals = roots_interval_cached(U1, X1) I = QQbar.gen() for cs in complexstrands: ip = cs[0][1] + I * cs[0][2] @@ -756,8 +756,8 @@ def braid_in_segment(glist, x0, x1, precision = {}): if matched != 1: # if matched == 0: # raise ValueError("unable to match braid endpoint with root") - precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision = precision) # new + precision = {f: precision[f] * 2 for f in U1} # new + return braid_in_segment(U1, x0, x1, precision = precision) # new # if matched > 1: # raise ValueError("braid endpoint mathes more than one root") matched = 0 @@ -766,8 +766,8 @@ def braid_in_segment(glist, x0, x1, precision = {}): finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())]) matched += 1 if matched != 1: - precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision = precision) # new + precision = {f: precision[f] * 2 for f in U1} # new + return braid_in_segment(U1, x0, x1, precision = precision) # new #if matched == 0: #raise ValueError("unable to match braid endpoint with root") #if matched > 1: @@ -1056,7 +1056,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - arrangement1 = (f1.subs({x: x + y}) for f1 in arrangement1) + arrangement1 = tuple(f1.subs({x: x + y}) for f1 in arrangement1) glist = tuple(f1.subs({x: x + y}) for f1 in glist) disc = discrim(glist) #with open("debug.txt","a") as fd: From bcb4797ec04e486f58096f2575e854bc152d1378 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 24 Sep 2022 16:17:53 +0200 Subject: [PATCH 031/122] tests --- src/sage/schemes/curves/zariski_vankampen.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index fdbe86fb0cf..a90ee6a1f52 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -23,9 +23,11 @@ EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy # optional - sirocco sage: R. = QQ[] sage: f = y^3 + x^3 - 1 + sage: braid_monodromy(f) # optional - sirocco + [s1*s0, s1*s0, s1*s0] sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ From bc05632d47e135165b720066e9a35f891623d280 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 24 Sep 2022 17:15:49 +0200 Subject: [PATCH 032/122] tests OK --- src/sage/schemes/curves/zariski_vankampen.py | 42 ++++++++++---------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 999bccc4c54..a90ee6a1f52 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -23,9 +23,11 @@ EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy # optional - sirocco sage: R. = QQ[] sage: f = y^3 + x^3 - 1 + sage: braid_monodromy(f) # optional - sirocco + [s1*s0, s1*s0, s1*s0] sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ @@ -697,11 +699,11 @@ def braid_in_segment(glist, x0, x1, precision = {}): sage: from sage.schemes.curves import zariski_vankampen as zvk sage: g = f.subs({x: x + 2 * y}) sage: p1 = QQbar(sqrt(- 1 / 3)) - sage: p1 = QQ(p1.real()) + I * QQ(p1.imag()) + sage: p2 = QQbar(1/2+sqrt(-1/3)/2) sage: glist = tuple([_[0] for _ in g.factor()]) - sage: B = zvk.braid_in_segment(glist, CC(p1), CC(p2)) # optional - sirocco + sage: B = zvk.braid_in_segment(glist, p1, p2) # optional - sirocco sage: B # optional - sirocco - s5*s3^-1 + s3*s0^-1*s1^-1*s0^-1*s5*s3^-1 """ g = prod(glist) F0 = g.base_ring() @@ -709,17 +711,17 @@ def braid_in_segment(glist, x0, x1, precision = {}): I1 = F1(QQbar.gen()) U1 = tuple(_.change_ring(F1) for _ in glist) g1 = g.change_ring(F1) - x, y = g.parent().gens() - X0 = QQ(x0.real()) + I1 * QQ(x0.imag()) - X1 = QQ(x1.real()) + I1 * QQ(x1.imag()) - #X0 = F1(x0) - #X1 = F1(x1) + x, y = g1.parent().gens() + #X0 = QQ(x0.real()) + I1 * QQ(x0.imag()) + #X1 = QQ(x1.real()) + I1 * QQ(x1.imag()) + X0 = F1(x0) + X1 = F1(x1) intervals = {} # precision = {} if precision == {}: # new - precision ={f: 53 for f in glist} # new + precision ={f: 53 for f in U1} # new y0s = [] - for f in glist: + for f in U1: if f.variables() == (y,): f0 = F1[y](f) else: @@ -734,16 +736,16 @@ def braid_in_segment(glist, x0, x1, precision = {}): break precision[f] *= 2 strands = [] - for f in glist: + for f in U1: for i in intervals[f]: - aux = followstrand(f, [p for p in glist if p != f], x0, x1, i.center(), precision[f]) + aux = followstrand(f, [p for p in U1 if p != f], x0, x1, i.center(), precision[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(glist, X0) - finalintervals = roots_interval_cached(glist, X1) + initialintervals = roots_interval_cached(U1, X0) + finalintervals = roots_interval_cached(U1, X1) I = QQbar.gen() for cs in complexstrands: ip = cs[0][1] + I * cs[0][2] @@ -756,8 +758,8 @@ def braid_in_segment(glist, x0, x1, precision = {}): if matched != 1: # if matched == 0: # raise ValueError("unable to match braid endpoint with root") - precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision = precision) # new + precision = {f: precision[f] * 2 for f in U1} # new + return braid_in_segment(U1, x0, x1, precision = precision) # new # if matched > 1: # raise ValueError("braid endpoint mathes more than one root") matched = 0 @@ -766,8 +768,8 @@ def braid_in_segment(glist, x0, x1, precision = {}): finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())]) matched += 1 if matched != 1: - precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision = precision) # new + precision = {f: precision[f] * 2 for f in U1} # new + return braid_in_segment(U1, x0, x1, precision = precision) # new #if matched == 0: #raise ValueError("unable to match braid endpoint with root") #if matched > 1: @@ -1056,7 +1058,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) - arrangement1 = (f1.subs({x: x + y}) for f1 in arrangement1) + arrangement1 = tuple(f1.subs({x: x + y}) for f1 in arrangement1) glist = tuple(f1.subs({x: x + y}) for f1 in glist) disc = discrim(glist) #with open("debug.txt","a") as fd: From 770384ee17ae018a865d17b30dc80e07cc3f9f1b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 1 Oct 2022 13:01:22 +0200 Subject: [PATCH 033/122] Geometric basis and oriented circuits --- src/sage/schemes/curves/zariski_vankampen.py | 102 ++++++++++++++----- 1 file changed, 75 insertions(+), 27 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index a90ee6a1f52..52985a0f90d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -780,7 +780,7 @@ def braid_in_segment(glist, x0, x1, precision = {}): return initialbraid * centralbraid * finalbraid -def orient_circuit(circuit): +def orient_circuit(circuit, convex = True): r""" Reverse a circuit if it goes clockwise; otherwise leave it unchanged. @@ -831,8 +831,14 @@ def orient_circuit(circuit): (A vertex at (-2, 2), A vertex at (-2, -2), None)] """ - prec = 53 vectors = [v[1].vector() - v[0].vector() for v in circuit] + if convex: + pr = vectors[0]*vectors[1] + if pr>0: + return circuit + elif pr<0: + return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) + prec = 53 while True: CIF = ComplexIntervalField(prec) totalangle = sum((CIF(*vectors[i]) / CIF(*vectors[i - 1])).argument() @@ -844,18 +850,21 @@ def orient_circuit(circuit): prec *= 2 -def geometric_basis(G, E, p): +def geometric_basis(G, E, p, regions): r""" Return a geometric basis, based on a vertex. INPUT: - + - ``G`` -- the graph of the bounded regions of a Voronoi Diagram - ``E`` -- the subgraph of ``G`` formed by the edges that touch an unbounded region - ``p`` -- a vertex of ``E`` + + - ``regions`` -- the bounded regions as a list of positively oriented + lists of vertices of ``G`` 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 @@ -868,14 +877,18 @@ def geometric_basis(G, E, p): 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: regions = [] + sage: for reg in regions: + ....: regv = reg.vertex_graph() + ....: G = G.union(regv) + ....: circ = orient_circuit(regv.eulerian_circuit()) + ....: regions.append(circ[0]) 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: geometric_basis(G, E, p, regions). [[A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), @@ -912,7 +925,11 @@ 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())] + region_graphs = [] + for reg in regions: + g = Graph(reg) + region_graphs.append(g) + EC = [v[0] for v in orient_circuit(E.eulerian_circuit(), convex = True)] i = EC.index(p) EC = EC[i:] + EC[:i + 1] # A counterclockwise eulerian circuit on the boundary, based at p if G.size() == E.size(): @@ -942,26 +959,52 @@ def geometric_basis(G, E, p): if v in I.connected_component_containing_vertex(q) and not v == q] r = max(distancequotients)[1] cutpath = I.shortest_path(q, r) - Gcut = copy(G) + #Gcut = copy(G) Ecut = copy(E) Ecut.delete_vertices([q, r]) - Gcut.delete_vertices(cutpath) + #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) + # an error instead of giving a wrong answer. It happens. Construct the dual graph. + Gd=Graph(len(regions)) + for i,j in Combinations(range(len(regions)),2): + Ga=region_graphs[i] + La=Ga.edges(sort=True) + Gb=region_graphs[j] + Lb=Gb.edges(sort=True) + Uc=[(a,b,c) for a,b,c in La if (a,b,c) in Lb or (b,a,c) in Lb] + if len(Uc)>=1: + arista=Uc[0] + if arista[0] in cutpath and arista[1] in cutpath: + k=cutpath.index(arista[0]) + l=cutpath.index(arista[1]) + if abs(k-l)!=1 and Set(k,l)!=Set(0,len(regions)-1): + print("arista fuera del corte") + Gd.add_edge((i,j)) + else: + Gd.add_edge((i,j)) + #if Gcut.connected_components_number() != 2: + #raise ValueError("unable to compute a correct path") + Gd1,Gd2 = Gd.connected_components() + GL1=list(Set(_ for c in Gd1 for _ in regions[c])) + GL1=flatten(GL1) + G1=G.subgraph(GL1) + regions1 = [regions[i] for i in Gd1] + GL2=list(Set(_ for c in Gd2 for _ in regions[c])) + GL2=flatten(GL2) + G2=G.subgraph(GL2) + regions2 = [regions[i] for i in Gd2] +# 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 + regions1, regions2 = regions2, regions1 E1, E2 = Ecut.connected_components_subgraphs() if EC[EC.index(q) + 1] in E2: @@ -978,8 +1021,8 @@ def geometric_basis(G, E, p): if n in E2: E2.add_edge(v, n, None) - gb1 = geometric_basis(G1, E1, q) - gb2 = geometric_basis(G2, E2, q) + gb1 = geometric_basis(G1, E1, q, regions1) + gb2 = geometric_basis(G2, E2, q, regions2) reverse_connecting = list(reversed(connecting_path)) resul = [connecting_path + path + reverse_connecting @@ -1064,9 +1107,14 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): #with open("debug.txt","a") as fd: #print('discriminante: ' + str(len(disc)), file = fd) V = corrected_voronoi_diagram(tuple(disc)) + Vreg=[_ for _ in V.regions().values() if _.is_compact()] G = Graph() - for reg in V.regions().values(): - G = G.union(reg.vertex_graph()) + regions = [] + for reg in Vreg: + regv = reg.vertex_graph() + G = G.union(regv) + circ = orient_circuit(regv.eulerian_circuit()) + regions.append(circ) E = Graph() for reg in V.regions().values(): if reg.rays() or reg.lines(): @@ -1074,7 +1122,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): p = next(E.vertex_iterator()) p0 = (p[0], p[1]) if computebm: - geombasis = geometric_basis(G, E, p) + geombasis = geometric_basis(G, E, p, regions) segs = set() for p in geombasis: for s in zip(p[:-1], p[1:]): From 3cbb8285a7c0d1a8ae114cc63b0712e651a7d60e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 2 Oct 2022 23:44:12 +0200 Subject: [PATCH 034/122] corrected functions --- src/sage/schemes/curves/zariski_vankampen.py | 81 ++++++++++---------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 52985a0f90d..00fd581b43d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -61,7 +61,7 @@ from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField - +from sage.matrix.all import matrix roots_interval_cache = {} @@ -699,11 +699,15 @@ def braid_in_segment(glist, x0, x1, precision = {}): sage: from sage.schemes.curves import zariski_vankampen as zvk sage: g = f.subs({x: x + 2 * y}) sage: p1 = QQbar(sqrt(- 1 / 3)) + sage: 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 = zvk.braid_in_segment(glist, p1, p2) # optional - sirocco + sage: B = zvk.braid_in_segment(glist, p1b, p2b) # optional - sirocco sage: B # optional - sirocco - s3*s0^-1*s1^-1*s0^-1*s5*s3^-1 + s5*s3^-1 """ g = prod(glist) F0 = g.base_ring() @@ -780,7 +784,7 @@ def braid_in_segment(glist, x0, x1, precision = {}): return initialbraid * centralbraid * finalbraid -def orient_circuit(circuit, convex = True): +def orient_circuit(circuit, convex = False): r""" Reverse a circuit if it goes clockwise; otherwise leave it unchanged. @@ -833,10 +837,10 @@ def orient_circuit(circuit, convex = True): """ vectors = [v[1].vector() - v[0].vector() for v in circuit] if convex: - pr = vectors[0]*vectors[1] - if pr>0: + pr = matrix([vectors[0], vectors[1]]).determinant() + if pr > 0: return circuit - elif pr<0: + elif pr < 0: return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) prec = 53 while True: @@ -873,22 +877,23 @@ def geometric_basis(G, E, p, regions): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import geometric_basis + sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, orient_circuit 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: Vreg = [_ for _ in V.regions().values() if _.is_compact()] sage: regions = [] - sage: for reg in regions: + sage: for reg in Vreg: ....: regv = reg.vertex_graph() ....: G = G.union(regv) - ....: circ = orient_circuit(regv.eulerian_circuit()) - ....: regions.append(circ[0]) + ....: circ = orient_circuit(regv.eulerian_circuit(), convex = True) + ....: regions.append(circ) 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, regions). + sage: geometric_basis(G, E, p, regions) [[A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), @@ -929,7 +934,7 @@ def geometric_basis(G, E, p, regions): for reg in regions: g = Graph(reg) region_graphs.append(g) - EC = [v[0] for v in orient_circuit(E.eulerian_circuit(), convex = True)] + 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 if G.size() == E.size(): @@ -944,9 +949,7 @@ def geometric_basis(G, E, p, regions): for v in E: if len(E.neighbors(v)) > 2: I.add_vertex(v) - 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] @@ -965,33 +968,32 @@ def geometric_basis(G, E, p, regions): #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. It happens. Construct the dual graph. - Gd=Graph(len(regions)) - for i,j in Combinations(range(len(regions)),2): - Ga=region_graphs[i] - La=Ga.edges(sort=True) - Gb=region_graphs[j] - Lb=Gb.edges(sort=True) - Uc=[(a,b,c) for a,b,c in La if (a,b,c) in Lb or (b,a,c) in Lb] - if len(Uc)>=1: - arista=Uc[0] + Gd = Graph(len(regions)) + for i, j in Combinations(range(len(regions)), 2): + Ga = region_graphs[i] + La = Ga.edges(sort = True) + Gb = region_graphs[j] + Lb = Gb.edges(sort = True) + Uc = [(a, b, c) for a, b, c in La if (a, b, c) in Lb or (b, a, c) in Lb] + if len(Uc) >= 1: + arista = Uc[0] if arista[0] in cutpath and arista[1] in cutpath: - k=cutpath.index(arista[0]) - l=cutpath.index(arista[1]) - if abs(k-l)!=1 and Set(k,l)!=Set(0,len(regions)-1): - print("arista fuera del corte") + k = cutpath.index(arista[0]) + l = cutpath.index(arista[1]) + if abs(k - l) != 1 and Set(k, l)!=Set(0, len(regions) - 1): Gd.add_edge((i,j)) else: - Gd.add_edge((i,j)) + Gd.add_edge((i, j)) #if Gcut.connected_components_number() != 2: #raise ValueError("unable to compute a correct path") Gd1,Gd2 = Gd.connected_components() - GL1=list(Set(_ for c in Gd1 for _ in regions[c])) - GL1=flatten(GL1) - G1=G.subgraph(GL1) + GL1 = list(Set(_ for c in Gd1 for _ in regions[c])) + GL1 = flatten(GL1) + G1 =G.subgraph(GL1) regions1 = [regions[i] for i in Gd1] - GL2=list(Set(_ for c in Gd2 for _ in regions[c])) - GL2=flatten(GL2) - G2=G.subgraph(GL2) + GL2 = list(Set(_ for c in Gd2 for _ in regions[c])) + GL2 = flatten(GL2) + G2 = G.subgraph(GL2) regions2 = [regions[i] for i in Gd2] # G1, G2 = Gcut.connected_components_subgraphs() # for v in cutpath: @@ -1113,7 +1115,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): for reg in Vreg: regv = reg.vertex_graph() G = G.union(regv) - circ = orient_circuit(regv.eulerian_circuit()) + circ = orient_circuit(regv.eulerian_circuit(), convex = True) regions.append(circ) E = Graph() for reg in V.regions().values(): @@ -1439,11 +1441,10 @@ def braid_monodromy_arrangement(flist, nodic = False): sage: flist = [x^2 - y^3, x + 3 * y - 5] sage: braid_monodromy_arrangement(flist) # 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], + 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], {1: 1, 2: 2, 3: 1, 4: 1}) - """ f = prod(flist) if len(flist) == 1: From 0016effe857a01bca0cdcc43320d07b02182f0fd Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 2 Oct 2022 23:46:52 +0200 Subject: [PATCH 035/122] corrected functions --- src/sage/schemes/curves/zariski_vankampen.py | 121 +++++++++++++------ 1 file changed, 85 insertions(+), 36 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index a90ee6a1f52..00fd581b43d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -61,7 +61,7 @@ from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField - +from sage.matrix.all import matrix roots_interval_cache = {} @@ -699,11 +699,15 @@ def braid_in_segment(glist, x0, x1, precision = {}): sage: from sage.schemes.curves import zariski_vankampen as zvk sage: g = f.subs({x: x + 2 * y}) sage: p1 = QQbar(sqrt(- 1 / 3)) + sage: 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 = zvk.braid_in_segment(glist, p1, p2) # optional - sirocco + sage: B = zvk.braid_in_segment(glist, p1b, p2b) # optional - sirocco sage: B # optional - sirocco - s3*s0^-1*s1^-1*s0^-1*s5*s3^-1 + s5*s3^-1 """ g = prod(glist) F0 = g.base_ring() @@ -780,7 +784,7 @@ def braid_in_segment(glist, x0, x1, precision = {}): return initialbraid * centralbraid * finalbraid -def orient_circuit(circuit): +def orient_circuit(circuit, convex = False): r""" Reverse a circuit if it goes clockwise; otherwise leave it unchanged. @@ -831,8 +835,14 @@ def orient_circuit(circuit): (A vertex at (-2, 2), A vertex at (-2, -2), None)] """ - prec = 53 vectors = [v[1].vector() - v[0].vector() for v in circuit] + if convex: + pr = matrix([vectors[0], vectors[1]]).determinant() + if pr > 0: + return circuit + elif pr < 0: + return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) + prec = 53 while True: CIF = ComplexIntervalField(prec) totalangle = sum((CIF(*vectors[i]) / CIF(*vectors[i - 1])).argument() @@ -844,18 +854,21 @@ def orient_circuit(circuit): prec *= 2 -def geometric_basis(G, E, p): +def geometric_basis(G, E, p, regions): r""" Return a geometric basis, based on a vertex. INPUT: - + - ``G`` -- the graph of the bounded regions of a Voronoi Diagram - ``E`` -- the subgraph of ``G`` formed by the edges that touch an unbounded region - ``p`` -- a vertex of ``E`` + + - ``regions`` -- the bounded regions as a list of positively oriented + lists of vertices of ``G`` 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 @@ -864,18 +877,23 @@ def geometric_basis(G, E, p): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import geometric_basis + sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, orient_circuit 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: Vreg = [_ for _ in V.regions().values() if _.is_compact()] + sage: regions = [] + sage: for reg in Vreg: + ....: regv = reg.vertex_graph() + ....: G = G.union(regv) + ....: circ = orient_circuit(regv.eulerian_circuit(), convex = True) + ....: regions.append(circ) 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: geometric_basis(G, E, p, regions) [[A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), @@ -912,6 +930,10 @@ def geometric_basis(G, E, p): A vertex at (-2, 2), A vertex at (-2, -2)]] """ + region_graphs = [] + for reg in regions: + g = Graph(reg) + region_graphs.append(g) 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 @@ -927,9 +949,7 @@ def geometric_basis(G, E, p): for v in E: if len(E.neighbors(v)) > 2: I.add_vertex(v) - 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] @@ -942,26 +962,51 @@ def geometric_basis(G, E, p): if v in I.connected_component_containing_vertex(q) and not v == q] r = max(distancequotients)[1] cutpath = I.shortest_path(q, r) - Gcut = copy(G) + #Gcut = copy(G) Ecut = copy(E) Ecut.delete_vertices([q, r]) - Gcut.delete_vertices(cutpath) + #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) + # an error instead of giving a wrong answer. It happens. Construct the dual graph. + Gd = Graph(len(regions)) + for i, j in Combinations(range(len(regions)), 2): + Ga = region_graphs[i] + La = Ga.edges(sort = True) + Gb = region_graphs[j] + Lb = Gb.edges(sort = True) + Uc = [(a, b, c) for a, b, c in La if (a, b, c) in Lb or (b, a, c) in Lb] + if len(Uc) >= 1: + arista = Uc[0] + if arista[0] in cutpath and arista[1] in cutpath: + k = cutpath.index(arista[0]) + l = cutpath.index(arista[1]) + if abs(k - l) != 1 and Set(k, l)!=Set(0, len(regions) - 1): + Gd.add_edge((i,j)) + else: + Gd.add_edge((i, j)) + #if Gcut.connected_components_number() != 2: + #raise ValueError("unable to compute a correct path") + Gd1,Gd2 = Gd.connected_components() + GL1 = list(Set(_ for c in Gd1 for _ in regions[c])) + GL1 = flatten(GL1) + G1 =G.subgraph(GL1) + regions1 = [regions[i] for i in Gd1] + GL2 = list(Set(_ for c in Gd2 for _ in regions[c])) + GL2 = flatten(GL2) + G2 = G.subgraph(GL2) + regions2 = [regions[i] for i in Gd2] +# 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 + regions1, regions2 = regions2, regions1 E1, E2 = Ecut.connected_components_subgraphs() if EC[EC.index(q) + 1] in E2: @@ -978,8 +1023,8 @@ def geometric_basis(G, E, p): if n in E2: E2.add_edge(v, n, None) - gb1 = geometric_basis(G1, E1, q) - gb2 = geometric_basis(G2, E2, q) + gb1 = geometric_basis(G1, E1, q, regions1) + gb2 = geometric_basis(G2, E2, q, regions2) reverse_connecting = list(reversed(connecting_path)) resul = [connecting_path + path + reverse_connecting @@ -1064,9 +1109,14 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): #with open("debug.txt","a") as fd: #print('discriminante: ' + str(len(disc)), file = fd) V = corrected_voronoi_diagram(tuple(disc)) + Vreg=[_ for _ in V.regions().values() if _.is_compact()] G = Graph() - for reg in V.regions().values(): - G = G.union(reg.vertex_graph()) + regions = [] + for reg in Vreg: + regv = reg.vertex_graph() + G = G.union(regv) + circ = orient_circuit(regv.eulerian_circuit(), convex = True) + regions.append(circ) E = Graph() for reg in V.regions().values(): if reg.rays() or reg.lines(): @@ -1074,7 +1124,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): p = next(E.vertex_iterator()) p0 = (p[0], p[1]) if computebm: - geombasis = geometric_basis(G, E, p) + geombasis = geometric_basis(G, E, p, regions) segs = set() for p in geombasis: for s in zip(p[:-1], p[1:]): @@ -1391,11 +1441,10 @@ def braid_monodromy_arrangement(flist, nodic = False): sage: flist = [x^2 - y^3, x + 3 * y - 5] sage: braid_monodromy_arrangement(flist) # 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], + 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], {1: 1, 2: 2, 3: 1, 4: 1}) - """ f = prod(flist) if len(flist) == 1: From 092f4f321f68289b0d282d8ff481141a7667974d Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 3 Oct 2022 07:58:47 +0200 Subject: [PATCH 036/122] add optional to a test --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 00fd581b43d..a3bd956ed9f 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1479,7 +1479,7 @@ def strand_components(flist): sage: from sage.schemes.curves.zariski_vankampen import strand_components # optional - sirocco sage: R. = QQ[] sage: flist = [x^2 - y^3, x + 3 * y - 5] - sage: strand_components(flist) + sage: strand_components(flist) # optional - sirocco {1: 1, 2: 2, 3: 1, 4: 1} """ f = prod(flist) From 6ab277c1d745c088a5e62b2b334565d7f902cbca Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 3 Oct 2022 20:12:21 +0200 Subject: [PATCH 037/122] for lint --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index a3bd956ed9f..9a0f5feb36b 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -868,7 +868,7 @@ def geometric_basis(G, E, p, regions): - ``p`` -- a vertex of ``E`` - ``regions`` -- the bounded regions as a list of positively oriented - lists of vertices of ``G`` + lists of vertices of ``G`` 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 From cafff6255ebbeaa2ddfb076720df45bbbcd53f1d Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 6 Oct 2022 19:30:08 +0200 Subject: [PATCH 038/122] step 2 for geometric_basis --- src/sage/schemes/curves/zariski_vankampen.py | 179 ++++++++++--------- 1 file changed, 97 insertions(+), 82 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9a0f5feb36b..58c0ce07fd5 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -41,7 +41,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** import itertools -from copy import copy, deepcopy +from copy import copy from sage.combinat.combination import Combinations from sage.sets.set import Set @@ -62,6 +62,7 @@ from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField from sage.matrix.all import matrix +from sage.rings.all import PolynomialRing roots_interval_cache = {} @@ -160,16 +161,16 @@ def sgn(x, y): -def discrim(flist): +def discrim(pols): r""" - Return the points in the discriminant of the product of the polynomials of a list ``flist``. + 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: - - ``flist`` -- a tuple of polynomials in two variables with coefficients in a + - ``pols`` -- a list or tuple of polynomials in two variables with coefficients in a number field with a fixed embedding in `\QQbar` OUTPUT: @@ -189,31 +190,25 @@ def discrim(flist): 0.2613789792873551? - 0.4527216721561923?*I, 0.2613789792873551? + 0.4527216721561923?*I] """ + flist = tuple(pols) x, y = flist[0].parent().gens() F = flist[0].base_ring() + R = PolynomialRing(F,(x,)) @parallel - def discrim_pairs(ftuple): - if len(ftuple) == 1: - f = ftuple[0] - return F[x](f.discriminant(y)) - elif len(ftuple) == 2: - f, g = ftuple - return F[x](f.resultant(g, y)) - - pairs = [((f,),) for f in flist] + [((f, g),) for f, g in Combinations(flist, 2)] + def discrim_pairs(f, g): + if f == g: + return R(f.discriminant(y)) + else: + return R(f.resultant(g, y)) + + pairs = [(f, f) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) rts = [] poly = 1 - #for f in flist: - #aux = F[x](f.discriminant(y)) - #poly = aux * poly - #for f, g in Combinations(flist, 2): - #aux = F[x](f.resultant(g, y)) - #poly = aux * poly for u in fdiscrim: h0 = u[1].radical() - h1 = F[x](h0 / h0.gcd(poly)) + h1 = h0 // h0.gcd(poly) rts += h1.roots(QQbar, multiplicities = False) poly = poly * h1 return rts @@ -470,7 +465,7 @@ def fieldI(F0): OUTPUT: - The embedding in `QQbar` of an extension `F` of `F0` containing `I`. + The extension `F` of `F0` containing `I` with an embedding in `QQbar`. EXAMPLES:: @@ -503,19 +498,19 @@ def fieldI(F0): F1 = NumberField(q, 'b', embedding = b1) b = F1.gen() if (F0.is_subring(F1)): - break - return F1 + return F1 + @parallel -def roots_interval(flist, x0): +def roots_interval(pols, x0): """ Find disjoint intervals that isolate the roots of a polynomial for a fixed value of the first variable. INPUT: - - ``flist`` -- a tuple of bivariate squarefree polynomials - - ``x0`` -- a value where the first coordinate will be fixed + - ``pols`` -- a list or a tuple of bivariate squarefree polynomials + - ``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, where @@ -549,6 +544,7 @@ def roots_interval(flist, x0): -0.933012701892219 + 1.29903810567666*I, -0.0669872981077806 + 0.433012701892219*I)] """ + flist = tuple(pols) f = prod(flist) F0 = f.base_ring() F1 = fieldI(F0) @@ -557,10 +553,10 @@ def roots_interval(flist, x0): f1 = f.change_ring(F1) x, y = f1.parent().gens() Ux = [F1[y](_.subs({x: F1(x0)})) for _ in U1] - fx = prod(Ux) + fx = F1[y](f1.subs({x: F1(x0)})) roots = [] - for _ in Ux: - roots += _.roots(QQbar, multiplicities=False) + for pol in Ux: + roots += pol.roots(QQbar, multiplicities=False) result = {} for i, r in enumerate(roots): prec = 53 @@ -642,22 +638,14 @@ def populate_roots_interval_cache(inputs): """ global roots_interval_cache tocompute = [inp for inp in inputs if inp not in roots_interval_cache] - problem_par = False - while not problem_par: + problem_par = True + while problem_par: # hack to deal with random fails in parallelization try: - #with open("debug.txt","a") as fd: - #print("Entra en populate",file=fd) result = roots_interval(tocompute) - #with open("debug.txt","a") as fd: - #print("Sale en populate",file=fd) for r in result: roots_interval_cache[r[0][0]] = r[1] - problem_par = True - #with open("debug.txt","a") as fd: - #print("Fin populate",file=fd) + problem_par = False except TypeError: - #with open("debug.txt","a") as fd: - #print("Falla en populate",file=fd) pass @@ -670,8 +658,9 @@ def braid_in_segment(glist, x0, x1, precision = {}): INPUT: - ``glist`` -- a tuple of polynomials in two variables - - ``x0`` -- a complex number, usually a Gauss rational - - ``x1`` -- a complex number, usually a Gauss rational + - ``x0`` -- a Gauss rational + - ``x1`` -- a Gauss rational + - ``precision`` -- a dictionary (default, an empty one) which will apply to each element of ``glist`` a *precision* OUTPUT: @@ -716,12 +705,9 @@ def braid_in_segment(glist, x0, x1, precision = {}): U1 = tuple(_.change_ring(F1) for _ in glist) g1 = g.change_ring(F1) x, y = g1.parent().gens() - #X0 = QQ(x0.real()) + I1 * QQ(x0.imag()) - #X1 = QQ(x1.real()) + I1 * QQ(x1.imag()) X0 = F1(x0) X1 = F1(x1) intervals = {} -# precision = {} if precision == {}: # new precision ={f: 53 for f in U1} # new y0s = [] @@ -732,7 +718,6 @@ def braid_in_segment(glist, x0, x1, precision = {}): 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]) intervals[f] = [r.interval(CIFp) for r in y0sf] @@ -760,12 +745,9 @@ def braid_in_segment(glist, x0, x1, precision = {}): initialstrands.append([(0, center.real(), center.imag()), (1, cs[0][1], cs[0][2])]) matched += 1 if matched != 1: -# if matched == 0: -# raise ValueError("unable to match braid endpoint with root") precision = {f: precision[f] * 2 for f in U1} # new return braid_in_segment(U1, x0, x1, precision = precision) # new -# if matched > 1: -# raise ValueError("braid endpoint mathes more than one root") + matched = 0 for center, interval in finalintervals.items(): if fp in interval: @@ -774,10 +756,6 @@ def braid_in_segment(glist, x0, x1, precision = {}): if matched != 1: precision = {f: precision[f] * 2 for f in U1} # new return braid_in_segment(U1, x0, x1, precision = precision) # new - #if matched == 0: - #raise ValueError("unable to match braid endpoint with root") - #if matched > 1: - #raise ValueError("braid endpoint matches more than one root") initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) @@ -792,6 +770,8 @@ def orient_circuit(circuit, convex = False): - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given by a list of edges + + - ``convex`` -- a boolean function, if set to ``True`` a simpler computation is made OUTPUT: @@ -853,22 +833,28 @@ def orient_circuit(circuit, convex = False): return circuit prec *= 2 - +#def geometric_basis(G, E, EC, p, dual_graph): def geometric_basis(G, E, p, regions): r""" Return a geometric basis, based on a vertex. INPUT: - - ``G`` -- the graph of the bounded regions of a Voronoi Diagram + - ``G`` -- a graph which correspond to the bounded edgess of a Voronoi Diagram - - ``E`` -- the subgraph of ``G`` formed by the edges that touch - an unbounded region + - ``E`` -- a subgraph of ``G`` which is a cycle; it corresponds to the bounded edges touching + an unbounded region of a Voronoi Diagram + + - ``EC`` -- A counterclockwise orientation of the vertices of ``E`` - ``p`` -- a vertex of ``E`` - ``regions`` -- the bounded regions as a list of positively oriented - lists of vertices of ``G`` + lists of edges of ``G`` + + - ``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. + It is given with counterclockwise orientation. 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 @@ -930,44 +916,48 @@ def geometric_basis(G, E, p, regions): A vertex at (-2, 2), A vertex at (-2, -2)]] """ + ######## This will disappear after the changes region_graphs = [] for reg in regions: g = Graph(reg) region_graphs.append(g) 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 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 + InternalEdges = [_ for _ in G.edges(sort = False) 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) + # Internal = Graph() # Replaced by the above lines + # for e in G.edges(sort=False): + # if not E.has_edge(e): + # Internal.add_edge(e) # interior graph # treat the case where I is empty - if not I: + if not Internal: for v in E: if len(E.neighbors(v)) > 2: - I.add_vertex(v) + Internal.add_vertex(v) for i, ECi in enumerate(EC): # q and r are the points we will cut through - if EC[i] in I: + if EC[i] in Internal: q = EC[i] connecting_path = EC[:i] break - if EC[-i] in I: + if EC[-i] in Internal: q = EC[-i] connecting_path = list(reversed(EC[-i:])) break - distancequotients = [(E.distance(q, v)**2 / I.distance(q, v), v) for v in E - if v in I.connected_component_containing_vertex(q) and not v == q] + distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in E + if v in Internal.connected_component_containing_vertex(q) and not v == q] r = max(distancequotients)[1] - cutpath = I.shortest_path(q, r) + cutpath = Internal.shortest_path(q, r) #Gcut = copy(G) 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. It happens. Construct the dual graph. + ############## To be replaced Gd = Graph(len(regions)) for i, j in Combinations(range(len(regions)), 2): Ga = region_graphs[i] @@ -984,12 +974,17 @@ def geometric_basis(G, E, p, regions): Gd.add_edge((i,j)) else: Gd.add_edge((i, j)) - #if Gcut.connected_components_number() != 2: - #raise ValueError("unable to compute a correct path") + ####################### + # Gd = copy(dual_graph) + # cp0 = cutpath + [cutpath[0]] + # cp1 = [(e, cp0[i + 1], None) for i, e in enumerate(cutpath)] + # cp1 += [(cp0[i + 1], e, None) for i, e in enumerate(cutpath)] + # borra = [_ for _ in DG.edges(sort = False) if _[2] in cp1] + # Gd.delete_edges(borra) Gd1,Gd2 = Gd.connected_components() GL1 = list(Set(_ for c in Gd1 for _ in regions[c])) GL1 = flatten(GL1) - G1 =G.subgraph(GL1) + G1 = G.subgraph(GL1) regions1 = [regions[i] for i in Gd1] GL2 = list(Set(_ for c in Gd2 for _ in regions[c])) GL2 = flatten(GL2) @@ -1106,13 +1101,12 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): arrangement1 = tuple(f1.subs({x: x + y}) for f1 in arrangement1) glist = tuple(f1.subs({x: x + y}) for f1 in glist) disc = discrim(glist) - #with open("debug.txt","a") as fd: - #print('discriminante: ' + str(len(disc)), file = fd) V = corrected_voronoi_diagram(tuple(disc)) - Vreg=[_ for _ in V.regions().values() if _.is_compact()] + V0 = [_ for _ in V.regions().values() if _.is_compact()] + #Vreg = [[e.vertices()+(None,) for e in _.facets() for _ in V0] G = Graph() regions = [] - for reg in Vreg: + for reg in V0: regv = reg.vertex_graph() G = G.union(regv) circ = orient_circuit(regv.eulerian_circuit(), convex = True) @@ -1123,8 +1117,29 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): E = E.union(reg.vertex_graph()) p = next(E.vertex_iterator()) p0 = (p[0], p[1]) + # Construct a dual graph for the compact regions; the dual edges + # of this graph have as labels the corresponding edges in G + # DG = Graph(len(Vreg_e)) + # Edges = [] + # crd = {} + # for i, r in enumerate(Vreg_e): + # for e in r: + # a, b = e[:2] + # e1 = (b, a, None) + # if e not in Edges: + # Edges += [e, e1] + # crd[e]=(i,) + # crd[e1]=(i,) + # else: + # crd[e] += (i,) + # crd[e1] += (i,) + # EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] + # for e in EdgesDual: + # DG.add_edge(crd[e] + (e,)) + # EC = [v[0] for v in orient_circuit(E.eulerian_circuit())] if computebm: geombasis = geometric_basis(G, E, p, regions) + #geombasis = geometric_basis(G, E, EC, p, GD) segs = set() for p in geombasis: for s in zip(p[:-1], p[1:]): @@ -1223,10 +1238,10 @@ def braid2rels(L, d): from sage.groups.finitely_presented import wrap_FpGroup B = BraidGroup(d) F = FreeGroup(d) - L1 = deepcopy(L) + L1 = copy(L) low = True while low: - L2 = deepcopy(L1) + L2 = copy(L1) j = 0 l = L[j] other = False @@ -1243,7 +1258,7 @@ def braid2rels(L, d): except ValueError: j += 1 low = len(L2) < len(L1) - L1 = deepcopy(L2) + L1 = copy(L2) c0 = () b0 = L1 while b0[0] + b0[-1] == 0: From 72b7e50d6db800fdac5ca651eacc3aca4af15038 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 7 Oct 2022 08:59:57 +0200 Subject: [PATCH 039/122] adding dual_graph in gb --- src/sage/schemes/curves/zariski_vankampen.py | 60 ++++++++++++-------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 58c0ce07fd5..9cbaa961893 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -854,7 +854,9 @@ def geometric_basis(G, E, p, regions): - ``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. - It is given with counterclockwise orientation. + The edges are labelled as the dual edges and the vertices are labelled + by a tuple whose first element is the ordering and the second one is the + list of vertices in the region and ``E``. 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 @@ -981,32 +983,32 @@ def geometric_basis(G, E, p, regions): # cp1 += [(cp0[i + 1], e, None) for i, e in enumerate(cutpath)] # borra = [_ for _ in DG.edges(sort = False) if _[2] in cp1] # Gd.delete_edges(borra) + # Gd1, Gd2 = Gd.connected_components_subgraphs() + # GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]]+[v for e in Gd2.edges(sort=False) for v in e[2]]+[v for v in cutpath] + # G2=G.subgraph(GL2) + # GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]]+[v for e in Gd1.edges(sort=False) for v in e[2]]+[v for v in cutpath] + # G1=G.subgraph(GL1) Gd1,Gd2 = Gd.connected_components() GL1 = list(Set(_ for c in Gd1 for _ in regions[c])) GL1 = flatten(GL1) G1 = G.subgraph(GL1) - regions1 = [regions[i] for i in Gd1] GL2 = list(Set(_ for c in Gd2 for _ in regions[c])) GL2 = flatten(GL2) G2 = G.subgraph(GL2) + regions1 = [regions[i] for i in Gd1] regions2 = [regions[i] for i in Gd2] -# 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: + ########################## + qi = EC.index(q) + ri = EC.index(r) + if EC[qi + 1] in G2: G1, G2 = G2, G1 + #Gd1, Gd2 = Gd2, Gd1 regions1, regions2 = regions2, regions1 - E1, E2 = Ecut.connected_components_subgraphs() - if EC[EC.index(q) + 1] in E2: + if EC[qi + 1] in E2: E1, E2 = E2, E1 + 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) @@ -1017,7 +1019,17 @@ def geometric_basis(G, E, p, regions): 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 [qi..ri - 1]] + [_ for _ in reversed(cutpath)] + EC2 = cutpath + EC[ri + 1 : ] + EC[1 : qi + 1] + else: + EC1 = EC[qi : ] + EC[1 : ri] + [_ for _ in reversed(cutpath)] + EC2 = cutpath + EC[ri + 1 : qi + 1] + + + # gb1 = geometric_basis(G1, E1, EC1, q, Gd1) + # gb2 = geometric_basis(G2, E2, EC2, q, Gd2) gb1 = geometric_basis(G1, E1, q, regions1) gb2 = geometric_basis(G2, E2, q, regions2) @@ -1103,7 +1115,6 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): disc = discrim(glist) V = corrected_voronoi_diagram(tuple(disc)) V0 = [_ for _ in V.regions().values() if _.is_compact()] - #Vreg = [[e.vertices()+(None,) for e in _.facets() for _ in V0] G = Graph() regions = [] for reg in V0: @@ -1119,24 +1130,27 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): p0 = (p[0], p[1]) # Construct a dual graph for the compact regions; the dual edges # of this graph have as labels the corresponding edges in G - # DG = Graph(len(Vreg_e)) + #Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] + #Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + # DG = Graph(len(Vreg1)) + # DG.relabel(Vreg1) # Edges = [] # crd = {} - # for i, r in enumerate(Vreg_e): + # for i, r in enumerate(Vreg): # for e in r: # a, b = e[:2] # e1 = (b, a, None) # if e not in Edges: # Edges += [e, e1] - # crd[e]=(i,) - # crd[e1]=(i,) + # crd[e]=(Vreg1[i],) + # crd[e1]=(Vreg1[i],) # else: - # crd[e] += (i,) - # crd[e1] += (i,) + # crd[e] += (Vreg1[i],) + # crd[e1] += (Vreg1[i],) # EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] # for e in EdgesDual: # DG.add_edge(crd[e] + (e,)) - # EC = [v[0] for v in orient_circuit(E.eulerian_circuit())] + # EC = [v[0] for v in orient_circuit(E.eulerian_circuit())] if computebm: geombasis = geometric_basis(G, E, p, regions) #geombasis = geometric_basis(G, E, EC, p, GD) @@ -1151,7 +1165,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): #with open("debug.txt","a") as fd: #print('vertices-aristas Voronoi: ' + str(len(vertices)) + ', ' + str(len(segs)), file = fd) #with open("debug.txt","a") as fd: - #print('intervalos vertices empezado', file = fd) + #print('intervalos vertices empez+ado', file = fd) tocacheverts = tuple([(glist, v) for v in vertices]) populate_roots_interval_cache(tocacheverts) #with open("debug.txt","a") as fd: From 0064af511a500cc6c1fce9c98b8a5cad0b4ed781 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 7 Oct 2022 18:15:41 +0200 Subject: [PATCH 040/122] 1st end of geometric_basis changes --- src/sage/schemes/curves/zariski_vankampen.py | 293 +++++++++---------- 1 file changed, 139 insertions(+), 154 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 00fd581b43d..5aab5ae40e3 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -41,7 +41,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** import itertools -from copy import copy, deepcopy +from copy import copy from sage.combinat.combination import Combinations from sage.sets.set import Set @@ -62,6 +62,7 @@ from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField from sage.matrix.all import matrix +from sage.rings.all import PolynomialRing roots_interval_cache = {} @@ -160,16 +161,16 @@ def sgn(x, y): -def discrim(flist): +def discrim(pols): r""" - Return the points in the discriminant of the product of the polynomials of a list ``flist``. + 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: - - ``flist`` -- a tuple of polynomials in two variables with coefficients in a + - ``pols`` -- a list or tuple of polynomials in two variables with coefficients in a number field with a fixed embedding in `\QQbar` OUTPUT: @@ -189,31 +190,25 @@ def discrim(flist): 0.2613789792873551? - 0.4527216721561923?*I, 0.2613789792873551? + 0.4527216721561923?*I] """ + flist = tuple(pols) x, y = flist[0].parent().gens() F = flist[0].base_ring() + R = PolynomialRing(F,(x,)) @parallel - def discrim_pairs(ftuple): - if len(ftuple) == 1: - f = ftuple[0] - return F[x](f.discriminant(y)) - elif len(ftuple) == 2: - f, g = ftuple - return F[x](f.resultant(g, y)) - - pairs = [((f,),) for f in flist] + [((f, g),) for f, g in Combinations(flist, 2)] + def discrim_pairs(f, g): + if f == g: + return R(f.discriminant(y)) + else: + return R(f.resultant(g, y)) + + pairs = [(f, f) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) rts = [] poly = 1 - #for f in flist: - #aux = F[x](f.discriminant(y)) - #poly = aux * poly - #for f, g in Combinations(flist, 2): - #aux = F[x](f.resultant(g, y)) - #poly = aux * poly for u in fdiscrim: h0 = u[1].radical() - h1 = F[x](h0 / h0.gcd(poly)) + h1 = h0 // h0.gcd(poly) rts += h1.roots(QQbar, multiplicities = False) poly = poly * h1 return rts @@ -470,7 +465,7 @@ def fieldI(F0): OUTPUT: - The embedding in `QQbar` of an extension `F` of `F0` containing `I`. + The extension `F` of `F0` containing `I` with an embedding in `QQbar`. EXAMPLES:: @@ -503,19 +498,19 @@ def fieldI(F0): F1 = NumberField(q, 'b', embedding = b1) b = F1.gen() if (F0.is_subring(F1)): - break - return F1 + return F1 + @parallel -def roots_interval(flist, x0): +def roots_interval(pols, x0): """ Find disjoint intervals that isolate the roots of a polynomial for a fixed value of the first variable. INPUT: - - ``flist`` -- a tuple of bivariate squarefree polynomials - - ``x0`` -- a value where the first coordinate will be fixed + - ``pols`` -- a list or a tuple of bivariate squarefree polynomials + - ``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, where @@ -549,6 +544,7 @@ def roots_interval(flist, x0): -0.933012701892219 + 1.29903810567666*I, -0.0669872981077806 + 0.433012701892219*I)] """ + flist = tuple(pols) f = prod(flist) F0 = f.base_ring() F1 = fieldI(F0) @@ -557,10 +553,10 @@ def roots_interval(flist, x0): f1 = f.change_ring(F1) x, y = f1.parent().gens() Ux = [F1[y](_.subs({x: F1(x0)})) for _ in U1] - fx = prod(Ux) + fx = F1[y](f1.subs({x: F1(x0)})) roots = [] - for _ in Ux: - roots += _.roots(QQbar, multiplicities=False) + for pol in Ux: + roots += pol.roots(QQbar, multiplicities=False) result = {} for i, r in enumerate(roots): prec = 53 @@ -642,22 +638,14 @@ def populate_roots_interval_cache(inputs): """ global roots_interval_cache tocompute = [inp for inp in inputs if inp not in roots_interval_cache] - problem_par = False - while not problem_par: + problem_par = True + while problem_par: # hack to deal with random fails in parallelization try: - #with open("debug.txt","a") as fd: - #print("Entra en populate",file=fd) result = roots_interval(tocompute) - #with open("debug.txt","a") as fd: - #print("Sale en populate",file=fd) for r in result: roots_interval_cache[r[0][0]] = r[1] - problem_par = True - #with open("debug.txt","a") as fd: - #print("Fin populate",file=fd) + problem_par = False except TypeError: - #with open("debug.txt","a") as fd: - #print("Falla en populate",file=fd) pass @@ -670,8 +658,9 @@ def braid_in_segment(glist, x0, x1, precision = {}): INPUT: - ``glist`` -- a tuple of polynomials in two variables - - ``x0`` -- a complex number, usually a Gauss rational - - ``x1`` -- a complex number, usually a Gauss rational + - ``x0`` -- a Gauss rational + - ``x1`` -- a Gauss rational + - ``precision`` -- a dictionary (default, an empty one) which will apply to each element of ``glist`` a *precision* OUTPUT: @@ -716,12 +705,9 @@ def braid_in_segment(glist, x0, x1, precision = {}): U1 = tuple(_.change_ring(F1) for _ in glist) g1 = g.change_ring(F1) x, y = g1.parent().gens() - #X0 = QQ(x0.real()) + I1 * QQ(x0.imag()) - #X1 = QQ(x1.real()) + I1 * QQ(x1.imag()) X0 = F1(x0) X1 = F1(x1) intervals = {} -# precision = {} if precision == {}: # new precision ={f: 53 for f in U1} # new y0s = [] @@ -732,7 +718,6 @@ def braid_in_segment(glist, x0, x1, precision = {}): 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]) intervals[f] = [r.interval(CIFp) for r in y0sf] @@ -760,12 +745,9 @@ def braid_in_segment(glist, x0, x1, precision = {}): initialstrands.append([(0, center.real(), center.imag()), (1, cs[0][1], cs[0][2])]) matched += 1 if matched != 1: -# if matched == 0: -# raise ValueError("unable to match braid endpoint with root") precision = {f: precision[f] * 2 for f in U1} # new return braid_in_segment(U1, x0, x1, precision = precision) # new -# if matched > 1: -# raise ValueError("braid endpoint mathes more than one root") + matched = 0 for center, interval in finalintervals.items(): if fp in interval: @@ -774,10 +756,6 @@ def braid_in_segment(glist, x0, x1, precision = {}): if matched != 1: precision = {f: precision[f] * 2 for f in U1} # new return braid_in_segment(U1, x0, x1, precision = precision) # new - #if matched == 0: - #raise ValueError("unable to match braid endpoint with root") - #if matched > 1: - #raise ValueError("braid endpoint matches more than one root") initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) @@ -792,6 +770,8 @@ def orient_circuit(circuit, convex = False): - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given by a list of edges + + - ``convex`` -- a boolean function, if set to ``True`` a simpler computation is made OUTPUT: @@ -853,22 +833,26 @@ def orient_circuit(circuit, convex = False): return circuit prec *= 2 - -def geometric_basis(G, E, p, regions): +def geometric_basis(G, E, EC, 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 which correspond to the bounded edgess of a Voronoi Diagram - - ``E`` -- the subgraph of ``G`` formed by the edges that touch - an unbounded region + - ``E`` -- a subgraph of ``G`` which is a cycle; it corresponds to the bounded edges touching + an unbounded region of a Voronoi Diagram + + - ``EC`` -- A counterclockwise orientation of the vertices of ``E`` - ``p`` -- a vertex of ``E`` - - - ``regions`` -- the bounded regions as a list of positively oriented - lists of vertices of ``G`` + + - ``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 ordering and the second one is the + list of vertices in the region and ``E``. 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 @@ -880,20 +864,39 @@ def geometric_basis(G, E, p, regions): sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, orient_circuit 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: V0 = [_ for _ in V.regions().values() if _.is_compact()] sage: G = Graph() - sage: Vreg = [_ for _ in V.regions().values() if _.is_compact()] - sage: regions = [] - sage: for reg in Vreg: + sage: for reg in V0: ....: regv = reg.vertex_graph() ....: G = G.union(regv) - ....: circ = orient_circuit(regv.eulerian_circuit(), convex = True) - ....: regions.append(circ) 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, regions) + sage: Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] + sage: Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + sage: DG = Graph(len(Vreg1)) + sage: DG.relabel(Vreg1) + sage: Edges = [] + sage: crd = {} + sage: for i, r in enumerate(Vreg): + ....: for e in r: + ....: a, b = e[ : 2] + ....: e1 = (b, a, None) + ....: if e not in Edges: + ....: Edges += [e, e1] + ....: crd[e]=(Vreg1[i],) + ....: crd[e1]=(Vreg1[i],) + ....: else: + ....: crd[e] += (Vreg1[i],) + ....: crd[e1] += (Vreg1[i],) + sage: EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] + sage: for e in EdgesDual: + ....: DG.add_edge(crd[e] + (e,)) + sage: EC0 = orient_circuit(E.eulerian_circuit()) + sage: EC = [EC0[0][0]] + [e[1] for e in EC0] + sage: geometric_basis(G, E, EC, p, DG) [[A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), @@ -930,88 +933,54 @@ def geometric_basis(G, E, p, regions): A vertex at (-2, 2), A vertex at (-2, -2)]] """ - region_graphs = [] - for reg in regions: - g = Graph(reg) - region_graphs.append(g) - 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 + EC = EC[i:-1] + EC[:i + 1] # A counterclockwise eulerian circuit on the boundary, based 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: + InternalEdges = [_ for _ in G.edges(sort = False) 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) + if not Internal: for v in E: if len(E.neighbors(v)) > 2: - I.add_vertex(v) + Internal.add_vertex(v) for i, ECi in enumerate(EC): # q and r are the points we will cut through - if EC[i] in I: + if EC[i] in Internal: q = EC[i] connecting_path = EC[:i] break - if EC[-i] in I: + if EC[-i] in Internal: q = EC[-i] connecting_path = list(reversed(EC[-i:])) break - distancequotients = [(E.distance(q, v)**2 / I.distance(q, v), v) for v in E - if v in I.connected_component_containing_vertex(q) and not v == q] + distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in E + if v in Internal.connected_component_containing_vertex(q) and not v == q] r = max(distancequotients)[1] - cutpath = I.shortest_path(q, r) + qi = EC.index(q) + ri = EC.index(r) + cutpath = Internal.shortest_path(q, r) #Gcut = copy(G) 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. It happens. Construct the dual graph. - Gd = Graph(len(regions)) - for i, j in Combinations(range(len(regions)), 2): - Ga = region_graphs[i] - La = Ga.edges(sort = True) - Gb = region_graphs[j] - Lb = Gb.edges(sort = True) - Uc = [(a, b, c) for a, b, c in La if (a, b, c) in Lb or (b, a, c) in Lb] - if len(Uc) >= 1: - arista = Uc[0] - if arista[0] in cutpath and arista[1] in cutpath: - k = cutpath.index(arista[0]) - l = cutpath.index(arista[1]) - if abs(k - l) != 1 and Set(k, l)!=Set(0, len(regions) - 1): - Gd.add_edge((i,j)) - else: - Gd.add_edge((i, j)) - #if Gcut.connected_components_number() != 2: - #raise ValueError("unable to compute a correct path") - Gd1,Gd2 = Gd.connected_components() - GL1 = list(Set(_ for c in Gd1 for _ in regions[c])) - GL1 = flatten(GL1) - G1 =G.subgraph(GL1) - regions1 = [regions[i] for i in Gd1] - GL2 = list(Set(_ for c in Gd2 for _ in regions[c])) - GL2 = flatten(GL2) - G2 = G.subgraph(GL2) - regions2 = [regions[i] for i in Gd2] -# 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: + Gd = copy(dual_graph) + cp1 = [(e, cutpath[i + 1], None) for i, e in enumerate(cutpath[: -1])] + cp1 += [(cutpath[i + 1], e, None) for i, e in enumerate(cutpath[: -1])] + borra = [_ for _ in Gd.edges(sort = False) if _[2] in cp1] + Gd.delete_edges(borra) + Gd1, Gd2 = Gd.connected_components_subgraphs() + GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]]+[v for e in Gd2.edges(sort=False) for v in e[2]]+[v for v in cutpath] + G2=G.subgraph(GL2) + GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]]+[v for e in Gd1.edges(sort=False) for v in e[2]]+[v for v in cutpath] + G1=G.subgraph(GL1) + if EC[qi + 1] in G2: G1, G2 = G2, G1 - regions1, regions2 = regions2, regions1 - + Gd1, Gd2 = Gd2, Gd1 E1, E2 = Ecut.connected_components_subgraphs() - if EC[EC.index(q) + 1] in E2: + if EC[qi + 1] in E2: E1, E2 = E2, E1 + 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) @@ -1022,9 +991,17 @@ def geometric_basis(G, E, p, regions): 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)] + [_ for _ in reversed(cutpath)] + EC2 = cutpath + EC[ri + 1 : -1] + EC[ : qi + 1] + else: + EC1 = EC[qi : ] + EC[1 : ri] + [_ for _ in reversed(cutpath)] + EC2 = cutpath + EC[ri + 1 : qi + 1] + - gb1 = geometric_basis(G1, E1, q, regions1) - gb2 = geometric_basis(G2, E2, q, regions2) + + 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 @@ -1106,25 +1083,44 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): arrangement1 = tuple(f1.subs({x: x + y}) for f1 in arrangement1) glist = tuple(f1.subs({x: x + y}) for f1 in glist) disc = discrim(glist) - #with open("debug.txt","a") as fd: - #print('discriminante: ' + str(len(disc)), file = fd) V = corrected_voronoi_diagram(tuple(disc)) - Vreg=[_ for _ in V.regions().values() if _.is_compact()] + V0 = [_ for _ in V.regions().values() if _.is_compact()] G = Graph() - regions = [] - for reg in Vreg: + for reg in V0: regv = reg.vertex_graph() G = G.union(regv) - circ = orient_circuit(regv.eulerian_circuit(), convex = True) - regions.append(circ) 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()) p0 = (p[0], p[1]) + # Construct a dual graph for the compact regions; the dual edges + # of this graph have as labels the corresponding edges in G + Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] + Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + DG = Graph(len(Vreg1)) + DG.relabel(Vreg1) + Edges = [] + crd = {} + for i, r in enumerate(Vreg): + for e in r: + a, b = e[:2] + e1 = (b, a, None) + if e not in Edges: + Edges += [e, e1] + crd[e]=(Vreg1[i],) + crd[e1]=(Vreg1[i],) + else: + crd[e] += (Vreg1[i],) + crd[e1] += (Vreg1[i],) + EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] + for e in EdgesDual: + DG.add_edge(crd[e] + (e,)) + EC0 = orient_circuit(E.eulerian_circuit()) + EC = [EC0[0][0]] + [e[1] for e in EC0] if computebm: - geombasis = geometric_basis(G, E, p, regions) + geombasis = geometric_basis(G, E, EC, p, DG) segs = set() for p in geombasis: for s in zip(p[:-1], p[1:]): @@ -1133,22 +1129,14 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): I = QQbar.gen() segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] vertices = list(set(flatten(segs))) - #with open("debug.txt","a") as fd: - #print('vertices-aristas Voronoi: ' + str(len(vertices)) + ', ' + str(len(segs)), file = fd) - #with open("debug.txt","a") as fd: - #print('intervalos vertices empezado', file = fd) tocacheverts = tuple([(glist, v) for v in vertices]) populate_roots_interval_cache(tocacheverts) - #with open("debug.txt","a") as fd: - #print('intervalos vertices hecho', file = fd) end_braid_computation = False while not end_braid_computation: try: braidscomputed = (braid_in_segment([(glist, seg[0], seg[1]) for seg in segs])) segsbraids = {} - #with open("debug.txt","a") as fd: - #print('Antes de paralelizar', file = fd) for braidcomputed in braidscomputed: seg = (braidcomputed[0][0][1], braidcomputed[0][0][2]) beginseg = (QQ(seg[0].real()), QQ(seg[0].imag())) @@ -1159,7 +1147,6 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): end_braid_computation = True except ChildProcessError: # hack to deal with random fails first time pass - #B = b.parent() B=BraidGroup(d) result = [] for path in geombasis: @@ -1187,8 +1174,6 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): if not holdstrand: roots_base.sort() strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} - #with open("debug.txt","a") as fd: - # print('diccionario hecho: ' + str(dic1), file = fd) if computebm and not holdstrand: return (result, strands) elif computebm and holdstrand: @@ -1223,10 +1208,10 @@ def braid2rels(L, d): from sage.groups.finitely_presented import wrap_FpGroup B = BraidGroup(d) F = FreeGroup(d) - L1 = deepcopy(L) + L1 = copy(L) low = True while low: - L2 = deepcopy(L1) + L2 = copy(L1) j = 0 l = L[j] other = False @@ -1243,7 +1228,7 @@ def braid2rels(L, d): except ValueError: j += 1 low = len(L2) < len(L1) - L1 = deepcopy(L2) + L1 = copy(L2) c0 = () b0 = L1 while b0[0] + b0[-1] == 0: @@ -1479,7 +1464,7 @@ def strand_components(flist): sage: from sage.schemes.curves.zariski_vankampen import strand_components # optional - sirocco sage: R. = QQ[] sage: flist = [x^2 - y^3, x + 3 * y - 5] - sage: strand_components(flist) + sage: strand_components(flist) # optional - sirocco {1: 1, 2: 2, 3: 1, 4: 1} """ f = prod(flist) From 82ad6b65debb985250e735518f64c04acaaafad9 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 7 Oct 2022 18:16:02 +0200 Subject: [PATCH 041/122] 1st end of geometric_basis changes --- src/sage/schemes/curves/zariski_vankampen.py | 178 +++++++------------ 1 file changed, 67 insertions(+), 111 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9cbaa961893..5aab5ae40e3 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -833,8 +833,7 @@ def orient_circuit(circuit, convex = False): return circuit prec *= 2 -#def geometric_basis(G, E, EC, p, dual_graph): -def geometric_basis(G, E, p, regions): +def geometric_basis(G, E, EC, p, dual_graph): r""" Return a geometric basis, based on a vertex. @@ -848,9 +847,6 @@ def geometric_basis(G, E, p, regions): - ``EC`` -- A counterclockwise orientation of the vertices of ``E`` - ``p`` -- a vertex of ``E`` - - - ``regions`` -- the bounded regions as a list of positively oriented - lists of edges of ``G`` - ``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. @@ -868,20 +864,39 @@ def geometric_basis(G, E, p, regions): sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, orient_circuit 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: V0 = [_ for _ in V.regions().values() if _.is_compact()] sage: G = Graph() - sage: Vreg = [_ for _ in V.regions().values() if _.is_compact()] - sage: regions = [] - sage: for reg in Vreg: + sage: for reg in V0: ....: regv = reg.vertex_graph() ....: G = G.union(regv) - ....: circ = orient_circuit(regv.eulerian_circuit(), convex = True) - ....: regions.append(circ) 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, regions) + sage: Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] + sage: Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + sage: DG = Graph(len(Vreg1)) + sage: DG.relabel(Vreg1) + sage: Edges = [] + sage: crd = {} + sage: for i, r in enumerate(Vreg): + ....: for e in r: + ....: a, b = e[ : 2] + ....: e1 = (b, a, None) + ....: if e not in Edges: + ....: Edges += [e, e1] + ....: crd[e]=(Vreg1[i],) + ....: crd[e1]=(Vreg1[i],) + ....: else: + ....: crd[e] += (Vreg1[i],) + ....: crd[e1] += (Vreg1[i],) + sage: EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] + sage: for e in EdgesDual: + ....: DG.add_edge(crd[e] + (e,)) + sage: EC0 = orient_circuit(E.eulerian_circuit()) + sage: EC = [EC0[0][0]] + [e[1] for e in EC0] + sage: geometric_basis(G, E, EC, p, DG) [[A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), @@ -918,26 +933,14 @@ def geometric_basis(G, E, p, regions): A vertex at (-2, 2), A vertex at (-2, -2)]] """ - ######## This will disappear after the changes - region_graphs = [] - for reg in regions: - g = Graph(reg) - region_graphs.append(g) - 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 + EC = EC[i:-1] + EC[:i + 1] # A counterclockwise eulerian circuit on the boundary, based at p if G.size() == E.size(): if E.is_cycle(): return [EC] InternalEdges = [_ for _ in G.edges(sort = False) 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) - # Internal = Graph() # Replaced by the above lines - # for e in G.edges(sort=False): - # if not E.has_edge(e): - # Internal.add_edge(e) # interior graph - # treat the case where I is empty if not Internal: for v in E: if len(E.neighbors(v)) > 2: @@ -954,56 +957,25 @@ def geometric_basis(G, E, p, regions): distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in E if v in Internal.connected_component_containing_vertex(q) and not v == q] r = max(distancequotients)[1] + qi = EC.index(q) + ri = EC.index(r) cutpath = Internal.shortest_path(q, r) #Gcut = copy(G) Ecut = copy(E) Ecut.delete_vertices([q, r]) - #Gcut.delete_vertices(cutpath) - ############## To be replaced - Gd = Graph(len(regions)) - for i, j in Combinations(range(len(regions)), 2): - Ga = region_graphs[i] - La = Ga.edges(sort = True) - Gb = region_graphs[j] - Lb = Gb.edges(sort = True) - Uc = [(a, b, c) for a, b, c in La if (a, b, c) in Lb or (b, a, c) in Lb] - if len(Uc) >= 1: - arista = Uc[0] - if arista[0] in cutpath and arista[1] in cutpath: - k = cutpath.index(arista[0]) - l = cutpath.index(arista[1]) - if abs(k - l) != 1 and Set(k, l)!=Set(0, len(regions) - 1): - Gd.add_edge((i,j)) - else: - Gd.add_edge((i, j)) - ####################### - # Gd = copy(dual_graph) - # cp0 = cutpath + [cutpath[0]] - # cp1 = [(e, cp0[i + 1], None) for i, e in enumerate(cutpath)] - # cp1 += [(cp0[i + 1], e, None) for i, e in enumerate(cutpath)] - # borra = [_ for _ in DG.edges(sort = False) if _[2] in cp1] - # Gd.delete_edges(borra) - # Gd1, Gd2 = Gd.connected_components_subgraphs() - # GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]]+[v for e in Gd2.edges(sort=False) for v in e[2]]+[v for v in cutpath] - # G2=G.subgraph(GL2) - # GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]]+[v for e in Gd1.edges(sort=False) for v in e[2]]+[v for v in cutpath] - # G1=G.subgraph(GL1) - Gd1,Gd2 = Gd.connected_components() - GL1 = list(Set(_ for c in Gd1 for _ in regions[c])) - GL1 = flatten(GL1) - G1 = G.subgraph(GL1) - GL2 = list(Set(_ for c in Gd2 for _ in regions[c])) - GL2 = flatten(GL2) - G2 = G.subgraph(GL2) - regions1 = [regions[i] for i in Gd1] - regions2 = [regions[i] for i in Gd2] - ########################## - qi = EC.index(q) - ri = EC.index(r) + Gd = copy(dual_graph) + cp1 = [(e, cutpath[i + 1], None) for i, e in enumerate(cutpath[: -1])] + cp1 += [(cutpath[i + 1], e, None) for i, e in enumerate(cutpath[: -1])] + borra = [_ for _ in Gd.edges(sort = False) if _[2] in cp1] + Gd.delete_edges(borra) + Gd1, Gd2 = Gd.connected_components_subgraphs() + GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]]+[v for e in Gd2.edges(sort=False) for v in e[2]]+[v for v in cutpath] + G2=G.subgraph(GL2) + GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]]+[v for e in Gd1.edges(sort=False) for v in e[2]]+[v for v in cutpath] + G1=G.subgraph(GL1) if EC[qi + 1] in G2: G1, G2 = G2, G1 - #Gd1, Gd2 = Gd2, Gd1 - regions1, regions2 = regions2, regions1 + Gd1, Gd2 = Gd2, Gd1 E1, E2 = Ecut.connected_components_subgraphs() if EC[qi + 1] in E2: E1, E2 = E2, E1 @@ -1020,18 +992,16 @@ def geometric_basis(G, E, p, regions): if n in E2: E2.add_edge(v, n, None) if qi < ri: - EC1 = [EC[j] for j in [qi..ri - 1]] + [_ for _ in reversed(cutpath)] - EC2 = cutpath + EC[ri + 1 : ] + EC[1 : qi + 1] + EC1 = [EC[j] for j in range(qi, ri)] + [_ for _ in reversed(cutpath)] + EC2 = cutpath + EC[ri + 1 : -1] + EC[ : qi + 1] else: EC1 = EC[qi : ] + EC[1 : ri] + [_ for _ in reversed(cutpath)] EC2 = cutpath + EC[ri + 1 : qi + 1] - # gb1 = geometric_basis(G1, E1, EC1, q, Gd1) - # gb2 = geometric_basis(G2, E2, EC2, q, Gd2) - gb1 = geometric_basis(G1, E1, q, regions1) - gb2 = geometric_basis(G2, E2, q, regions2) + 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 @@ -1116,12 +1086,9 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): V = corrected_voronoi_diagram(tuple(disc)) V0 = [_ for _ in V.regions().values() if _.is_compact()] G = Graph() - regions = [] for reg in V0: regv = reg.vertex_graph() G = G.union(regv) - circ = orient_circuit(regv.eulerian_circuit(), convex = True) - regions.append(circ) E = Graph() for reg in V.regions().values(): if reg.rays() or reg.lines(): @@ -1130,30 +1097,30 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): p0 = (p[0], p[1]) # Construct a dual graph for the compact regions; the dual edges # of this graph have as labels the corresponding edges in G - #Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] - #Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} - # DG = Graph(len(Vreg1)) - # DG.relabel(Vreg1) - # Edges = [] - # crd = {} - # for i, r in enumerate(Vreg): - # for e in r: - # a, b = e[:2] - # e1 = (b, a, None) - # if e not in Edges: - # Edges += [e, e1] - # crd[e]=(Vreg1[i],) - # crd[e1]=(Vreg1[i],) - # else: - # crd[e] += (Vreg1[i],) - # crd[e1] += (Vreg1[i],) - # EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] - # for e in EdgesDual: - # DG.add_edge(crd[e] + (e,)) - # EC = [v[0] for v in orient_circuit(E.eulerian_circuit())] + Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] + Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + DG = Graph(len(Vreg1)) + DG.relabel(Vreg1) + Edges = [] + crd = {} + for i, r in enumerate(Vreg): + for e in r: + a, b = e[:2] + e1 = (b, a, None) + if e not in Edges: + Edges += [e, e1] + crd[e]=(Vreg1[i],) + crd[e1]=(Vreg1[i],) + else: + crd[e] += (Vreg1[i],) + crd[e1] += (Vreg1[i],) + EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] + for e in EdgesDual: + DG.add_edge(crd[e] + (e,)) + EC0 = orient_circuit(E.eulerian_circuit()) + EC = [EC0[0][0]] + [e[1] for e in EC0] if computebm: - geombasis = geometric_basis(G, E, p, regions) - #geombasis = geometric_basis(G, E, EC, p, GD) + geombasis = geometric_basis(G, E, EC, p, DG) segs = set() for p in geombasis: for s in zip(p[:-1], p[1:]): @@ -1162,22 +1129,14 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): I = QQbar.gen() segs = [(a[0] + I * a[1], b[0] + I * b[1]) for a, b in segs] vertices = list(set(flatten(segs))) - #with open("debug.txt","a") as fd: - #print('vertices-aristas Voronoi: ' + str(len(vertices)) + ', ' + str(len(segs)), file = fd) - #with open("debug.txt","a") as fd: - #print('intervalos vertices empez+ado', file = fd) tocacheverts = tuple([(glist, v) for v in vertices]) populate_roots_interval_cache(tocacheverts) - #with open("debug.txt","a") as fd: - #print('intervalos vertices hecho', file = fd) end_braid_computation = False while not end_braid_computation: try: braidscomputed = (braid_in_segment([(glist, seg[0], seg[1]) for seg in segs])) segsbraids = {} - #with open("debug.txt","a") as fd: - #print('Antes de paralelizar', file = fd) for braidcomputed in braidscomputed: seg = (braidcomputed[0][0][1], braidcomputed[0][0][2]) beginseg = (QQ(seg[0].real()), QQ(seg[0].imag())) @@ -1188,7 +1147,6 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): end_braid_computation = True except ChildProcessError: # hack to deal with random fails first time pass - #B = b.parent() B=BraidGroup(d) result = [] for path in geombasis: @@ -1216,8 +1174,6 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): if not holdstrand: roots_base.sort() strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} - #with open("debug.txt","a") as fd: - # print('diccionario hecho: ' + str(dic1), file = fd) if computebm and not holdstrand: return (result, strands) elif computebm and holdstrand: From 20a1947a22d891c8cde09f683de0504df691c526 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 7 Oct 2022 18:28:02 +0200 Subject: [PATCH 042/122] format corrections --- src/sage/schemes/curves/zariski_vankampen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 5aab5ae40e3..859fdf57be0 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -969,9 +969,9 @@ def geometric_basis(G, E, EC, p, dual_graph): borra = [_ for _ in Gd.edges(sort = False) if _[2] in cp1] Gd.delete_edges(borra) Gd1, Gd2 = Gd.connected_components_subgraphs() - GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]]+[v for e in Gd2.edges(sort=False) for v in e[2]]+[v for v in cutpath] + GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]] + [v for e in Gd2.edges(sort = False) for v in e[2]] + [v for v in cutpath] G2=G.subgraph(GL2) - GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]]+[v for e in Gd1.edges(sort=False) for v in e[2]]+[v for v in cutpath] + GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]] + [v for e in Gd1.edges(sort = False) for v in e[2]] + [v for v in cutpath] G1=G.subgraph(GL1) if EC[qi + 1] in G2: G1, G2 = G2, G1 @@ -1097,8 +1097,8 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): p0 = (p[0], p[1]) # Construct a dual graph for the compact regions; the dual edges # of this graph have as labels the corresponding edges in G - Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] - Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + Vreg = [[e.vertices() + (None, ) for e in _.facets()] for _ in V0] + Vreg1 = {V0.index(_) : (V0.index(_), tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} DG = Graph(len(Vreg1)) DG.relabel(Vreg1) Edges = [] From b90c5d751d62c04fcadc027eb81a31de9294a0de Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 17 Oct 2022 07:39:14 +0200 Subject: [PATCH 043/122] ultimos cambios --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 859fdf57be0..54d3635cde3 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -934,7 +934,7 @@ def geometric_basis(G, E, EC, p, dual_graph): A vertex at (-2, -2)]] """ i = EC.index(p) - EC = EC[i:-1] + EC[:i + 1] # A counterclockwise eulerian circuit on the boundary, based at p + EC = EC[i:-1] + EC[: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] From 2abee3d4fa485a05ffa26f961484e274db737e1b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 15 Feb 2023 09:58:34 +0100 Subject: [PATCH 044/122] correciones zvk --- src/sage/schemes/curves/zariski_vankampen.py | 72 +++++++++----------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 5aab5ae40e3..e9e6ccffdc1 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -493,11 +493,13 @@ def fieldI(F0): F1a = F0a.absolute_field('b0') b0 = F1a.gen() q = b0.minpoly() - qroots = q.roots(QQbar, multiplicities = False) - for b1 in qroots: + qembd = F1a.embeddings(QQbar) + for h1 in qembd: + b1 = h1(b0) + b2 = h1(F1a(F0a.gen(0))) + b3 = F0.gen(0) F1 = NumberField(q, 'b', embedding = b1) - b = F1.gen() - if (F0.is_subring(F1)): + if b3 in F1 and b2.imag()>0: return F1 @@ -546,14 +548,11 @@ def roots_interval(pols, x0): """ flist = tuple(pols) f = prod(flist) - F0 = f.base_ring() - F1 = fieldI(F0) + F1 = f.base_ring() I1 = F1(QQbar.gen()) - U1 = [_.change_ring(F1) for _ in flist] - f1 = f.change_ring(F1) - x, y = f1.parent().gens() - Ux = [F1[y](_.subs({x: F1(x0)})) for _ in U1] - fx = F1[y](f1.subs({x: F1(x0)})) + x, y = f.parent().gens() + Ux = [F1[y](_.subs({x: F1(x0)})) for _ in flist] + fx = F1[y](f.subs({x: F1(x0)})) roots = [] for pol in Ux: roots += pol.roots(QQbar, multiplicities=False) @@ -699,19 +698,16 @@ def braid_in_segment(glist, x0, x1, precision = {}): s5*s3^-1 """ g = prod(glist) - F0 = g.base_ring() - F1 = fieldI(F0) + F1 = g.base_ring() I1 = F1(QQbar.gen()) - U1 = tuple(_.change_ring(F1) for _ in glist) - g1 = g.change_ring(F1) - x, y = g1.parent().gens() + x, y = g.parent().gens() X0 = F1(x0) X1 = F1(x1) intervals = {} if precision == {}: # new - precision ={f: 53 for f in U1} # new + precision ={f: 53 for f in glist} # new y0s = [] - for f in U1: + for f in glist: if f.variables() == (y,): f0 = F1[y](f) else: @@ -725,16 +721,16 @@ def braid_in_segment(glist, x0, x1, precision = {}): break precision[f] *= 2 strands = [] - for f in U1: + for f in glist: for i in intervals[f]: - aux = followstrand(f, [p for p in U1 if p != f], x0, x1, i.center(), precision[f]) + aux = followstrand(f, [p for p in glist if p != f], x0, x1, i.center(), precision[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(U1, X0) - finalintervals = roots_interval_cached(U1, X1) + initialintervals = roots_interval_cached(glist, X0) + finalintervals = roots_interval_cached(glist, X1) I = QQbar.gen() for cs in complexstrands: ip = cs[0][1] + I * cs[0][2] @@ -745,8 +741,8 @@ def braid_in_segment(glist, x0, x1, precision = {}): initialstrands.append([(0, center.real(), center.imag()), (1, cs[0][1], cs[0][2])]) matched += 1 if matched != 1: - precision = {f: precision[f] * 2 for f in U1} # new - return braid_in_segment(U1, x0, x1, precision = precision) # new + precision = {f: precision[f] * 2 for f in list} # new + return braid_in_segment(glist, x0, x1, precision = precision) # new matched = 0 for center, interval in finalintervals.items(): @@ -754,8 +750,8 @@ def braid_in_segment(glist, x0, x1, precision = {}): finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())]) matched += 1 if matched != 1: - precision = {f: precision[f] * 2 for f in U1} # new - return braid_in_segment(U1, x0, x1, precision = precision) # new + precision = {f: precision[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision = precision) # new initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) @@ -934,7 +930,7 @@ def geometric_basis(G, E, EC, p, dual_graph): A vertex at (-2, -2)]] """ i = EC.index(p) - EC = EC[i:-1] + EC[:i + 1] # A counterclockwise eulerian circuit on the boundary, based at p + EC = EC[i:-1] + EC[: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] @@ -969,9 +965,9 @@ def geometric_basis(G, E, EC, p, dual_graph): borra = [_ for _ in Gd.edges(sort = False) if _[2] in cp1] Gd.delete_edges(borra) Gd1, Gd2 = Gd.connected_components_subgraphs() - GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]]+[v for e in Gd2.edges(sort=False) for v in e[2]]+[v for v in cutpath] + GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]] + [v for e in Gd2.edges(sort = False) for v in e[2]] + [v for v in cutpath] G2=G.subgraph(GL2) - GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]]+[v for e in Gd1.edges(sort=False) for v in e[2]]+[v for v in cutpath] + GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]] + [v for e in Gd1.edges(sort = False) for v in e[2]] + [v for v in cutpath] G1=G.subgraph(GL1) if EC[qi + 1] in G2: G1, G2 = G2, G1 @@ -1072,8 +1068,11 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): arrangement1 = (f, ) else: arrangement1 = arrangement + F = fieldI(f.base_ring()) + I1 = F(QQbar.gen()) + f = f.change_ring(F) + arrangement1 = [_.change_ring(F) for _ in arrangement1] x, y = f.parent().gens() - F = f.base_ring() glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor()) g = prod(glist) d = g.degree(y) @@ -1097,8 +1096,8 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): p0 = (p[0], p[1]) # Construct a dual graph for the compact regions; the dual edges # of this graph have as labels the corresponding edges in G - Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] - Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + Vreg = [[e.vertices() + (None, ) for e in _.facets()] for _ in V0] + Vreg1 = {V0.index(_) : (V0.index(_), tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} DG = Graph(len(Vreg1)) DG.relabel(Vreg1) Edges = [] @@ -1159,16 +1158,11 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): if len(arrangement1) == 1: return result else: - F1 = fieldI(f.base_ring()) - I1 = F1(QQbar.gen()) - p1 = p0[0] + I1 * p0[1] - U1 = [_.change_ring(F1) for _ in arrangement1] - x, y = U1[0].parent().gens() strands = {} roots_base = [] - for i, h in enumerate(U1): + for i, h in enumerate(arrangement1): h0 = h.subs({x: p1}) - h1 = F1[y](h0) + h1 = F[y](h0) rt = h1.roots(QQbar, multiplicities = False) roots_base += [(_,i) for _ in rt] if not holdstrand: From c4641dca8c2f876e5ff2bfcdd4b6cb21d37fd1e2 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 15 Feb 2023 17:12:43 +0100 Subject: [PATCH 045/122] avoid repetitions --- src/sage/schemes/curves/zariski_vankampen.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 558761effbc..be8bde4e419 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1161,8 +1161,6 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): p1 = p0[0] + I1 * p0[1] strands = {} roots_base = [] - strands = {} - roots_base = [] for i, h in enumerate(arrangement1): h0 = h.subs({x: p1}) h1 = F[y](h0) From 050d8bce2ba65b14954d2c67422651fdf521e42f Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 15 Feb 2023 17:19:48 +0100 Subject: [PATCH 046/122] style --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index be8bde4e419..3a1248e7686 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1378,7 +1378,7 @@ def relation(x, b): else: simplified = False relations = (braid2rels([(b.Tietze(), d) for b in bm])) - R = [] + R = [] for r in relations: R += r[1] if projective: From 8c3634205697db94938669f03f2f8f812c421e51 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 15 Feb 2023 21:20:58 +0100 Subject: [PATCH 047/122] changes in base fields caused errors in tests --- src/sage/schemes/curves/zariski_vankampen.py | 26 ++++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 3a1248e7686..206b8ead198 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -474,7 +474,7 @@ def fieldI(F0): 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 + 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 sage: from sage.schemes.curves.zariski_vankampen import fieldI @@ -524,9 +524,11 @@ def roots_interval(pols, x0): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import roots_interval + sage: from sage.schemes.curves.zariski_vankampen import roots_interval, fieldI # optional - sirocco sage: R. = QQ[] - sage: flist = (y^3 - x^2, ) + sage: K = fieldI(QQ) + sage: f = y^3 - x^2 + sage: flist = (f.change_ring(K), ) sage: ri = roots_interval(flist, 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, @@ -586,9 +588,11 @@ def roots_interval_cached(flist, 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 # optional - sirocco + 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) @@ -619,9 +623,11 @@ def populate_roots_interval_cache(inputs): 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 # optional - sirocco 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)]) @@ -667,9 +673,11 @@ def braid_in_segment(glist, x0, x1, precision = {}): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment, fieldI # optional - sirocco sage: R. = QQ[] + sage: K = fieldI(QQ) sage: f = x^2 + y^3 + 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) # optional - sirocco @@ -684,8 +692,10 @@ def braid_in_segment(glist, x0, x1, precision = {}): 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: from sage.schemes.curves import zariski_vankampen as zvk # optional - sirocco + sage: Kw1 = zvk.fieldI(Kw) sage: g = f.subs({x: x + 2 * y}) + sage: g = g.change_ring(Kw1) sage: p1 = QQbar(sqrt(- 1 / 3)) sage: p1a = CC(p1) sage: p1b=QQ(p1a.real())+I*QQ(p1a.imag()) From 47f036a2fdb72b0c0f1ae078aeadc80d5022d4e4 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 16 Feb 2023 00:32:37 +0100 Subject: [PATCH 048/122] too optional lines --- src/sage/schemes/curves/zariski_vankampen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 206b8ead198..75fcd7b2355 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -524,7 +524,7 @@ def roots_interval(pols, x0): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import roots_interval, fieldI # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import roots_interval, fieldI sage: R. = QQ[] sage: K = fieldI(QQ) sage: f = y^3 - x^2 @@ -588,7 +588,7 @@ def roots_interval_cached(flist, x0): TESTS:: - sage: from sage.schemes.curves.zariski_vankampen import roots_interval, roots_interval_cached, roots_interval_cache, fieldI # optional - sirocco + 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 @@ -623,7 +623,7 @@ def populate_roots_interval_cache(inputs): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import populate_roots_interval_cache, roots_interval_cache, fieldI # optional - sirocco + 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 @@ -673,7 +673,7 @@ def braid_in_segment(glist, x0, x1, precision = {}): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment, fieldI # 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 From 5f54caa3393c53746ede9a3bea0531869f25fb6b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 16 Feb 2023 08:01:55 +0100 Subject: [PATCH 049/122] more optional issues for testing --- src/sage/schemes/curves/zariski_vankampen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 75fcd7b2355..f79760ae479 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -693,9 +693,9 @@ def braid_in_segment(glist, x0, x1, precision = {}): 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 # optional - sirocco - sage: Kw1 = zvk.fieldI(Kw) + sage: Kw1 = zvk.fieldI(Kw) # optional - sirocco sage: g = f.subs({x: x + 2 * y}) - sage: g = g.change_ring(Kw1) + sage: g = g.change_ring(Kw1) # optional - sirocco sage: p1 = QQbar(sqrt(- 1 / 3)) sage: p1a = CC(p1) sage: p1b=QQ(p1a.real())+I*QQ(p1a.imag()) From d32000e4483c4db281cc34b4c3ef4abab2c518f2 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 27 Feb 2023 23:27:02 +0100 Subject: [PATCH 050/122] small corrections --- src/sage/schemes/curves/zariski_vankampen.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index f79760ae479..137d1302142 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -781,7 +781,7 @@ def orient_circuit(circuit, convex = False): OUTPUT: - The same circuit if it goes counterclockwise, and its reverse otherwise + The same circuit if it goes counterclockwise, and its reversed otherwise EXAMPLES:: @@ -792,7 +792,7 @@ def orient_circuit(circuit, convex = False): sage: for reg in V.regions().values(): ....: if reg.rays() or reg.lines(): ....: E = E.union(reg.vertex_graph()) - sage: E.vertices(sort=True) + sage: E.vertices(sort = True) [A vertex at (-2, -2), A vertex at (-2, 2), A vertex at (2, -2), @@ -1027,7 +1027,6 @@ def geometric_basis(G, E, EC, p, dual_graph): - def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -1118,8 +1117,8 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): e1 = (b, a, None) if e not in Edges: Edges += [e, e1] - crd[e]=(Vreg1[i],) - crd[e1]=(Vreg1[i],) + crd[e] = (Vreg1[i],) + crd[e1] = (Vreg1[i],) else: crd[e] += (Vreg1[i],) crd[e1] += (Vreg1[i],) From 1ee08dee961aa1ee1b0e3b8c044f11ba70b07152 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 18 Mar 2023 13:46:18 +0100 Subject: [PATCH 051/122] error doc --- src/sage/schemes/curves/affine_curve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 3733e25bc52..41d196ee811 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1741,7 +1741,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: From cf22a282840fbed98b442a41f0058d24bcb1038c Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 18 Mar 2023 14:04:38 +0100 Subject: [PATCH 052/122] Mejorar documentacion --- src/doc/en/reference/curves/index.rst | 1 + src/sage/schemes/curves/zariski_vankampen.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) 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/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 137d1302142..daaa75de237 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -430,7 +430,7 @@ def newton(f, x0, i0): INPUT: - - ``f``` -- a univariate polynomial + - ``f`` -- a univariate polynomial - ``x0`` -- a number - ``I0`` -- an interval @@ -457,15 +457,15 @@ def newton(f, x0, i0): def fieldI(F0): r""" - Return the (either double or trivial) extension of a number field which contains `I`. + Return the (either double or trivial) extension of a number field which contains ``I``. INPUT: - - ``F0`` -- a number field with an embedding in `QQbar`. + - ``F0`` -- a number field with an embedding in ``QQbar``. OUTPUT: - The extension `F` of `F0` containing `I` with an embedding in `QQbar`. + The extension ``F`` of ``F0`` containing ``I`` with an embedding in ``QQbar``. EXAMPLES:: @@ -619,7 +619,7 @@ def populate_roots_interval_cache(inputs): INPUT: - - ``inputs`` -- a list of tuples (flist, x0) + - ``inputs`` -- a list of tuples ``(flist, x0)`` EXAMPLES:: @@ -861,9 +861,9 @@ def geometric_basis(G, E, EC, p, dual_graph): list of vertices in the region and ``E``. 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:: @@ -1037,7 +1037,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): with an embedding in the complex numbers - ``arrangement`` -- an optional tuple of polynomials whose product equals ``f``, - in order to provide information for `braid_monodromy_arrangement`. + in order to provide information for ``braid_monodromy_arrangement``. - ``computebm`` -- an optional boolean variable (default ``True``). It is set to False, only the string assignment is given. t makes only sense if arrangement has @@ -1052,7 +1052,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): in the discriminant of the projection of ``f``. If ``arrangement`` contains more than one element, some information to be used by - `braid_monodromy_arrangement` is provided. + ``braid_monodromy_arrangement`` is provided. If ``computebm`` is set to ``False`` only some information to be used by ``strand_components`` is given. From c7feda28feb10f3e7b4e451f2d86e155bc8b4a4b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 18 Mar 2023 15:50:56 +0100 Subject: [PATCH 053/122] checking geometric_basis --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index daaa75de237..1a56fbe0dc1 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -944,7 +944,7 @@ def geometric_basis(G, E, EC, p, dual_graph): if G.size() == E.size(): if E.is_cycle(): return [EC] - InternalEdges = [_ for _ in G.edges(sort = False) if _ not in E.edges(sort = True)] + 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) if not Internal: From 11ee3d4f9f93b6469f0415abcdd46be8c563cf70 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 18 Mar 2023 17:56:09 +0100 Subject: [PATCH 054/122] adding examples --- src/sage/schemes/curves/zariski_vankampen.py | 148 +++++++++++-------- 1 file changed, 83 insertions(+), 65 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 1a56fbe0dc1..7c9f845b18e 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -947,16 +947,16 @@ def geometric_basis(G, E, EC, p, dual_graph): 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) - if not Internal: + if not Internal: # Creo que se puede quitar for v in E: if len(E.neighbors(v)) > 2: Internal.add_vertex(v) for i, ECi in enumerate(EC): # q and r are the points we will cut through - if EC[i] in Internal: + if ECi in Internal: q = EC[i] connecting_path = EC[:i] break - if EC[-i] in Internal: + if EC[-i] in Internal: # creo que sobra q = EC[-i] connecting_path = list(reversed(EC[-i:])) break @@ -975,10 +975,12 @@ def geometric_basis(G, E, EC, p, dual_graph): borra = [_ for _ in Gd.edges(sort = False) if _[2] in cp1] Gd.delete_edges(borra) Gd1, Gd2 = Gd.connected_components_subgraphs() - GL2=[v for r in Gd2.vertices(sort = True) for v in r[1]] + [v for e in Gd2.edges(sort = False) for v in e[2]] + [v for v in cutpath] - G2=G.subgraph(GL2) - GL1=[v for r in Gd1.vertices(sort = True) for v in r[1]] + [v for e in Gd1.edges(sort = False) for v in e[2]] + [v for v in cutpath] - G1=G.subgraph(GL1) + GL2 = [v for r in Gd2.vertices(sort = True) for v in r[1]] + GL2 += [v for e in Gd2.edges(sort = False) for v in e[2]] + [v for v in cutpath] + G2 = G.subgraph(GL2) + GL1 = [v for r in Gd1.vertices(sort = True) for v in r[1]] + GL1 += [v for e in Gd1.edges(sort = False) for v in e[2]] + [v for v in cutpath] + G1 = G.subgraph(GL1) if EC[qi + 1] in G2: G1, G2 = G2, G1 Gd1, Gd2 = Gd2, Gd1 @@ -1039,22 +1041,25 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): - ``arrangement`` -- an optional tuple of polynomials whose product equals ``f``, in order to provide information for ``braid_monodromy_arrangement``. - - ``computebm`` -- an optional boolean variable (default ``True``). It is set to False, only the - string assignment is given. t makes only sense if arrangement has - more than one polynomial - - - + - ``computebm`` -- an optional boolean variable (default ``True``) to actually + compute the braid monodromy + + - ``holdstrand`` -- an optional boolean variable (default ``False``) to stop + the computation of string assignment. + OUTPUT: - If ``arrangement`` contains only one element, a list of braids. The braids correspond to paths based in the same point; + If ``computebm`` is set to ``True`` and ``arrangement`` contains only one element, 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``. - If ``arrangement`` contains more than one element, some information to be used by - ``braid_monodromy_arrangement`` is provided. + If ``computebm`` is set to ``True`` and ``arrangement`` contains more than one element, + either assignment of the roots over the base point to elements of + ''arrangement`` (``holdstrand`` set to ``True``) or + the assignment of strands to elements of ``arrangement`` (``holdstrand`` set to ``True``). - If ``computebm`` is set to ``False`` only some information to be used by ``strand_components`` is given. + If ``computebm`` is set to ``False`` the second part of the above is given (depending on ``holdstrand``). .. NOTE:: @@ -1066,11 +1071,25 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): 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 + sage: bm = braid_monodromy(f); bm # 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: flist = (x^2 - y^3, x + 3 * y - 5) + sage: bm1 = braid_monodromy(f, arrangement=flist) # optional - sirocco + sage: bm1[0] == bm + True + sage: bm1[1] + {1: 1, 2: 2, 3: 1, 4: 1} + sage: braid_monodromy(f, arrangement=flist, computebm=False)==bm1[1] + True + sage: bm2 = braid_monodromy(f, arrangement=flist, holdstrand=True) + sage: bm2[0] == bm + True + sage: bm2[1] == braid_monodromy(f, arrangement=flist, computebm=False, holdstrand=True) + True + """ global roots_interval_cache if arrangement == (): @@ -1105,29 +1124,29 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): p0 = (p[0], p[1]) # Construct a dual graph for the compact regions; the dual edges # of this graph have as labels the corresponding edges in G - Vreg = [[e.vertices() + (None, ) for e in _.facets()] for _ in V0] - Vreg1 = {V0.index(_) : (V0.index(_), tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} - DG = Graph(len(Vreg1)) - DG.relabel(Vreg1) - Edges = [] - crd = {} - for i, r in enumerate(Vreg): - for e in r: - a, b = e[:2] - e1 = (b, a, None) - if e not in Edges: - Edges += [e, e1] - crd[e] = (Vreg1[i],) - crd[e1] = (Vreg1[i],) - else: - crd[e] += (Vreg1[i],) - crd[e1] += (Vreg1[i],) - EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] - for e in EdgesDual: - DG.add_edge(crd[e] + (e,)) - EC0 = orient_circuit(E.eulerian_circuit()) - EC = [EC0[0][0]] + [e[1] for e in EC0] if computebm: + Vreg = [[e.vertices() + (None, ) for e in _.facets()] for _ in V0] + Vreg1 = {V0.index(_) : (V0.index(_), tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + DG = Graph(len(Vreg1)) + DG.relabel(Vreg1) + Edges = [] + crd = {} + for i, r in enumerate(Vreg): + for e in r: + a, b = e[:2] + e1 = (b, a, None) + if e not in Edges: + Edges += [e, e1] + crd[e] = (Vreg1[i],) + crd[e1] = (Vreg1[i],) + else: + crd[e] += (Vreg1[i],) + crd[e1] += (Vreg1[i],) + EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] + for e in EdgesDual: + DG.add_edge(crd[e] + (e,)) + EC0 = orient_circuit(E.eulerian_circuit()) + EC = [EC0[0][0]] + [e[1] for e in EC0] geombasis = geometric_basis(G, E, EC, p, DG) segs = set() for p in geombasis: @@ -1164,26 +1183,27 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - if len(arrangement1) == 1: - return result - else: - p1 = p0[0] + I1 * p0[1] - strands = {} - roots_base = [] - for i, h in enumerate(arrangement1): - h0 = h.subs({x: p1}) - h1 = F[y](h0) - rt = h1.roots(QQbar, multiplicities = False) - roots_base += [(_,i) for _ in rt] - if not holdstrand: - roots_base.sort() - strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} - if computebm and not holdstrand: - return (result, strands) - elif computebm and holdstrand: - return (result,roots_base) - else: - return strands + if len(arrangement1) == 1: + return result + p1 = p0[0] + I1 * p0[1] + strands = {} + roots_base = [] + for i, h in enumerate(arrangement1): + h0 = h.subs({x: p1}) + h1 = F[y](h0) + rt = h1.roots(QQbar, multiplicities = False) + roots_base += [(_,i) for _ in rt] + if not holdstrand: + roots_base.sort() + strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} + if computebm and not holdstrand: + return (result, strands) + elif computebm and holdstrand: + return (result,roots_base) + elif not computebm and not holdstrand: + return strands + elif not computebm and holdstrand: + return roots_base @parallel def braid2rels(L, d): @@ -1398,7 +1418,7 @@ def relation(x, b): return G -def braid_monodromy_arrangement(flist, nodic = False): +def braid_monodromy_arrangement(flist): r""" Compute the braid monodromy of a projection of the curve defined by a list of polynomials with the extra information about the correspondence of strands @@ -1440,13 +1460,11 @@ def braid_monodromy_arrangement(flist, nodic = False): d = f.degree() dic ={j + 1 : 1 for j in range(d)} return (braid_monodromy(f), dic) - return braid_monodromy(f, arrangement = flist, holdstrand = nodic) + return braid_monodromy(f, arrangement = flist, holdstrand = False) def strand_components(flist): r""" - Compute the braid monodromy of a projection of the curve - defined by a tuple of polynomials with the extra information about the correspondence of strands - and elements of the list. + Compute only the assignment from strands to elements of ``flist``. INPUT: From 56d8ec6e99ed5ed2c6c7236af0366ccff59e124d Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 18 Mar 2023 18:27:42 +0100 Subject: [PATCH 055/122] doc typos --- src/sage/schemes/curves/zariski_vankampen.py | 43 +++++++++----------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 7c9f845b18e..3fa3d6e3143 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -199,8 +199,7 @@ def discrim(pols): def discrim_pairs(f, g): if f == g: return R(f.discriminant(y)) - else: - return R(f.resultant(g, y)) + return R(f.resultant(g, y)) pairs = [(f, f) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) @@ -475,8 +474,8 @@ def fieldI(F0): 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 - - + + sage: from sage.schemes.curves.zariski_vankampen import fieldI sage: p = QQ[x](x^4 + 1) sage: a0 = p.roots(QQbar, multiplicities = False)[0] @@ -776,7 +775,7 @@ def orient_circuit(circuit, convex = False): - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given by a list of edges - + - ``convex`` -- a boolean function, if set to ``True`` a simpler computation is made OUTPUT: @@ -844,16 +843,16 @@ def geometric_basis(G, E, EC, p, dual_graph): Return a geometric basis, based on a vertex. INPUT: - + - ``G`` -- a graph which correspond to the bounded edgess of a Voronoi Diagram - ``E`` -- a subgraph of ``G`` which is a cycle; it corresponds to the bounded edges touching an unbounded region of a Voronoi Diagram - + - ``EC`` -- 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 @@ -1027,8 +1026,6 @@ def geometric_basis(G, E, EC, p, dual_graph): return resul - - def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -1037,28 +1034,28 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): - ``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``, in order to provide information for ``braid_monodromy_arrangement``. - + - ``computebm`` -- an optional boolean variable (default ``True``) to actually compute the braid monodromy - + - ``holdstrand`` -- an optional boolean variable (default ``False``) to stop the computation of string assignment. - + OUTPUT: - If ``computebm`` is set to ``True`` and ``arrangement`` contains only one element, a list of braids. + If ``computebm`` is set to ``True`` and ``arrangement`` contains only one element, 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``. - - If ``computebm`` is set to ``True`` and ``arrangement`` contains more than one element, - either assignment of the roots over the base point to elements of - ''arrangement`` (``holdstrand`` set to ``True``) or + + If ``computebm`` is set to ``True`` and ``arrangement`` contains more than one element, + either assignment of the roots over the base point to elements of + ''arrangement`` (``holdstrand`` set to ``True``) or the assignment of strands to elements of ``arrangement`` (``holdstrand`` set to ``True``). - + If ``computebm`` is set to ``False`` the second part of the above is given (depending on ``holdstrand``). .. NOTE:: @@ -1089,7 +1086,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): True sage: bm2[1] == braid_monodromy(f, arrangement=flist, computebm=False, holdstrand=True) True - + """ global roots_interval_cache if arrangement == (): @@ -1316,7 +1313,7 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, 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``. - + - ``braidmonodromy`` -- (default: ``None``); if it is set to a list of braids braid monodromy is not computed ad this list is used instead. @@ -1360,7 +1357,7 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, sage: f = y^3 + x^3 + zeta * x + 1 sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > - + We compute the fundamental group of the complement of a quartic with ``Puiseux = True``:: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco From b650801719f3e87ca2121647b121c91f386dd483 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 18 Mar 2023 18:29:31 +0100 Subject: [PATCH 056/122] doc typos --- src/sage/schemes/curves/zariski_vankampen.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 3fa3d6e3143..bacd01318ed 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1468,16 +1468,10 @@ def strand_components(flist): - ``flist`` -- a list of polynomial with two variables, over a number field with an embedding in the complex numbers - OUTPUT: - A dictionary attaching a number `i` (strand) to a number `j` (a polynomial in the list). - .. NOTE:: - - The projection over the `x` axis is used if there are no vertical asymptotes. - Otherwise, a linear change of variables is done to fall into the previous case. - EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import strand_components # optional - sirocco From beab5ca720a97c6e5aa2b0bb53ac12e739c5bf30 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 18 Mar 2023 21:56:35 +0100 Subject: [PATCH 057/122] forgot optional sirocco --- src/sage/schemes/curves/zariski_vankampen.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index bacd01318ed..4ca7ecd7578 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1075,16 +1075,16 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): s1*s0*s2*s0^-1*s2*s1^-1] sage: flist = (x^2 - y^3, x + 3 * y - 5) sage: bm1 = braid_monodromy(f, arrangement=flist) # optional - sirocco - sage: bm1[0] == bm + sage: bm1[0] == bm # optional - sirocco True - sage: bm1[1] + sage: bm1[1] # optional - sirocco {1: 1, 2: 2, 3: 1, 4: 1} - sage: braid_monodromy(f, arrangement=flist, computebm=False)==bm1[1] + sage: braid_monodromy(f, arrangement=flist, computebm=False)==bm1[1] # optional - sirocco True - sage: bm2 = braid_monodromy(f, arrangement=flist, holdstrand=True) - sage: bm2[0] == bm + sage: bm2 = braid_monodromy(f, arrangement=flist, holdstrand=True) # optional - sirocco + sage: bm2[0] == bm # optional - sirocco True - sage: bm2[1] == braid_monodromy(f, arrangement=flist, computebm=False, holdstrand=True) + sage: bm2[1] == braid_monodromy(f, arrangement=flist, computebm=False, holdstrand=True) # optional - sirocco True """ From 439b6631c0afa60a8243a25a4a2350667b644cd3 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 19 Mar 2023 12:20:12 +0100 Subject: [PATCH 058/122] new aux function voronoi_cells to simplify braid_monodromy --- src/sage/schemes/curves/zariski_vankampen.py | 150 ++++++++++++++----- 1 file changed, 112 insertions(+), 38 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 4ca7ecd7578..416e76ffa3e 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -270,6 +270,113 @@ def corrected_voronoi_diagram(points): break return V +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: + + - ``V`` -- a corrected Voronoi diagram + + OUTPUT: + + The graph of the 1-skeleton of ``G``, the subgraph ``E`` of the boundary, a vertex `p` in ``E``, + a counterclockwise orientation ``EC`` of ``E`` (as an ordered list of vertices with identical + first and last elements), and the dual graph ``DG`` 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 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 (2000001/2000000, -24500001/7000000), + A vertex at (2000001/2000000, 500001/1000000), + A vertex at (2000001/2000000, -24500001/7000000), + A vertex at (11/4, 4), + A vertex at (2000001/2000000, 500001/1000000), + A vertex at (9/2, 9/2), + A vertex at (11/4, 4), + A vertex at (9/2, -9/2), + A vertex at (9/2, 9/2))), + (1, + (A vertex at (11/4, 4), + A vertex at (2000001/2000000, 500001/1000000), + A vertex at (1000001/2000000, 1000001/2000000), + A vertex at (2000001/2000000, 500001/1000000), + A vertex at (-49000001/14000000, 1000001/2000000), + A vertex at (1000001/2000000, 1000001/2000000), + A vertex at (-4, 4), + A vertex at (11/4, 4), + A vertex at (-49000001/14000000, 1000001/2000000), + A vertex at (-4, 4))), + (A vertex at (2000001/2000000, 500001/1000000), A vertex at (11/4, 4), None)) + sage: list(Ge).index(edg[-1]) + 13 +""" + V0 = [_ for _ in V.regions().values() if _.is_compact()] + Vnc = [_ for _ in V.regions().values() if not _.is_compact()] + G = G=Graph([u.vertices() for v in V0 for u in v.faces(1)], format = 'list_of_edges') + E=Graph([u.vertices() for v in Vnc for u in v.faces(1) if u.is_compact()], format = 'list_of_edges') + p = next(E.vertex_iterator()) + EC0 = orient_circuit(E.eulerian_circuit()) + EC = [EC0[0][0]] + [e[1] for e in EC0] + Vreg1 = {V0.index(_) : (V0.index(_), tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} + DG = Graph(len(V0)) + DG.relabel(Vreg1) + Edges = [] + crd = {} + for i, v in enumerate(V0): + r = [e.vertices() + (None, ) for e in v.facets()] + for e in r: + a, b = e[:2] + e1 = (b, a, None) + if e not in Edges: + Edges += [e, e1] + crd[e] = (Vreg1[i],) + crd[e1] = (Vreg1[i],) + else: + crd[e] += (Vreg1[i],) + crd[e1] += (Vreg1[i],) + EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] + for e in EdgesDual: + DG.add_edge(crd[e] + (e,)) + return (G, E, p, EC, DG) + def segments(points): """ @@ -1108,42 +1215,9 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): glist = tuple(f1.subs({x: x + y}) for f1 in glist) disc = discrim(glist) V = corrected_voronoi_diagram(tuple(disc)) - V0 = [_ for _ in V.regions().values() if _.is_compact()] - G = Graph() - for reg in V0: - regv = reg.vertex_graph() - G = G.union(regv) - 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()) + G, E, p, EC, DG = voronoi_cells(V) p0 = (p[0], p[1]) - # Construct a dual graph for the compact regions; the dual edges - # of this graph have as labels the corresponding edges in G if computebm: - Vreg = [[e.vertices() + (None, ) for e in _.facets()] for _ in V0] - Vreg1 = {V0.index(_) : (V0.index(_), tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} - DG = Graph(len(Vreg1)) - DG.relabel(Vreg1) - Edges = [] - crd = {} - for i, r in enumerate(Vreg): - for e in r: - a, b = e[:2] - e1 = (b, a, None) - if e not in Edges: - Edges += [e, e1] - crd[e] = (Vreg1[i],) - crd[e1] = (Vreg1[i],) - else: - crd[e] += (Vreg1[i],) - crd[e1] += (Vreg1[i],) - EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] - for e in EdgesDual: - DG.add_edge(crd[e] + (e,)) - EC0 = orient_circuit(E.eulerian_circuit()) - EC = [EC0[0][0]] + [e[1] for e in EC0] geombasis = geometric_basis(G, E, EC, p, DG) segs = set() for p in geombasis: @@ -1364,9 +1438,9 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, 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); print (g) # optional - sirocco - Finitely presented group < x0, x1, x2, x3 | x3^-1*x2^-1*x0*x2, x1*x2*x1^-1*x0^-1, x0*x2^-1*x1^-1*x2, x3*x1^-1 > + Finitely presented group < x0, x1, x2, x3 | x3*x2^-1*x1^-1*x2, x3*x2^-1*x1^-1*x0^-1*x1*x2^-1*x1^-1*x0*x1*x2, x0^-1*x2, x1*x2*x1*x2^-1*x1^-1*x0^-1 > sage: g.simplified() # optional - sirocco - Finitely presented group < x0, x1 | x1^-1*x0^2*x1^-1, (x1*x0^-1)^3 > + Finitely presented group < x0, x1 | x0^-1*x1^2*x0^-1, (x1^-1*x0)^3 > sage: g = fundamental_group(f, puiseux = True, projective = True); print (g.order(), g.abelian_invariants()) # optional - sirocco 12 (4,) @@ -1376,9 +1450,9 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, sage: R. = QQ[] sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) sage: bm = braid_monodromy(f); print(bm) # optional - sirocco - [s0^-1*s1*s2*s0*s1*s0^-1*s1^-1, (s1*s0)^2, s0^-1*s1^-1*s0^-1*s1*s0*s2*s1] + [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(f, projective = True, braidmonodromy = bm); print (g) # optional - sirocco - Finitely presented group < x0, x2 | x0^2*x2^2, x0^2*x2^-2, x2*(x0^-1*x2^-1)^2*x0^-1 > + 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()) # optional - sirocco 12 (4,) """ From 7a07c0898a0f15c7fdc45f84418fa2a7e88099c1 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 19 Mar 2023 15:54:30 +0100 Subject: [PATCH 059/122] more examples affine curve --- src/sage/schemes/curves/affine_curve.py | 31 +++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 41d196ee811..369efdebaac 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1696,6 +1696,28 @@ def fundamental_group(self, puiseux = False, braidmonodromy = None): Return a presentation of the fundamental group of the complement of ``self``. + INPUT: + + - ``puiseux`` -- (default: ``False``) boolean to decide if the + presentation is constructed in the classical way or using Puiseux + shortcut. + + - ``braidmonodromy`` -- (default: ``False``) If the value is ``False`` + it apply first the ``braid_monodromy`` method. If it has been already + computed it can be passed as a parameter. + + 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 + sage: bm = C.braid_monodromy() # optional - sirocco + sage: C.fundamental_group(braidmonodromy=bm) # optional - sirocco + Finitely presented group < x0 | > + sage: g = C.fundamental_group(puiseux=True); g # optional - sirocco + Finitely presented group < x0, x1 | x1*x0^-1, x1*x0*x1^-1*x0^-1 > + sage: g.simplified() # optional - sirocco + Finitely presented group < x0 | > + sage: g == C.fundamental_group(puiseux=True, braidmonodromy=bm) # optional - sirocco + True In the case of number fields, they need to have an embedding to the algebraic field:: From 7ee7f66bba756574b93d482ebcc98a8871efdc8d Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 19 Mar 2023 16:07:08 +0100 Subject: [PATCH 060/122] . --- src/sage/schemes/curves/affine_curve.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 369efdebaac..123d08082a4 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1732,7 +1732,8 @@ def fundamental_group(self, puiseux = False, braidmonodromy = None): sage: bm = C.braid_monodromy() # optional - sirocco sage: C.fundamental_group(braidmonodromy=bm) # optional - sirocco Finitely presented group < x0 | > - sage: g = C.fundamental_group(puiseux=True); g # optional - sirocco + sage: g = C.fundamental_group(puiseux=True) # optional - sirocco + sage: g # optional - sirocco Finitely presented group < x0, x1 | x1*x0^-1, x1*x0*x1^-1*x0^-1 > sage: g.simplified() # optional - sirocco Finitely presented group < x0 | > From a763faf3120e708bbc2ef4d239b785c9290447ee Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 19 Mar 2023 16:59:43 +0100 Subject: [PATCH 061/122] doc --- src/sage/schemes/curves/zariski_vankampen.py | 60 +++++++------------- 1 file changed, 19 insertions(+), 41 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 416e76ffa3e..1b62dd2af79 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -49,6 +49,7 @@ 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.misc.cachefunc import cached_function @@ -582,6 +583,7 @@ def fieldI(F0): 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 + Another example where ``F`` and ``F0`` coincide. sage: from sage.schemes.curves.zariski_vankampen import fieldI sage: p = QQ[x](x^4 + 1) @@ -887,7 +889,8 @@ def orient_circuit(circuit, convex = False): OUTPUT: - The same circuit if it goes counterclockwise, and its reversed otherwise + The same circuit if it goes counterclockwise, and its reversed otherwise, given as + the ordered list of vertices with identic extremities EXAMPLES:: @@ -973,41 +976,10 @@ def geometric_basis(G, E, EC, p, dual_graph): EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, orient_circuit + 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: V0 = [_ for _ in V.regions().values() if _.is_compact()] - sage: G = Graph() - sage: for reg in V0: - ....: regv = reg.vertex_graph() - ....: G = G.union(regv) - 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: Vreg = [[e.vertices()+(None,) for e in _.facets()] for _ in V0] - sage: Vreg1 = {V0.index(_):(V0.index(_),tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} - sage: DG = Graph(len(Vreg1)) - sage: DG.relabel(Vreg1) - sage: Edges = [] - sage: crd = {} - sage: for i, r in enumerate(Vreg): - ....: for e in r: - ....: a, b = e[ : 2] - ....: e1 = (b, a, None) - ....: if e not in Edges: - ....: Edges += [e, e1] - ....: crd[e]=(Vreg1[i],) - ....: crd[e1]=(Vreg1[i],) - ....: else: - ....: crd[e] += (Vreg1[i],) - ....: crd[e1] += (Vreg1[i],) - sage: EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] - sage: for e in EdgesDual: - ....: DG.add_edge(crd[e] + (e,)) - sage: EC0 = orient_circuit(E.eulerian_circuit()) - sage: EC = [EC0[0][0]] + [e[1] for e in EC0] + 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), @@ -1300,7 +1272,6 @@ def braid2rels(L, d): sage: braid2rels(L, 4) # optional - sirocco [(4, 1, -2, -1), (2, -4, -2, 1)] """ - from sage.groups.finitely_presented import wrap_FpGroup B = BraidGroup(d) F = FreeGroup(d) L1 = copy(L) @@ -1340,7 +1311,7 @@ def braid2rels(L, d): else: b = prod(A1[:-1]) b1 = len(b.Tietze()) / len(A1) - par = (A1[-1].exponent_sum() / d / (d - 1) * 2)%2 + par = (A1[-1].exponent_sum() / d / (d - 1) * 2) % 2 if res is None or b1 < res[3]: res = [tau, A1[:-1], par, b1] if res[2] == 1: @@ -1384,6 +1355,7 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, 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``. @@ -1401,13 +1373,19 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy # optional - sirocco sage: R. = QQ[] sage: f = x^2 + y^3 sage: fundamental_group(f) # optional - sirocco - Finitely presented group < ... > + Finitely presented group < x1, x2 | x1*x2*x1^-1*x2^-1*x1^-1*x2 > sage: fundamental_group(f, simplified=False) # optional - sirocco - Finitely presented group < ... > + Finitely presented group < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-1*x1^-1, x0*x1*x0^-1*x2^-1 > + sage: fundamental_group(f, projective=True) # optional - sirocco + Finitely presented group < x0 | x0^3 > + sage: bm = braid_monodromy(f); bm # optional - sirocco + [(s1*s0)^2] + sage: fundamental_group(f, braidmonodromy=bm) # optional - sirocco + Finitely presented group < x1, x2 | x1*x2*x1^-1*x2^-1*x1^-1*x2 > :: @@ -1415,7 +1393,7 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, sage: R. = QQ[] sage: f = y^3 + x^3 sage: fundamental_group(f) # optional - sirocco - Finitely presented group < ... > + 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`:: @@ -1432,7 +1410,7 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > - We compute the fundamental group of the complement of a quartic with ``Puiseux = True``:: + We compute the fundamental group of the complement of a quartic with ``True`` for ``Puiseux``:: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco sage: R. = QQ[] From 2f740397b6935ff6994629afb21e71634febe058 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 20 Mar 2023 12:29:58 +0100 Subject: [PATCH 062/122] fundamental_group_arrangement --- src/sage/schemes/curves/zariski_vankampen.py | 86 +++++++++++++------- 1 file changed, 58 insertions(+), 28 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 1b62dd2af79..73e08e868dc 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -209,7 +209,7 @@ def discrim_pairs(f, g): for u in fdiscrim: h0 = u[1].radical() h1 = h0 // h0.gcd(poly) - rts += h1.roots(QQbar, multiplicities = False) + rts += h1.roots(QQbar, multiplicities=False) poly = poly * h1 return rts @@ -578,8 +578,8 @@ def fieldI(F0): 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: 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 @@ -587,8 +587,8 @@ def fieldI(F0): 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: a0 = p.roots(QQbar, multiplicities=False)[0] + sage: F0. = NumberField(p, embedding=a0) sage: F1 = fieldI(F0) sage: F0 == F1 True @@ -606,7 +606,7 @@ def fieldI(F0): b1 = h1(b0) b2 = h1(F1a(F0a.gen(0))) b3 = F0.gen(0) - F1 = NumberField(q, 'b', embedding = b1) + F1 = NumberField(q, 'b', embedding=b1) if b3 in F1 and b2.imag()>0: return F1 @@ -763,7 +763,7 @@ def populate_roots_interval_cache(inputs): @parallel -def braid_in_segment(glist, x0, x1, precision = {}): +def braid_in_segment(glist, x0, x1, precision={}): """ Return the braid formed by the `y` roots of ``f`` when `x` moves from ``x0`` to ``x1``. @@ -796,7 +796,7 @@ def braid_in_segment(glist, x0, x1, precision = {}): Check that :trac:`26503` is fixed:: sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0] - sage: Kw. = NumberField(wp.minpoly(), embedding = wp) + 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) @@ -830,7 +830,7 @@ def braid_in_segment(glist, x0, x1, precision = {}): f0 = F1[y](f) else: f0 = F1[y](f.subs({x: X0})) - y0sf = f0.roots(QQbar, multiplicities = False) + y0sf = f0.roots(QQbar, multiplicities=False) y0s += list(y0sf) while True: CIFp = ComplexIntervalField(precision[f]) @@ -860,7 +860,7 @@ def braid_in_segment(glist, x0, x1, precision = {}): matched += 1 if matched != 1: precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision = precision) # new + return braid_in_segment(glist, x0, x1, precision=precision) # new matched = 0 for center, interval in finalintervals.items(): @@ -869,14 +869,14 @@ def braid_in_segment(glist, x0, x1, precision = {}): matched += 1 if matched != 1: precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision = precision) # new + return braid_in_segment(glist, x0, x1, precision=precision) # new initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) return initialbraid * centralbraid * finalbraid -def orient_circuit(circuit, convex = False): +def orient_circuit(circuit, convex=False): r""" Reverse a circuit if it goes clockwise; otherwise leave it unchanged. @@ -901,7 +901,7 @@ def orient_circuit(circuit, convex = False): sage: for reg in V.regions().values(): ....: if reg.rays() or reg.lines(): ....: E = E.union(reg.vertex_graph()) - sage: E.vertices(sort = True) + sage: E.vertices(sort=True) [A vertex at (-2, -2), A vertex at (-2, 2), A vertex at (2, -2), @@ -1022,9 +1022,9 @@ def geometric_basis(G, E, EC, p, dual_graph): if G.size() == E.size(): if E.is_cycle(): return [EC] - InternalEdges = [_ for _ in G.edges(sort = True) if _ not in E.edges(sort = True)] + 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) + Internal = G.subgraph(vertices=InternalVertices, edges=InternalEdges) if not Internal: # Creo que se puede quitar for v in E: if len(E.neighbors(v)) > 2: @@ -1050,14 +1050,14 @@ def geometric_basis(G, E, EC, p, dual_graph): Gd = copy(dual_graph) cp1 = [(e, cutpath[i + 1], None) for i, e in enumerate(cutpath[: -1])] cp1 += [(cutpath[i + 1], e, None) for i, e in enumerate(cutpath[: -1])] - borra = [_ for _ in Gd.edges(sort = False) if _[2] in cp1] + borra = [_ for _ in Gd.edges(sort=True) if _[2] in cp1] Gd.delete_edges(borra) Gd1, Gd2 = Gd.connected_components_subgraphs() - GL2 = [v for r in Gd2.vertices(sort = True) for v in r[1]] - GL2 += [v for e in Gd2.edges(sort = False) for v in e[2]] + [v for v in cutpath] + GL2 = [v for r in Gd2.vertices(sort=True) for v in r[1]] + GL2 += [v for e in Gd2.edges(sort=True) for v in e[2]] + [v for v in cutpath] G2 = G.subgraph(GL2) - GL1 = [v for r in Gd1.vertices(sort = True) for v in r[1]] - GL1 += [v for e in Gd1.edges(sort = False) for v in e[2]] + [v for v in cutpath] + GL1 = [v for r in Gd1.vertices(sort=True) for v in r[1]] + GL1 += [v for e in Gd1.edges(sort=True) for v in e[2]] + [v for v in cutpath] G1 = G.subgraph(GL1) if EC[qi + 1] in G2: G1, G2 = G2, G1 @@ -1105,7 +1105,7 @@ def geometric_basis(G, E, EC, p, dual_graph): return resul -def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): +def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -1234,7 +1234,7 @@ def braid_monodromy(f, arrangement = (), computebm = True, holdstrand = False): for i, h in enumerate(arrangement1): h0 = h.subs({x: p1}) h1 = F[y](h0) - rt = h1.roots(QQbar, multiplicities = False) + rt = h1.roots(QQbar, multiplicities=False) roots_base += [(_,i) for _ in rt] if not holdstrand: roots_base.sort() @@ -1336,7 +1336,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in gb.relations()] return U -def fundamental_group(f, simplified = True, projective = False, puiseux = False, braidmonodromy = None): +def fundamental_group(f, simplified=True, projective=False, puiseux=False, braidmonodromy=None): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. @@ -1415,11 +1415,11 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco 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); print (g) # optional - sirocco + sage: g = fundamental_group(f, puiseux=True); print (g) # optional - sirocco Finitely presented group < x0, x1, x2, x3 | x3*x2^-1*x1^-1*x2, x3*x2^-1*x1^-1*x0^-1*x1*x2^-1*x1^-1*x0*x1*x2, x0^-1*x2, x1*x2*x1*x2^-1*x1^-1*x0^-1 > sage: g.simplified() # optional - sirocco Finitely presented group < x0, x1 | x0^-1*x1^2*x0^-1, (x1^-1*x0)^3 > - sage: g = fundamental_group(f, puiseux = True, projective = True); print (g.order(), g.abelian_invariants()) # optional - sirocco + sage: g = fundamental_group(f, puiseux=True, projective=True); print (g.order(), g.abelian_invariants()) # optional - sirocco 12 (4,) We compute first a braid monodromy and use it for the computation of the fundamental group:: @@ -1429,7 +1429,7 @@ def fundamental_group(f, simplified = True, projective = False, puiseux = False, sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) sage: bm = braid_monodromy(f); print(bm) # optional - sirocco [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(f, projective = True, braidmonodromy = bm); print (g) # optional - sirocco + sage: g = fundamental_group(f, projective=True, braidmonodromy=bm); print (g) # optional - sirocco 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()) # optional - sirocco 12 (4,) @@ -1509,7 +1509,37 @@ def braid_monodromy_arrangement(flist): d = f.degree() dic ={j + 1 : 1 for j in range(d)} return (braid_monodromy(f), dic) - return braid_monodromy(f, arrangement = flist, holdstrand = False) + return braid_monodromy(f, arrangement=flist, holdstrand=False) + +def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, braidmonodromy=None): + f = prod(flist) + if braidmonodromy is None: + bm, dic = braid_monodromy_arrangement(flist) + else: + bm = braidmonodromy + g = fundamental_group(f, simplified=False, projective=projective, puiseux=puiseux, braidmonodromy=bm) + if not simplified: + return (g, dic) + hom = g.simplification_isomorphism() + g1 = hom.codomain() + dic1 = {} + for j in range(g.ngens()): + x = hom(g.gen(j)).Tietze() + if len(x) == 1: + i = x[0] + if i not in dic1.keys(): + dic1[i] = dic[j + 1] + if projective: + Lg = [j+1 for j in range(len(flist)) if j+1 not in dic1.keys()] + if len(Lg) == 1: + j = Lg[0] + i = [i + 1 for i in range(g.ngens()) if dic[i + 1] == j][0] + t = hom(g([i])).Tietze() + dic1[t] = j + n = g1.ngens() + rels = [_.Tietze() for _ in g1.relations()] + g1 = FreeGroup(n) / rels + return (g1, dic1) def strand_components(flist): r""" @@ -1537,4 +1567,4 @@ def strand_components(flist): d = f.degree() dic ={j + 1 : 1 for j in range(d)} return dic - return braid_monodromy(f, arrangement = flist, computebm = False) + return braid_monodromy(f, arrangement=flist, computebm=False) From 8b89c422214f060ab77bf5ed3ae7bf6a21f7dec3 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 20 Mar 2023 15:27:53 +0100 Subject: [PATCH 063/122] examples for fundamental_group_arrangement --- src/sage/schemes/curves/zariski_vankampen.py | 68 +++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 73e08e868dc..8cae2e515bc 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1485,7 +1485,7 @@ def braid_monodromy_arrangement(flist): 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 number `i` (strand) to a number `j` (a polynomial in the list). + - A dictionary attaching a number ``i`` (strand) to a number ``j`` (a polynomial in the list). .. NOTE:: @@ -1512,14 +1512,73 @@ def braid_monodromy_arrangement(flist): return braid_monodromy(f, arrangement=flist, holdstrand=False) def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, braidmonodromy=None): + 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``. + + - ``braidmonodromy`` -- (default: ``None``); it can be set to the output + of ``braid_monodromy_arrangement`` to avoid an extra computation. + + 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: from sage.schemes.curves.zariski_vankampen import braid_monodromy_arrangement, fundamental_group_arrangement # optional - sirocco + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3 * y - 5] + sage: g, dic = fundamental_group_arrangement(flist) # optional - sirocco + sage: g # optional - sirocco + Finitely presented group < x0, x1, x2 | x2*x1*x2^-1*x1^-1, x1*x0*x1^-1*x0^-1, x2*x0*x2*x0^-1*x2^-1*x0^-1 > + sage: dic # optional - sirocco + {(-1, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1} + sage: BM = braid_monodromy_arrangement(flist) + sage: (g, dic) == fundamental_group_arrangement(flist, braidmonodromy=BM) + True + sage: fundamental_group_arrangement(flist, simplified=False, braidmonodromy=BM) + (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {(-4, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1, 4: 1}) + sage: fundamental_group_arrangement(flist, projective=True, braidmonodromy=BM) + (Finitely presented group < x | >, {(-1, -1, -1): 2, 1: 1}) + """ f = prod(flist) if braidmonodromy is None: bm, dic = braid_monodromy_arrangement(flist) else: - bm = braidmonodromy + bm, dic = braidmonodromy g = fundamental_group(f, simplified=False, projective=projective, puiseux=puiseux, braidmonodromy=bm) - if not simplified: + if not simplified and projective: return (g, dic) + elif not simplified: + a = tuple(-i - 1 for i in reversed(range(g.ngens()))) + dic[a] = len(flist) + 1 + return (g,dic) hom = g.simplification_isomorphism() g1 = hom.codomain() dic1 = {} @@ -1536,6 +1595,9 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis i = [i + 1 for i in range(g.ngens()) if dic[i + 1] == j][0] t = hom(g([i])).Tietze() dic1[t] = j + else: + t = prod(hom(x) for x in g.gens()).inverse().Tietze() + dic1[t] = len(flist) + 1 n = g1.ngens() rels = [_.Tietze() for _ in g1.relations()] g1 = FreeGroup(n) / rels From 08ab5797e761fb6232c356b7d8efe8dd84c65c40 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 20 Mar 2023 19:08:11 +0100 Subject: [PATCH 064/122] adding optional sirocco to some examples --- src/sage/schemes/curves/zariski_vankampen.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 8cae2e515bc..5d4ba069227 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1559,12 +1559,12 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis Finitely presented group < x0, x1, x2 | x2*x1*x2^-1*x1^-1, x1*x0*x1^-1*x0^-1, x2*x0*x2*x0^-1*x2^-1*x0^-1 > sage: dic # optional - sirocco {(-1, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1} - sage: BM = braid_monodromy_arrangement(flist) - sage: (g, dic) == fundamental_group_arrangement(flist, braidmonodromy=BM) + sage: BM = braid_monodromy_arrangement(flist) # optional - sirocco + sage: (g, dic) == fundamental_group_arrangement(flist, braidmonodromy=BM) # optional - sirocco True - sage: fundamental_group_arrangement(flist, simplified=False, braidmonodromy=BM) + sage: fundamental_group_arrangement(flist, simplified=False, braidmonodromy=BM) # optional - sirocco (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {(-4, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1, 4: 1}) - sage: fundamental_group_arrangement(flist, projective=True, braidmonodromy=BM) + sage: fundamental_group_arrangement(flist, projective=True, braidmonodromy=BM) # optional - sirocco (Finitely presented group < x | >, {(-1, -1, -1): 2, 1: 1}) """ f = prod(flist) From 587afdb49b4ded89d440af3009f270db10f2bbd3 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 21 Mar 2023 09:27:31 +0100 Subject: [PATCH 065/122] style --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 5d4ba069227..62a146d6757 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -607,7 +607,7 @@ def fieldI(F0): b2 = h1(F1a(F0a.gen(0))) b3 = F0.gen(0) F1 = NumberField(q, 'b', embedding=b1) - if b3 in F1 and b2.imag()>0: + if b3 in F1 and b2.imag() > 0: return F1 From 270ef0ad50e53d50a72c165c41aa2c9e4bfb17fd Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 26 Mar 2023 20:06:33 +0200 Subject: [PATCH 066/122] avoid import from all.py --- src/sage/schemes/curves/zariski_vankampen.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 62a146d6757..82e5db76f15 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -56,14 +56,14 @@ from sage.misc.flatten import flatten from sage.misc.misc_c import prod from sage.parallel.decorate import parallel -from sage.rings.all import NumberField +from sage.rings.number_field.number_field import NumberField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.complex_mpfr import ComplexField from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField -from sage.matrix.all import matrix -from sage.rings.all import PolynomialRing +from sage.matrix.constructor import matrix +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing roots_interval_cache = {} From a4813d46ef2f8ae39cf410af17c721d54d400de0 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 27 Mar 2023 13:46:37 +0200 Subject: [PATCH 067/122] fix braid2rels --- src/sage/schemes/curves/zariski_vankampen.py | 95 +++++--------------- 1 file changed, 22 insertions(+), 73 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 82e5db76f15..d9349c1a923 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -64,6 +64,7 @@ from sage.rings.real_mpfr import RealField from sage.matrix.constructor import matrix from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.libs.braiding import rightnormalform roots_interval_cache = {} @@ -183,7 +184,7 @@ def discrim(pols): sage: from sage.schemes.curves.zariski_vankampen import discrim sage: R. = QQ[] sage: flist = (y^3 + x^3 - 1, 2 * x + y) - sage: discrim(flist) # optional - sirocco + sage: discrim(flist) [1, -0.500000000000000? - 0.866025403784439?*I, -0.500000000000000? + 0.866025403784439?*I, @@ -282,7 +283,7 @@ def voronoi_cells(V): OUTPUT: - The graph of the 1-skeleton of ``G``, the subgraph ``E`` of the boundary, a vertex `p` in ``E``, + The graph of the 1-skeleton of ``G``, the subgraph ``E`` of the boundary, a vertex ``p`` in ``E``, a counterclockwise orientation ``EC`` of ``E`` (as an ordered list of vertices with identical first and last elements), and the dual graph ``DG`` of ``V``, where the vertices are labelled by the compact regions of ``V`` and the edges by their dual edges. @@ -351,8 +352,8 @@ def voronoi_cells(V): """ V0 = [_ for _ in V.regions().values() if _.is_compact()] Vnc = [_ for _ in V.regions().values() if not _.is_compact()] - G = G=Graph([u.vertices() for v in V0 for u in v.faces(1)], format = 'list_of_edges') - E=Graph([u.vertices() for v in Vnc for u in v.faces(1) if u.is_compact()], format = 'list_of_edges') + G = Graph([u.vertices() for v in V0 for u in v.faces(1)], format = 'list_of_edges') + E = Graph([u.vertices() for v in Vnc for u in v.faces(1) if u.is_compact()], format = 'list_of_edges') p = next(E.vertex_iterator()) EC0 = orient_circuit(E.eulerian_circuit()) EC = [EC0[0][0]] + [e[1] for e in EC0] @@ -379,57 +380,6 @@ def voronoi_cells(V): return (G, E, p, EC, DG) -def segments(points): - """ - Return the bounded segments of the Voronoi diagram of the given points. - - INPUT: - - - ``points`` -- a list of complex points - - OUTPUT: - - A list of pairs ``(p1, p2)``, where ``p1`` and ``p2`` are the - endpoints of the segments in the Voronoi diagram. - - EXAMPLES:: - - sage: from sage.schemes.curves.zariski_vankampen import discrim, segments - sage: R. = QQ[] - sage: f = y^3 + x^3 - 1 - sage: disc = discrim([f]) - sage: sorted(segments(disc)) - [(-192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316, - -192951821525958031/90044183378780414), - (-192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316, - -144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), - (192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316, - 144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), - (-192951821525958031/90044183378780414, - 192951821525958031/67764026159052316*I - 192951821525958031/67764026159052316), - (-192951821525958031/90044183378780414, 1/38590364305191606), - (1/38590364305191606, - -144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), - (1/38590364305191606, - 144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), - (-5/2*I + 5/2, - -144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326), - (-5/2*I + 5/2, 5/2*I + 5/2), - (5/2*I + 5/2, - 144713866144468523/66040650000519163*I + 167101179147960739/132081300001038326)] - """ - V = corrected_voronoi_diagram(tuple(points)) - res = set() - for region in V.regions().values(): - if region.rays(): - continue - 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] - def followstrand(f, factors, x0, x1, y0a, prec=53): r""" @@ -948,6 +898,7 @@ def orient_circuit(circuit, convex=False): return circuit prec *= 2 + def geometric_basis(G, E, EC, p, dual_graph): r""" Return a geometric basis, based on a vertex. @@ -1023,7 +974,7 @@ def geometric_basis(G, E, EC, p, dual_graph): if E.is_cycle(): return [EC] 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]] + InternalVertices = [v for e in InternalEdges for v in e[:2]] Internal = G.subgraph(vertices=InternalVertices, edges=InternalEdges) if not Internal: # Creo que se puede quitar for v in E: @@ -1031,7 +982,7 @@ def geometric_basis(G, E, EC, p, dual_graph): Internal.add_vertex(v) for i, ECi in enumerate(EC): # q and r are the points we will cut through if ECi in Internal: - q = EC[i] + q = ECi connecting_path = EC[:i] break if EC[-i] in Internal: # creo que sobra @@ -1066,7 +1017,6 @@ def geometric_basis(G, E, EC, p, dual_graph): if EC[qi + 1] in E2: E1, E2 = E2, E1 - 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) @@ -1084,8 +1034,6 @@ def geometric_basis(G, E, EC, p, dual_graph): EC1 = EC[qi : ] + EC[1 : ri] + [_ for _ in reversed(cutpath)] EC2 = cutpath + EC[ri + 1 : qi + 1] - - gb1 = geometric_basis(G1, E1, EC1, q, Gd1) gb2 = geometric_basis(G2, E2, EC2, q, Gd2) @@ -1158,7 +1106,7 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): True sage: bm1[1] # optional - sirocco {1: 1, 2: 2, 3: 1, 4: 1} - sage: braid_monodromy(f, arrangement=flist, computebm=False)==bm1[1] # optional - sirocco + sage: braid_monodromy(f, arrangement=flist, computebm=False) == bm1[1] # optional - sirocco True sage: bm2 = braid_monodromy(f, arrangement=flist, holdstrand=True) # optional - sirocco sage: bm2[0] == bm # optional - sirocco @@ -1279,16 +1227,16 @@ def braid2rels(L, d): while low: L2 = copy(L1) j = 0 - l = L[j] other = False while j < len(L2) - 1 and not other: try: - k = L2.index(-l) + l = L2[j] + k = L2[j:].index(-l)+j A = L2[j + 1: k] - Bn = next((_ for _ in A if 0 < (_^2 - l^2)^2 < 2), None) - if B is None: + Bn = next((_ for _ in A if (abs(_) - abs(l)) ** 2 == 1), None) + if Bn is None: other = True - L2 = L2[:j] + B + L2[k + 1:] + L2 = L2[:j] + A + L2[k + 1:] else: j += 1 except ValueError: @@ -1305,15 +1253,16 @@ def braid2rels(L, d): res = None for tau in A: sg = B(c0) * B(b0).conjugating_braid(tau) - A1 = sg.right_normal_form() - if len(A1)==1: + 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[:-1]) - b1 = len(b.Tietze()) / len(A1) - par = (A1[-1].exponent_sum() / d / (d - 1) * 2) % 2 + b = prod(A1) + b1 = len(b.Tietze()) / (len(A1) + 1) if res is None or b1 < res[3]: - res = [tau, A1[:-1], par, b1] + 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]) @@ -1323,7 +1272,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in U] pasos = [B.one()] + [_ for _ in reversed(res[1])] for C in pasos: - U = [(F(a) * C**(-1)).Tietze() for a in U] + U = [(F(a) * C ** (-1)).Tietze() for a in U] ga = F / U P = ga.gap().PresentationFpGroup() dic = P.TzOptions().sage() From 3ca7ac2eb3395c85a31525fe590bcc047418fa90 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 28 Mar 2023 17:10:11 +0200 Subject: [PATCH 068/122] Add TODO --- src/sage/schemes/curves/zariski_vankampen.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index d9349c1a923..266c3b26183 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1452,6 +1452,11 @@ def braid_monodromy_arrangement(flist): s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, s1*s0*s2*s0^-1*s2*s1^-1], {1: 1, 2: 2, 3: 1, 4: 1}) + + .. TODO:: + + Create a class ``arrangements_of_curves`` with a ``braid_monodromy`` method + it can be also a method for affine line arrangements. """ f = prod(flist) if len(flist) == 1: @@ -1515,6 +1520,13 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {(-4, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1, 4: 1}) sage: fundamental_group_arrangement(flist, projective=True, braidmonodromy=BM) # optional - sirocco (Finitely presented group < x | >, {(-1, -1, -1): 2, 1: 1}) + + .. 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. """ f = prod(flist) if braidmonodromy is None: From 468d7c74ca68f028026255b9e69893c665e11f7c Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 28 Mar 2023 18:12:42 +0200 Subject: [PATCH 069/122] remove empty line --- src/sage/schemes/curves/zariski_vankampen.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 266c3b26183..3024dc1a370 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1,4 +1,3 @@ - r""" Zariski-Van Kampen method implementation From f1a9dd32084e0c05b039f7f551a69349bc6cd962 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 31 Mar 2023 16:44:11 +0200 Subject: [PATCH 070/122] imports are now alphabetically ordered braidmonodromy has been changed to braid_mon Erase spaces around = in keywords --- src/sage/schemes/curves/affine_curve.py | 10 +++--- src/sage/schemes/curves/zariski_vankampen.py | 36 ++++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 123d08082a4..2870e2d64bb 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1691,7 +1691,7 @@ class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): """ _point = AffinePlaneCurvePoint_field - def fundamental_group(self, puiseux = False, braidmonodromy = None): + def fundamental_group(self, puiseux=False, braid_mon=None): r""" Return a presentation of the fundamental group of the complement of ``self``. @@ -1702,7 +1702,7 @@ def fundamental_group(self, puiseux = False, braidmonodromy = None): presentation is constructed in the classical way or using Puiseux shortcut. - - ``braidmonodromy`` -- (default: ``False``) If the value is ``False`` + - ``braid_mon`` -- (default: ``False``) If the value is ``False`` it apply first the ``braid_monodromy`` method. If it has been already computed it can be passed as a parameter. @@ -1730,14 +1730,14 @@ def fundamental_group(self, puiseux = False, braidmonodromy = None): sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | > sage: bm = C.braid_monodromy() # optional - sirocco - sage: C.fundamental_group(braidmonodromy=bm) # optional - sirocco + sage: C.fundamental_group(braid_mon=bm) # optional - sirocco Finitely presented group < x0 | > sage: g = C.fundamental_group(puiseux=True) # optional - sirocco sage: g # optional - sirocco Finitely presented group < x0, x1 | x1*x0^-1, x1*x0*x1^-1*x0^-1 > sage: g.simplified() # optional - sirocco Finitely presented group < x0 | > - sage: g == C.fundamental_group(puiseux=True, braidmonodromy=bm) # optional - sirocco + sage: g == C.fundamental_group(puiseux=True, braid_mon=bm) # optional - sirocco True In the case of number fields, they need to have an embedding @@ -1763,7 +1763,7 @@ def fundamental_group(self, puiseux = False, braidmonodromy = None): raise NotImplementedError("the base field must have an embedding" " to the algebraic field") f = self.defining_polynomial() - return fundamental_group(f, projective=False, puiseux = puiseux, braidmonodromy = braidmonodromy) + return fundamental_group(f, projective=False, puiseux = puiseux, braid_mon = braid_mon) def braid_monodromy(self): r""" diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 3024dc1a370..880ae1e1720 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -51,19 +51,19 @@ 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 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.number_field.number_field import NumberField from sage.rings.complex_interval_field import ComplexIntervalField from sage.rings.complex_mpfr import ComplexField +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.matrix.constructor import matrix -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.libs.braiding import rightnormalform roots_interval_cache = {} @@ -1284,7 +1284,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in gb.relations()] return U -def fundamental_group(f, simplified=True, projective=False, puiseux=False, braidmonodromy=None): +def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid_mon=None): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. @@ -1308,7 +1308,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid of the complement of the affine curve will be computed, adding one relation if ``projective`` is set to ``True``. - - ``braidmonodromy`` -- (default: ``None``); if it is set to a list of braids + - ``braid_mon`` -- (default: ``None``); if it is set to a list of braids braid monodromy is not computed ad this list is used instead. If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation @@ -1332,7 +1332,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid Finitely presented group < x0 | x0^3 > sage: bm = braid_monodromy(f); bm # optional - sirocco [(s1*s0)^2] - sage: fundamental_group(f, braidmonodromy=bm) # optional - sirocco + sage: fundamental_group(f, braid_mon=bm) # optional - sirocco Finitely presented group < x1, x2 | x1*x2*x1^-1*x2^-1*x1^-1*x2 > :: @@ -1377,7 +1377,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) sage: bm = braid_monodromy(f); print(bm) # optional - sirocco [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(f, projective=True, braidmonodromy=bm); print (g) # optional - sirocco + sage: g = fundamental_group(f, projective=True, braid_mon=bm); print (g) # optional - sirocco 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()) # optional - sirocco 12 (4,) @@ -1387,10 +1387,10 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid x, y = g.parent().gens() while g.degree(y) < g.degree(): g = g.subs({x: x + y}) - if braidmonodromy is None: + if braid_mon is None: bm = braid_monodromy(g) else: - bm =braidmonodromy + bm =braid_mon d = bm[0].parent().strands() F = FreeGroup(d) @@ -1464,7 +1464,7 @@ def braid_monodromy_arrangement(flist): return (braid_monodromy(f), dic) return braid_monodromy(f, arrangement=flist, holdstrand=False) -def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, braidmonodromy=None): +def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, braid_mon=None): 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 @@ -1489,7 +1489,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis of the complement of the affine curve will be computed, adding one relation if ``projective`` is set to ``True``. - - ``braidmonodromy`` -- (default: ``None``); it can be set to the output + - ``braid_mon`` -- (default: ``None``); it can be set to the output of ``braid_monodromy_arrangement`` to avoid an extra computation. OUTPUT: @@ -1513,11 +1513,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis sage: dic # optional - sirocco {(-1, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1} sage: BM = braid_monodromy_arrangement(flist) # optional - sirocco - sage: (g, dic) == fundamental_group_arrangement(flist, braidmonodromy=BM) # optional - sirocco + sage: (g, dic) == fundamental_group_arrangement(flist, braid_mon=BM) # optional - sirocco True - sage: fundamental_group_arrangement(flist, simplified=False, braidmonodromy=BM) # optional - sirocco + sage: fundamental_group_arrangement(flist, simplified=False, braid_mon=BM) # optional - sirocco (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {(-4, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1, 4: 1}) - sage: fundamental_group_arrangement(flist, projective=True, braidmonodromy=BM) # optional - sirocco + sage: fundamental_group_arrangement(flist, projective=True, braid_mon=BM) # optional - sirocco (Finitely presented group < x | >, {(-1, -1, -1): 2, 1: 1}) .. TODO:: @@ -1528,11 +1528,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis applying a generic line section. """ f = prod(flist) - if braidmonodromy is None: + if braid_mon is None: bm, dic = braid_monodromy_arrangement(flist) else: - bm, dic = braidmonodromy - g = fundamental_group(f, simplified=False, projective=projective, puiseux=puiseux, braidmonodromy=bm) + bm, dic = braid_mon + g = fundamental_group(f, simplified=False, projective=projective, puiseux=puiseux, braid_mon=bm) if not simplified and projective: return (g, dic) elif not simplified: From 235000731d0507148bdd30e22bf2f46c8d7028c3 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 5 Apr 2023 15:22:46 +0200 Subject: [PATCH 071/122] delete spaces after keywords --- src/sage/schemes/curves/affine_curve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 2870e2d64bb..de88c74082c 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1763,7 +1763,7 @@ def fundamental_group(self, puiseux=False, braid_mon=None): raise NotImplementedError("the base field must have an embedding" " to the algebraic field") f = self.defining_polynomial() - return fundamental_group(f, projective=False, puiseux = puiseux, braid_mon = braid_mon) + return fundamental_group(f, projective=False, puiseux=puiseux, braid_mon=braid_mon) def braid_monodromy(self): r""" From 332a878c6f52fd17f3e11c0c852a7ab3c650923d Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 8 Apr 2023 12:01:36 +0200 Subject: [PATCH 072/122] remove some indents --- src/sage/schemes/curves/zariski_vankampen.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 880ae1e1720..c89b176e98d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1452,10 +1452,10 @@ def braid_monodromy_arrangement(flist): s1*s0*s2*s0^-1*s2*s1^-1], {1: 1, 2: 2, 3: 1, 4: 1}) - .. TODO:: + .. TODO:: - Create a class ``arrangements_of_curves`` with a ``braid_monodromy`` method - it can be also a method for affine line arrangements. + Create a class ``arrangements_of_curves`` with a ``braid_monodromy`` method + it can be also a method for affine line arrangements. """ f = prod(flist) if len(flist) == 1: @@ -1520,12 +1520,12 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis sage: fundamental_group_arrangement(flist, projective=True, braid_mon=BM) # optional - sirocco (Finitely presented group < x | >, {(-1, -1, -1): 2, 1: 1}) - .. TODO:: + .. 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. + 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. """ f = prod(flist) if braid_mon is None: From 513fb2944929cf2704f6b49d5cea509de98f66c3 Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 10 Apr 2023 13:23:54 -0700 Subject: [PATCH 073/122] build/pkgs/libgd/spkg-src: Obsolete, remove --- build/pkgs/libgd/spkg-src | 34 ---------------------------------- 1 file changed, 34 deletions(-) delete mode 100755 build/pkgs/libgd/spkg-src diff --git a/build/pkgs/libgd/spkg-src b/build/pkgs/libgd/spkg-src deleted file mode 100755 index 35ee15123f7..00000000000 --- a/build/pkgs/libgd/spkg-src +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash - -VERSION="2.1.1" - -if [ $# -ne 0 ]; then - UPSTREAM_SOURCE_DIRECTORY=$1 - echo "Using tarballs from $UPSTREAM_SOURCE_DIRECTORY instead of downloading" -fi - -SPKG_ROOT=`pwd` - -set -e -shopt -s extglob - -# Remove old sources and download new -rm -rf gd* - -URL="https://bitbucket.org/libgd/gd-libgd/downloads" -if [ -z "$UPSTREAM_SOURCE_DIRECTORY" ]; then - tar xJf <( curl -L "$URL/libgd-$VERSION.tar.xz" ) -else - tar xJf "$UPSTREAM_SOURCE_DIRECTORY/libgd-$VERSION.tar.xz" -fi - -# Autoreconf it so that it builds correctly on Cygwin64 and ppc64le -cd libgd-$VERSION -rm -f aclocal.m4 Makefile.in configure -autoreconf -fiv -rm -rf autom4te.cache src/config.hin~ -cd $SPKG_ROOT -mv "libgd-$VERSION" src - -# Recompress directory -tar cjf libgd-`sed s/[.]p.*// package-version.txt`.tar.bz2 src From b2c4fc8b995b916ffbd7f4fc2c0dfb143d14969a Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 10 Apr 2023 13:46:42 -0700 Subject: [PATCH 074/122] build/pkgs/libgd: Disable most libraries, remove dependencies --- build/pkgs/libgd/dependencies | 2 +- build/pkgs/libgd/spkg-install.in | 24 ++++++------------------ 2 files changed, 7 insertions(+), 19 deletions(-) diff --git a/build/pkgs/libgd/dependencies b/build/pkgs/libgd/dependencies index e2cbc65b7da..35803d781b5 100644 --- a/build/pkgs/libgd/dependencies +++ b/build/pkgs/libgd/dependencies @@ -1,4 +1,4 @@ -libpng freetype xz +xz # xz needed to unpack tarball when sage-bootstrap-python is Python < 3.3 ---------- diff --git a/build/pkgs/libgd/spkg-install.in b/build/pkgs/libgd/spkg-install.in index d291869a923..e314d99d2ab 100644 --- a/build/pkgs/libgd/spkg-install.in +++ b/build/pkgs/libgd/spkg-install.in @@ -2,25 +2,13 @@ cd src export CFLAGS="-g $CFLAGS" -if [ "$UNAME" = "CYGWIN" ]; then - # Compiling with vpx support creates a broken library in some cases - # because the vpx package itself is broken on some older Cygwin versions; - # we don't need this feature so safer to just disable - # https://github.com/sagemath/sage/issues/27970 - LIBGD_CONFIGURE="--without-vpx $LIBGD_CONFIGURE" -fi -if [ -n "$SAGE_FREETYPE_PREFIX" ]; then - LIBGD_CONFIGURE="--with-freetype=$SAGE_FREETYPE_PREFIX $LIBGD_CONFIGURE" -else - LIBGD_CONFIGURE="--with-freetype=yes $LIBGD_CONFIGURE" -fi - -# We explicitly disable X and fontconfig support, since (1) X is not a SAGE dependency, -# and (2) the gd build fails on a lot of OS X PPC machines when X is enabled. -# Also, libgd will try to link against system libavif/libvmaf and fail -# on Fedora 34 +# We explicitly disable X, fontconfig, and support of various formats/libraries. +# We only need png. +# see https://github.com/libgd/libgd/blob/master/configure.ac sdh_configure --without-jpeg --without-xpm --without-x --without-fontconfig \ - --without-avif \ + --without-avif --without-freetype --without-raqm --without-liq \ + --without-tiff --without-webp --without-heif \ + --disable-gd-formats \ --with-zlib="$SAGE_LOCAL" $LIBGD_CONFIGURE sdh_make sdh_make_install From 24543e42a884e36016c1598bb27150c9ff34291f Mon Sep 17 00:00:00 2001 From: Matthias Koeppe Date: Mon, 10 Apr 2023 13:46:58 -0700 Subject: [PATCH 075/122] build/pkgs/libgd: Upgrade to 2.3.3 --- build/pkgs/libgd/SPKG.rst | 7 +------ build/pkgs/libgd/checksums.ini | 6 +++--- build/pkgs/libgd/package-version.txt | 2 +- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/build/pkgs/libgd/SPKG.rst b/build/pkgs/libgd/SPKG.rst index cade8ea66c7..e022fa67978 100644 --- a/build/pkgs/libgd/SPKG.rst +++ b/build/pkgs/libgd/SPKG.rst @@ -23,9 +23,4 @@ Upstream Contact - https://libgd.github.io - Pierre Joye (http://blog.thepimp.net) -- http://libgd.bitbucket.org/ - -Special Update/Build Instructions ---------------------------------- - -See spkg-src script. +- https://github.com/libgd/libgd diff --git a/build/pkgs/libgd/checksums.ini b/build/pkgs/libgd/checksums.ini index 0db72cded2e..504b0d2d5be 100644 --- a/build/pkgs/libgd/checksums.ini +++ b/build/pkgs/libgd/checksums.ini @@ -1,5 +1,5 @@ tarball=libgd-VERSION.tar.xz -sha1=dddf5e9d25cb0b20b8642d5cbcfad67f8903532f -md5=0ee844caca06bb02bf4b4dabdfab4fb1 -cksum=902217083 +sha1=7ce6ecb5aed26c08246a37b6351c886ab4b51ca2 +md5=7a58b54d375eda236414201252a0ee3c +cksum=1360697583 upstream_url=https://github.com/libgd/libgd/releases/download/gd-VERSION/libgd-VERSION.tar.xz diff --git a/build/pkgs/libgd/package-version.txt b/build/pkgs/libgd/package-version.txt index f90b1afc082..0bee604df76 100644 --- a/build/pkgs/libgd/package-version.txt +++ b/build/pkgs/libgd/package-version.txt @@ -1 +1 @@ -2.3.2 +2.3.3 From 5b4f4472df04d5656e5fecb29b3701fdc4b92be0 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 16 Apr 2023 01:04:27 +0200 Subject: [PATCH 076/122] Add trivial cases and method for line arrangements --- .../hyperplane_arrangement/arrangement.py | 55 ++++++++ src/sage/schemes/curves/zariski_vankampen.py | 132 +++++++++++------- 2 files changed, 138 insertions(+), 49 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index b740ef79956..849a0f62896 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3377,6 +3377,61 @@ def derivation_module_basis(self, algorithm="singular"): else: raise ValueError("invalid algorithm") + def _fundamental_group_(self): + r""" + It computes the fundamental group of the complement of an affine line arrangement in `\mathbb{C}^2` + with equations with coefficients in a subfield of ``QQbar`` + + OUTPUT: + + A group finitely presented with the assignation of each line to a member of a group (meridian). + + EXAMPLES:: + + sage: A. = HyperplaneArrangements(QQ) + sage: L = [y + x, y + x - 1] + sage: H = A(L) + sage: G, dic = H._fundamental_group_(); G + Finitely presented group < x0, x1 | > + sage: L = [x, y, x + 1, y + 1, x - y] + sage: H = A(L); list(H) + [Hyperplane 0*x + y + 0, + Hyperplane 0*x + y + 1, + Hyperplane x - y + 0, + Hyperplane x + 0*y + 0, + Hyperplane x + 0*y + 1] + sage: G, dic = H._fundamental_group_() + sage: G + Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, x2^-1*x0^-1*x2*x4*x0*x4^-1, + x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, + x0*x1^-1*x0^-1*x3^-1*x1*x3, + x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > + sage: dic + {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,)} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement + from sage.rings.qqbar import QQbar + n = self.dimension() + if n != 2: + print("This method only applies to two dimensional arrangements") + return None + K = self.base_ring() + if not K.is_subring(QQbar): + print("This method only works if the base field has an embedding in QQbar") + return None + S = self.parent().ambient_space().symmetric_space() + L = [] + for h in self: + coeff = h.coefficients() + V = (1,) + S.gens() + p = S.sum(V[i]*c for i, c in enumerate(coeff)) + L.append(p) + G, dic = fundamental_group_arrangement(L, puiseux=True) + return (G, dic) class HyperplaneArrangements(Parent, UniqueRepresentation): """ diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index c89b176e98d..0884ace2515 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1125,17 +1125,25 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): arrangement1 = [_.change_ring(F) for _ in arrangement1] x, y = f.parent().gens() glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor()) - g = prod(glist) + 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) arrangement1 = tuple(f1.subs({x: x + y}) for f1 in arrangement1) glist = tuple(f1.subs({x: x + y}) for f1 in glist) - disc = discrim(glist) - V = corrected_voronoi_diagram(tuple(disc)) - G, E, p, EC, DG = voronoi_cells(V) - p0 = (p[0], p[1]) + if d>0: + disc = discrim(glist) + else: + disc = [] + if len(disc) == 0: + computebm = False + result = [] + p0 = (0, 0) + else: + V = corrected_voronoi_diagram(tuple(disc)) + G, E, p, EC, DG = voronoi_cells(V) + p0 = (p[0], p[1]) if computebm: geombasis = geometric_basis(G, E, EC, p, DG) segs = set() @@ -1173,8 +1181,8 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - if len(arrangement1) == 1: - return result + if len(arrangement1) == 1: + return result p1 = p0[0] + I1 * p0[1] strands = {} roots_base = [] @@ -1186,13 +1194,14 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): if not holdstrand: roots_base.sort() strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} - if computebm and not holdstrand: + computebm0 = computebm or len(disc) == 0 + if computebm0 and not holdstrand: return (result, strands) - elif computebm and holdstrand: + elif computebm0 and holdstrand: return (result,roots_base) - elif not computebm and not holdstrand: - return strands - elif not computebm and holdstrand: + elif not computebm0 and not holdstrand: + return strands + elif not computebm0 and holdstrand: return roots_base @parallel @@ -1377,21 +1386,33 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) sage: bm = braid_monodromy(f); print(bm) # optional - sirocco [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(f, projective=True, braid_mon=bm); print (g) # optional - sirocco + sage: g = fundamental_group(f, projective=True, braid_mon=bm); g # optional - sirocco 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()) # optional - sirocco 12 (4,) """ 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}) + # if g.degree(y) == 1: + # if projective: + # return FreeGroup(1) / [[g.degree()]] + # return FreeGroup(1)/[] if braid_mon is None: bm = braid_monodromy(g) else: - bm =braid_mon - d = bm[0].parent().strands() + bm = braid_mon + if bm == []: + d = g.degree(y) + else: + d = bm[0].parent().strands() F = FreeGroup(d) @parallel @@ -1458,10 +1479,8 @@ def braid_monodromy_arrangement(flist): it can be also a method for affine line arrangements. """ f = prod(flist) - if len(flist) == 1: - d = f.degree() - dic ={j + 1 : 1 for j in range(d)} - return (braid_monodromy(f), dic) + # if len(flist) == 1: + # return (braid_monodromy(f), {}) return braid_monodromy(f, arrangement=flist, holdstrand=False) def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, braid_mon=None): @@ -1469,7 +1488,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis 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. - +g INPUT: - ``flist`` -- a tuple of polynomial with two variables, over a number field @@ -1511,15 +1530,15 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis sage: g # optional - sirocco Finitely presented group < x0, x1, x2 | x2*x1*x2^-1*x1^-1, x1*x0*x1^-1*x0^-1, x2*x0*x2*x0^-1*x2^-1*x0^-1 > sage: dic # optional - sirocco - {(-1, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1} + {1: (1,), 2: (2,), 3: (-1, -3, -2, -1)} sage: BM = braid_monodromy_arrangement(flist) # optional - sirocco sage: (g, dic) == fundamental_group_arrangement(flist, braid_mon=BM) # optional - sirocco True sage: fundamental_group_arrangement(flist, simplified=False, braid_mon=BM) # optional - sirocco - (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {(-4, -3, -2, -1): 3, 1: 1, 2: 2, 3: 1, 4: 1}) + (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {1: (1,), 2: (2,), 3: (-4, -3, -2, -1)}) sage: fundamental_group_arrangement(flist, projective=True, braid_mon=BM) # optional - sirocco - (Finitely presented group < x | >, {(-1, -1, -1): 2, 1: 1}) - + (Finitely presented group < x | >, {1: (1,), 2: (-1, -1, -1)}) + .. TODO:: Create a class ``arrangements_of_curves`` with a ``fundamental_group`` method @@ -1527,37 +1546,52 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis hyperplane arrangements defined over a number subfield of ``QQbar`` after applying a generic line section. """ - f = prod(flist) + if len(flist)>0: + f = prod(flist) + x, y = flist[0].parent().gens() + else: + R = PolynomialRing(QQ, ('x', 'y')) + x, y = R.gens() + f = R(1) + flist1 = [_ for _ in flist] + if projective: + while f.degree(y) < f.degree(): + flist1 = [g.subs({x: x + y}) for g in flist] + f = prod(flist1) if braid_mon is None: - bm, dic = braid_monodromy_arrangement(flist) + if len(flist1) == 1: + bm = braid_monodromy_arrangement(flist1) + dic = {j + 1: 1 for j in range(len(flist))} + elif len(flist1) == 0: + bm = [] + dic = dict() + else: + bm, dic = braid_monodromy_arrangement(flist1) else: bm, dic = braid_mon g = fundamental_group(f, simplified=False, projective=projective, puiseux=puiseux, braid_mon=bm) - if not simplified and projective: - return (g, dic) - elif not simplified: - a = tuple(-i - 1 for i in reversed(range(g.ngens()))) - dic[a] = len(flist) + 1 - return (g,dic) - hom = g.simplification_isomorphism() + if simplified: + 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 j in range(g.ngens()): - x = hom(g.gen(j)).Tietze() - if len(x) == 1: - i = x[0] - if i not in dic1.keys(): - dic1[i] = dic[j + 1] - if projective: - Lg = [j+1 for j in range(len(flist)) if j+1 not in dic1.keys()] - if len(Lg) == 1: - j = Lg[0] - i = [i + 1 for i in range(g.ngens()) if dic[i + 1] == j][0] - t = hom(g([i])).Tietze() - dic1[t] = j - else: + for i in range(len(flist1)): + j = [j1 for j1 in dic.keys() if dic[j1] == i + 1][0] + dic1[i + 1] = hom(g([j])).Tietze() + # if projective: + # Lg = [j+1 for j in range(len(flist1)) if j+1 not in dic1.keys()] + # if len(Lg) == 1: + # j = Lg[0] + # i = [i + 1 for i in range(g.ngens()) if dic[i + 1] == j][0] + # t = hom(g([i])).Tietze() + # dic1[j] = t + # elif f.degree(y) == f.degree(): + if not projective and f.degree(y) == f.degree(): t = prod(hom(x) for x in g.gens()).inverse().Tietze() - dic1[t] = len(flist) + 1 + dic1[len(flist1) + 1] = t n = g1.ngens() rels = [_.Tietze() for _ in g1.relations()] g1 = FreeGroup(n) / rels From 41ab326148e41997adfe1346d026b0adbf3d2ee4 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 16 Apr 2023 01:12:40 +0200 Subject: [PATCH 077/122] typos --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 6 +++--- src/sage/schemes/curves/zariski_vankampen.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 849a0f62896..373e02d8177 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3400,10 +3400,10 @@ def _fundamental_group_(self): Hyperplane x - y + 0, Hyperplane x + 0*y + 0, Hyperplane x + 0*y + 1] - sage: G, dic = H._fundamental_group_() + sage: G, dic = H._fundamental_group_() # optional - sirocco sage: G Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, x2^-1*x0^-1*x2*x4*x0*x4^-1, - x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, + x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, x0*x1^-1*x0^-1*x3^-1*x1*x3, x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > sage: dic @@ -3412,7 +3412,7 @@ def _fundamental_group_(self): .. WARNING:: This functionality requires the sirocco package to be installed. - """ + """ from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement from sage.rings.qqbar import QQbar n = self.dimension() diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 0884ace2515..8420dcdfe5b 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1488,7 +1488,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis 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. -g + INPUT: - ``flist`` -- a tuple of polynomial with two variables, over a number field @@ -1549,14 +1549,14 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis if len(flist)>0: f = prod(flist) x, y = flist[0].parent().gens() - else: + else: R = PolynomialRing(QQ, ('x', 'y')) x, y = R.gens() f = R(1) flist1 = [_ for _ in flist] if projective: while f.degree(y) < f.degree(): - flist1 = [g.subs({x: x + y}) for g in flist] + flist1 = [g.subs({x: x + y}) for g in flist] f = prod(flist1) if braid_mon is None: if len(flist1) == 1: From d14da65a8880a1c455a2250f7f4ebc475d1fe5ce Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 16 Apr 2023 12:26:51 +0200 Subject: [PATCH 078/122] fundamental group of projective line arrangements --- .../hyperplane_arrangement/arrangement.py | 33 +++++++++++++++---- src/sage/schemes/curves/zariski_vankampen.py | 14 +++++--- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 373e02d8177..74c15cfb2e5 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3391,7 +3391,7 @@ def _fundamental_group_(self): sage: A. = HyperplaneArrangements(QQ) sage: L = [y + x, y + x - 1] sage: H = A(L) - sage: G, dic = H._fundamental_group_(); G + sage: G, dic = H._fundamental_group_(); G # optional - sirocco Finitely presented group < x0, x1 | > sage: L = [x, y, x + 1, y + 1, x - y] sage: H = A(L); list(H) @@ -3407,16 +3407,28 @@ def _fundamental_group_(self): x0*x1^-1*x0^-1*x3^-1*x1*x3, x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > sage: dic - {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,)} - + {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} + sage: H = hyperplane_arrangements.braid(4).essentialization() + sage: H._fundamental_group_() # optional - sirocco + (Finitely presented group < x0, x1, x2, x3, x4 | x1*x3*x1^-1*x3^-1, + x0*x2*x0^-1*x2^-1, x4^-1*x1^-1*x0^-1*x4*x0*x1, + x3*x4*x2*x3^-1*x2^-1*x4^-1, x3*x4^-1*x3^-1*x2^-1*x4*x2, + x4*x0*x1^-1*x0^-1*x4^-1*x1 >, + {1: (5,), 2: (1,), 3: (2,), 4: (3,), 5: (4,), 6: (-2, -1, -4, -3, -5)}) + .. WARNING:: This functionality requires the sirocco package to be installed. """ from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement from sage.rings.qqbar import QQbar + from sage.combinat.permutation import Permutation n = self.dimension() - if n != 2: + r = len(self) + affine = n == 2 + projective = n==3 and self.is_central() + casos = affine or projective + if not casos: print("This method only applies to two dimensional arrangements") return None K = self.base_ring() @@ -3424,13 +3436,22 @@ def _fundamental_group_(self): print("This method only works if the base field has an embedding in QQbar") return None S = self.parent().ambient_space().symmetric_space() + if projective: + S = PolynomialRing(K, S.gens()[:-1]) + infinity = [0, 0, 0, 1] == self[0].primitive().coefficients() L = [] for h in self: coeff = h.coefficients() + if projective: + coeff = (coeff[3], coeff[1], coeff[2]) V = (1,) + S.gens() p = S.sum(V[i]*c for i, c in enumerate(coeff)) - L.append(p) - G, dic = fundamental_group_arrangement(L, puiseux=True) + if p.degree() > 0: + L.append(p) + G, dic = fundamental_group_arrangement(L, puiseux=True, projective=projective and not infinity) + if infinity: + p = Permutation([r] + [j for j in range(1, r)] ) + dic = {j: dic[p(j)] for j in range(1, r + 1)} return (G, dic) class HyperplaneArrangements(Parent, UniqueRepresentation): diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 8420dcdfe5b..5b015103acf 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1200,7 +1200,7 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): elif computebm0 and holdstrand: return (result,roots_base) elif not computebm0 and not holdstrand: - return strands + return strands elif not computebm0 and holdstrand: return roots_base @@ -1538,7 +1538,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {1: (1,), 2: (2,), 3: (-4, -3, -2, -1)}) sage: fundamental_group_arrangement(flist, projective=True, braid_mon=BM) # optional - sirocco (Finitely presented group < x | >, {1: (1,), 2: (-1, -1, -1)}) - + .. TODO:: Create a class ``arrangements_of_curves`` with a ``fundamental_group`` method @@ -1548,12 +1548,18 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis """ if len(flist)>0: f = prod(flist) - x, y = flist[0].parent().gens() + R = f.parent() else: R = PolynomialRing(QQ, ('x', 'y')) - x, y = R.gens() 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: while f.degree(y) < f.degree(): flist1 = [g.subs({x: x + y}) for g in flist] From 9ab56ccd1e5b95d65ecf5220324d86e6f36583b4 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 16 Apr 2023 14:50:33 +0200 Subject: [PATCH 079/122] dimension 1 --- .../hyperplane_arrangement/arrangement.py | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 74c15cfb2e5..db77055f58d 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3377,10 +3377,15 @@ def derivation_module_basis(self, algorithm="singular"): else: raise ValueError("invalid algorithm") - def _fundamental_group_(self): + def _fundamental_group_(self, proj=False): r""" It computes the fundamental group of the complement of an affine line arrangement in `\mathbb{C}^2` with equations with coefficients in a subfield of ``QQbar`` + + INPUT: + + - ``proj`` -- (optional, default ``False``). It decides if it computes the fundamental group + of the complement in the affine or projective space OUTPUT: @@ -3408,8 +3413,14 @@ def _fundamental_group_(self): x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > sage: dic {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} + sage: H=A(x,y,x+y) + sage: H._fundamental_group_() + (Finitely presented group < x0, x1, x2 | x0*x1^-1*x0^-1*x2^-1*x1*x2, x0*x1*x2*x1^-1*x0^-1*x2^-1 >, + {1: (3,), 2: (1,), 3: (2,), 4: (-3, -2, -1)}) + sage: H._fundamental_group_(proj=True) # optional - sirocco + (Finitely presented group < x0, x1, x2 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) sage: H = hyperplane_arrangements.braid(4).essentialization() - sage: H._fundamental_group_() # optional - sirocco + sage: H._fundamental_group_(proj=True) # optional - sirocco (Finitely presented group < x0, x1, x2, x3, x4 | x1*x3*x1^-1*x3^-1, x0*x2*x0^-1*x2^-1, x4^-1*x1^-1*x0^-1*x4*x0*x1, x3*x4*x2*x3^-1*x2^-1*x4^-1, x3*x4^-1*x3^-1*x2^-1*x4*x2, @@ -3420,16 +3431,23 @@ def _fundamental_group_(self): This functionality requires the sirocco package to be installed. """ - from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement - from sage.rings.qqbar import QQbar from sage.combinat.permutation import Permutation + from sage.groups.free_group import FreeGroup + from sage.rings.qqbar import QQbar + from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement n = self.dimension() r = len(self) - affine = n == 2 - projective = n==3 and self.is_central() + affine = n == 2 and not proj + projective = n==3 and self.is_central() and proj + if (n == 1 and not proj) or (n == 2 and proj and self.is_central()): + r1 = r - projective + G = FreeGroup(r1) / [] + dic = {j: (j,) for j in range(1, r)} + dic[r] = tuple(-j for j in reversed(range(1, r))) + return (G, dic) casos = affine or projective if not casos: - print("This method only applies to two dimensional arrangements") + print("This method does not apply") return None K = self.base_ring() if not K.is_subring(QQbar): From 90a7d720dad65d93a81456dba6991690e31b1e30 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 16 Apr 2023 19:12:13 +0200 Subject: [PATCH 080/122] hyperplane sections --- .../geometry/hyperplane_arrangement/arrangement.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index db77055f58d..857770205b1 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3377,6 +3377,18 @@ def derivation_module_basis(self, algorithm="singular"): else: raise ValueError("invalid algorithm") + def hyperplane_section(self): + P = self.intersection_poset(element_label="subspace") + n0 = self.dimension() + n1 = self.center().dimension() + U = [p.linear_part().basis_matrix() for p in P if p.dimension() == n1 + 1] + for v in ZZ^n0: + if 0 not in [w * v for w in U]: + break + h0 = self.parent()((0,) + tuple(v)) + H1 = H.add_hyperplane(h0) + return H1.restriction(h0) + def _fundamental_group_(self, proj=False): r""" It computes the fundamental group of the complement of an affine line arrangement in `\mathbb{C}^2` From 387f4233f66aa45dd4325d9eb45106e15272d0b5 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 17 Apr 2023 00:29:30 +0200 Subject: [PATCH 081/122] Consider permutations --- .../hyperplane_arrangement/arrangement.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 857770205b1..55114f72f14 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -344,18 +344,19 @@ # - create ties with the Sage matroid methods # - hyperplane arrangements over other fields +from sage.combinat.permutation import Permutation +from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane +from sage.matrix.constructor import matrix, vector +from sage.misc.cachefunc import cached_method +from sage.modules.free_module import VectorSpace +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.rational_field import QQ from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import richcmp from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.integer_ring import ZZ -from sage.rings.rational_field import QQ -from sage.misc.cachefunc import cached_method -from sage.matrix.constructor import matrix, vector -from sage.modules.free_module import VectorSpace -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane class HyperplaneArrangementElement(Element): @@ -1033,7 +1034,7 @@ def deletion(self, hyperplanes): raise ValueError('hyperplane is not in the arrangement') return parent(*planes, backend=self._backend) - def restriction(self, hyperplane): + def restriction(self, hyperplane, permutation=False): r""" Return the restriction to a hyperplane. @@ -1098,7 +1099,18 @@ def restriction(self, hyperplane): names = list(parent._names) names.pop(pivot) H = HyperplaneArrangements(parent.base_ring(), names=tuple(names)) - return H(*hyperplanes, signed=False, backend=self._backend) + result = H(*hyperplanes, signed=False, backend=self._backend) + if permutation: + L1 = [_ for _ in result] + L = [] + for h in hyperplanes: + h0 = H(h, signed=False)[0] + j = L1.index(h0) + L.append(j + 1) + P = Permutation(L) + return (result, P) + else: + return result def change_ring(self, base_ring): """ @@ -3382,20 +3394,20 @@ def hyperplane_section(self): n0 = self.dimension() n1 = self.center().dimension() U = [p.linear_part().basis_matrix() for p in P if p.dimension() == n1 + 1] - for v in ZZ^n0: + for v in ZZ**n0: if 0 not in [w * v for w in U]: break h0 = self.parent()((0,) + tuple(v)) - H1 = H.add_hyperplane(h0) - return H1.restriction(h0) + H1 = self.add_hyperplane(h0) + return H1.restriction(h0, permutation=True) def _fundamental_group_(self, proj=False): r""" It computes the fundamental group of the complement of an affine line arrangement in `\mathbb{C}^2` with equations with coefficients in a subfield of ``QQbar`` - + INPUT: - + - ``proj`` -- (optional, default ``False``). It decides if it computes the fundamental group of the complement in the affine or projective space @@ -3418,12 +3430,12 @@ def _fundamental_group_(self, proj=False): Hyperplane x + 0*y + 0, Hyperplane x + 0*y + 1] sage: G, dic = H._fundamental_group_() # optional - sirocco - sage: G + sage: G # optional - sirocco Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, x2^-1*x0^-1*x2*x4*x0*x4^-1, x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, x0*x1^-1*x0^-1*x3^-1*x1*x3, x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > - sage: dic + sage: dic # optional - sirocco {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} sage: H=A(x,y,x+y) sage: H._fundamental_group_() @@ -3443,7 +3455,6 @@ def _fundamental_group_(self, proj=False): This functionality requires the sirocco package to be installed. """ - from sage.combinat.permutation import Permutation from sage.groups.free_group import FreeGroup from sage.rings.qqbar import QQbar from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement From c8c1714934dc2e356052f43478564cb6fa0423b8 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 17 Apr 2023 08:50:14 +0200 Subject: [PATCH 082/122] correct permutation to restriction --- .../hyperplane_arrangement/arrangement.py | 55 ++++++++++++++++++- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 55114f72f14..44276b5ef6c 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -1042,10 +1042,18 @@ def restriction(self, hyperplane, permutation=False): - ``hyperplane`` -- a hyperplane of the hyperplane arrangement + - ``permutation`` -- (optional, default ``False``) If ``True`` + it computes the permutation relating the order of hyperplanes + in the input and in the output. + OUTPUT: The restriction of the hyperplane arrangement to the given - ``hyperplane``. + ``hyperplane``. If ``permutation`` is set to ``True``, also a permutation + ``P`` such that if if the restriction of the ``j``-th hyperplane of ``self`` + (forgetting ``hyperplane``) occupies the ``k``-th position in the restriction, + then ``P(j)=k``. If several hyperplanes of ``self`` produce the same hyperplane + in the restriction, the second output is ``None``. EXAMPLES:: @@ -1055,6 +1063,12 @@ def restriction(self, hyperplane, permutation=False): Hyperplane 0*u + 0*x + y - z + 0 sage: R = A.restriction(H); R Arrangement + sage: A.restriction(H, permutation=True) + (Arrangement , None) + sage: A.add_hyperplane(z).restriction(z, permutation=True) + (Arrangement of 6 hyperplanes of dimension 3 and rank 3, [1, 2, 3, 4, 5, 6]) + sage: A.add_hyperplane(u).restriction(u, permutation=True) + (Arrangement of 6 hyperplanes of dimension 3 and rank 3, [2, 4, 5, 6, 3, 1]) sage: D = A.deletion(H); D Arrangement of 5 hyperplanes of dimension 4 and rank 3 sage: ca = A.characteristic_polynomial() @@ -1107,8 +1121,11 @@ def restriction(self, hyperplane, permutation=False): h0 = H(h, signed=False)[0] j = L1.index(h0) L.append(j + 1) - P = Permutation(L) - return (result, P) + if len(L) > len(set(L)): + return (result, None) + else: + P = Permutation(L) + return (result, P) else: return result @@ -3390,6 +3407,38 @@ def derivation_module_basis(self, algorithm="singular"): raise ValueError("invalid algorithm") def hyperplane_section(self): + r""" + It computes a generic hyperplane section of ``self``, a central arrangement + + OUTPUT: + + An arrangement obtained by intersecting with a generic hyperplane. + + EXAMPLES:: + + sage: A. = hyperplane_arrangements.braid(4); A + Arrangement of 6 hyperplanes of dimension 4 and rank 3 + sage: [_.coefficients() for _ in A] + [[0, 0, 0, 1, -1], + [0, 0, 1, -1, 0], + [0, 0, 1, 0, -1], + [0, 1, -1, 0, 0], + [0, 1, 0, -1, 0], + [0, 1, 0, 0, -1]] + sage: A1, P = A.hyperplane_section() + sage: [_.coefficients() for _ in A1] + [[0, 0, 0, 1], + [0, 0, 1, -1], + [0, 0, 1, 0], + [0, 1, -1, 0], + [0, 1, 0, -1], + [0, 1, 0, 0]] + sage: P + [2, 4, 5, 6, 3, 1] + """ + if not self.is_central(): + print("It applies only to central arrangements") + return None P = self.intersection_poset(element_label="subspace") n0 = self.dimension() n1 = self.center().dimension() From 004df52099edd19a9879e9f8b9d981d102a1b3af Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 17 Apr 2023 15:24:57 +0200 Subject: [PATCH 083/122] fundamental group --- .../hyperplane_arrangement/arrangement.py | 205 +++++++++++++++--- 1 file changed, 171 insertions(+), 34 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 44276b5ef6c..e39da7dff30 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -695,7 +695,7 @@ def plot(self, **kwds): from sage.geometry.hyperplane_arrangement.plot import plot return plot(self, **kwds) - def cone(self, variable='t'): + def cone(self, variable='t', permutation=False): r""" Return the cone over the hyperplane arrangement. @@ -703,11 +703,19 @@ def cone(self, variable='t'): - ``variable`` -- string; the name of the additional variable + - ``permutation`` -- (optional, default ``False``) If ``True`` + it computes the permutation relating the order of hyperplanes + in the input and in the output. + OUTPUT: A new hyperplane arrangement. Its equations consist of `[0, -d, a_1, \ldots, a_n]` for each `[d, a_1, \ldots, a_n]` in the original arrangement and the equation `[0, 1, 0, \ldots, 0]`. + If ``permutation`` is set to ``True``, also a permutation + ``P`` such that if if the cone of the ``j``-th hyperplane of ``self`` + occupies the ``k``-th position in the cone, then ``P(j)=k``. + .. WARNING:: @@ -741,6 +749,11 @@ def cone(self, variable='t'): Hyperplane t + 0*x + y - z + 0, Hyperplane t + x - y + 0*z + 0, Hyperplane t + x + 0*y - z + 0) + sage: b1, P = a.cone(permutation=True) + sage: b1 == b + True + sage: P + [1, 5, 2, 6, 3, 7, 4] """ hyperplanes = [] for h in self.hyperplanes(): @@ -750,7 +763,18 @@ def cone(self, variable='t'): P = self.parent() names = (variable,) + P._names H = HyperplaneArrangements(self.parent().base_ring(), names=names) - return H(*hyperplanes, backend=self._backend) + result = H(*hyperplanes, backend=self._backend) + if permutation: + L1 = [_ for _ in result] + L = [] + for h in hyperplanes: + h0 = H(h)[0] + j = L1.index(h0) + L.append(j + 1) + P = Permutation(L) + return (result, P) + else: + return result @cached_method def intersection_poset(self, element_label="int"): @@ -3406,9 +3430,14 @@ def derivation_module_basis(self, algorithm="singular"): else: raise ValueError("invalid algorithm") - def hyperplane_section(self): + def hyperplane_section(self, proj=True): r""" - It computes a generic hyperplane section of ``self``, a central arrangement + It computes a generic hyperplane section of ``self``, an arrangement + + INPUT: + + - ``proj`` -- (optional, default ``True``). It decides if it the + ambient space is affine or projective OUTPUT: @@ -3420,49 +3449,90 @@ def hyperplane_section(self): Arrangement of 6 hyperplanes of dimension 4 and rank 3 sage: [_.coefficients() for _ in A] [[0, 0, 0, 1, -1], - [0, 0, 1, -1, 0], - [0, 0, 1, 0, -1], - [0, 1, -1, 0, 0], - [0, 1, 0, -1, 0], - [0, 1, 0, 0, -1]] + [0, 0, 1, -1, 0], + [0, 0, 1, 0, -1], + [0, 1, -1, 0, 0], + [0, 1, 0, -1, 0], + [0, 1, 0, 0, -1]] sage: A1, P = A.hyperplane_section() + sage: A1 + Arrangement of 6 hyperplanes of dimension 3 and rank 3 sage: [_.coefficients() for _ in A1] - [[0, 0, 0, 1], - [0, 0, 1, -1], - [0, 0, 1, 0], - [0, 1, -1, 0], - [0, 1, 0, -1], - [0, 1, 0, 0]] + [[0, 0, 1, -1], + [0, 1, -1, 0], + [0, 1, 0, -1], + [0, 3, 3, 10], + [0, 3, 10, 3], + [0, 10, 3, 3]] sage: P - [2, 4, 5, 6, 3, 1] + [1, 2, 3, 6, 5, 4] + sage: a = hyperplane_arrangements.semiorder(3); a + Arrangement of 6 hyperplanes of dimension 3 and rank 2 + sage: a1, p = a.hyperplane_section(proj=False) + sage: a1 + Arrangement of 6 hyperplanes of dimension 2 and rank 2 + sage: p + [2, 1, 5, 3, 6, 4] """ - if not self.is_central(): - print("It applies only to central arrangements") - return None - P = self.intersection_poset(element_label="subspace") + from sage.matrix.constructor import Matrix + if proj and not self.is_central(): + raise TypeError('The arrangement is not central') n0 = self.dimension() + r = self.n_hyperplanes() + A = self.parent() + if not proj: + H, perm0 = self.cone(permutation=True) + H1, perm1 = H.hyperplane_section() + perm = perm0 * perm1 + k = perm(r + 1) + h0 = H1[k - 1] + c0 = h0.coefficients()[1:] + j = next((i for i, x in enumerate(c0)), None) + H1a = H1.deletion(h0) + c1a = [h.coefficients()[1:] for h in H1a] + mat = Matrix([c0] + c1a) + mat.swap_columns(0, j) + for j in range(1, n0): + mat.add_multiple_of_column(j, 0, -mat[0, j] / mat[0, 0]) + vrs = H1.parent().variable_names()[1:] + A1 = HyperplaneArrangements(self.base_ring(), names=vrs) + H1b = A1(mat.rows()[1:]) + L = [] + for j in range(1, r + 1): + k0 = perm(j) + if k0 < k: + L.append(k0) + elif k0 > k: + L.append(k0 - 1) + return (H1b, Permutation(L)) + P = self.intersection_poset(element_label="subspace") n1 = self.center().dimension() - U = [p.linear_part().basis_matrix() for p in P if p.dimension() == n1 + 1] + U = [p.linear_part().basis()[0] for p in P if p.dimension() == n1 + 1] + U0 = sum(U) for v in ZZ**n0: - if 0 not in [w * v for w in U]: + v1 = v + U0 + if 0 not in [w * v1 for w in U]: break - h0 = self.parent()((0,) + tuple(v)) + h0 = self.parent()((0,) + tuple(v1)) H1 = self.add_hyperplane(h0) return H1.restriction(h0, permutation=True) def _fundamental_group_(self, proj=False): r""" - It computes the fundamental group of the complement of an affine line arrangement in `\mathbb{C}^2` - with equations with coefficients in a subfield of ``QQbar`` + It computes the fundamental group of the complement of an affine + hyperplane arrangement in `\mathbb{C}^n`, or a projective hyperplane + arrangement in `\mathbb{CP}^n`, `n=1,2`, whose equations have + coefficients in a subfield of ``QQbar`` INPUT: - - ``proj`` -- (optional, default ``False``). It decides if it computes the fundamental group - of the complement in the affine or projective space + - ``proj`` -- (optional, default ``False``). It decides if it computes the + fundamental group of the complement in the affine or projective space OUTPUT: - A group finitely presented with the assignation of each line to a member of a group (meridian). + A group finitely presented with the assignation of each hyperplane to + a member of a group (meridian). EXAMPLES:: @@ -3491,7 +3561,7 @@ def _fundamental_group_(self, proj=False): (Finitely presented group < x0, x1, x2 | x0*x1^-1*x0^-1*x2^-1*x1*x2, x0*x1*x2*x1^-1*x0^-1*x2^-1 >, {1: (3,), 2: (1,), 3: (2,), 4: (-3, -2, -1)}) sage: H._fundamental_group_(proj=True) # optional - sirocco - (Finitely presented group < x0, x1, x2 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) + (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) sage: H = hyperplane_arrangements.braid(4).essentialization() sage: H._fundamental_group_(proj=True) # optional - sirocco (Finitely presented group < x0, x1, x2, x3, x4 | x1*x3*x1^-1*x3^-1, @@ -3512,19 +3582,17 @@ def _fundamental_group_(self, proj=False): affine = n == 2 and not proj projective = n==3 and self.is_central() and proj if (n == 1 and not proj) or (n == 2 and proj and self.is_central()): - r1 = r - projective + r1 = r - proj G = FreeGroup(r1) / [] dic = {j: (j,) for j in range(1, r)} dic[r] = tuple(-j for j in reversed(range(1, r))) return (G, dic) casos = affine or projective if not casos: - print("This method does not apply") - return None + raise TypeError('The method does not apply') K = self.base_ring() if not K.is_subring(QQbar): - print("This method only works if the base field has an embedding in QQbar") - return None + raise TypeError('the base field is not in QQbar') S = self.parent().ambient_space().symmetric_space() if projective: S = PolynomialRing(K, S.gens()[:-1]) @@ -3544,6 +3612,75 @@ def _fundamental_group_(self, proj=False): dic = {j: dic[p(j)] for j in range(1, r + 1)} return (G, dic) + def fundamental_group(self, projective=False): + r""" + It computes the fundamental group of the complement of an affine + hyperplane arrangement in `\mathbb{C}^n`, or a projective hyperplane + arrangement in `\mathbb{CP}^n`, whose equations have + coefficients in a subfield of ``QQbar`` + + INPUT: + + - ``projective`` -- (optional, default ``False``). It decides if it computes the + fundamental group of the complement in the affine or projective space + + OUTPUT: + + A group finitely presented with the assignation of each hyperplane to + a member of a group (meridian). + + EXAMPLES:: + + sage: A. = HyperplaneArrangements(QQ) + sage: L = [y + x, y + x - 1] + sage: H = A(L) + sage: G, dic = H.fundamental_group(); G # optional - sirocco + Finitely presented group < x0, x1 | > + sage: L = [x, y, x + 1, y + 1, x - y] + sage: H = A(L); list(H) + [Hyperplane 0*x + y + 0, + Hyperplane 0*x + y + 1, + Hyperplane x - y + 0, + Hyperplane x + 0*y + 0, + Hyperplane x + 0*y + 1] + sage: G, dic = H.fundamental_group() # optional - sirocco + sage: G # optional - sirocco + Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, x2^-1*x0^-1*x2*x4*x0*x4^-1, + x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, + x0*x1^-1*x0^-1*x3^-1*x1*x3, + x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > + sage: dic # optional - sirocco + {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} + sage: H=A(x,y,x+y) + sage: H.fundamental_group() + (Finitely presented group < x0, x1, x2 | x0*x1^-1*x0^-1*x2^-1*x1*x2, x0*x1*x2*x1^-1*x0^-1*x2^-1 >, + {1: (3,), 2: (1,), 3: (2,), 4: (-3, -2, -1)}) + sage: H.fundamental_group(projective=True) # optional - sirocco + (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) + sage: H = hyperplane_arrangements.braid(4) + sage: H.fundamental_group(projective=True) # optional - sirocco + (Finitely presented group < x0, x1, x2, x3, x4 | x0*x3*x0^-1*x3^-1, x2*x4^-1*x2^-1*x4, x2*x3*x1*x3^-1*x2^-1*x1^-1, + x1*x4*x0*x1^-1*x0^-1*x4^-1, x3^-1*x2^-1*x1^-1*x3*x1*x2, x0^-1*x1*x4*x0*x4^-1*x1^-1 >, + {1: (5,), 2: (1,), 3: (2,), 4: (3,), 5: (-4, -5, -3, -2, -1), 6: (4,)}) + sage: H = hyperplane_arrangements.coordinate(5) + sage: g = H.fundamental_group(projective=True)[0] + sage: g.is_abelian(), g.abelian_invariants() + (True, (0, 0, 0, 0)) + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + n = self.dimension() + if n <= 2 or (n == 3 and projective): + return self._fundamental_group_(proj=projective) + H1, P = self.hyperplane_section(proj=projective) + H2, dic = H1.fundamental_group(projective=projective) + if not projective: + P = Permutation(list(P) + [self.n_hyperplanes() + 1]) + dic = {j: dic[P(j)] for j in dic.keys()} + return (H2, dic) + class HyperplaneArrangements(Parent, UniqueRepresentation): """ Hyperplane arrangements. From cbae394998614e5e07c4b21701b0581db1bfb5a3 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 17 Apr 2023 15:31:45 +0200 Subject: [PATCH 084/122] lint error --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index e39da7dff30..43dd5dc3ff7 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3500,7 +3500,7 @@ def hyperplane_section(self, proj=True): L = [] for j in range(1, r + 1): k0 = perm(j) - if k0 < k: + if k0 < k: L.append(k0) elif k0 > k: L.append(k0 - 1) From 54ee1d75e928a07020287ea88da7bbfb070c3b5b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 17 Apr 2023 18:45:14 +0200 Subject: [PATCH 085/122] permutation in essentialization --- .../hyperplane_arrangement/arrangement.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 43dd5dc3ff7..4741668053d 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -1522,7 +1522,7 @@ def is_simplicial(self): return all(R.n_facets() == rank for R in self.regions()) @cached_method - def essentialization(self): + def essentialization(self, permutation=False): r""" Return the essentialization of the hyperplane arrangement. @@ -1615,7 +1615,18 @@ def echelon_col_iter(row_iter): names = tuple(name for i, name in enumerate(parent._names) if i not in echelon_pivots) # Construct the result restricted_parent = HyperplaneArrangements(R, names=names) - return restricted_parent(*restricted, signed=False, backend=self._backend) + result = restricted_parent(*restricted, signed=False, backend=self._backend) + if permutation: + L1 = [_ for _ in result] + L = [] + for h in restricted: + h0 = restricted_parent(h, signed=False)[0] + j = L1.index(h0) + L.append(j + 1) + P = Permutation(L) + return (result, P) + else: + return result def sign_vector(self, p): r""" From e1a8c72f152f9d109be39b0dc90d525751c783dc Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 18 Apr 2023 15:26:17 +0200 Subject: [PATCH 086/122] problems in permutation for hyperplane_section --- .../hyperplane_arrangement/arrangement.py | 80 ++++++++++++------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 4741668053d..d73dc154980 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -714,7 +714,8 @@ def cone(self, variable='t', permutation=False): original arrangement and the equation `[0, 1, 0, \ldots, 0]`. If ``permutation`` is set to ``True``, also a permutation ``P`` such that if if the cone of the ``j``-th hyperplane of ``self`` - occupies the ``k``-th position in the cone, then ``P(j)=k``. + occupies the ``k``-th position in the cone, then ``P(j)=k``. In the source + the hyperplane at infinity is the last one. .. WARNING:: @@ -754,6 +755,8 @@ def cone(self, variable='t', permutation=False): True sage: P [1, 5, 2, 6, 3, 7, 4] + sage: b1[P(b1.n_hyperplanes())-1] + Hyperplane t + 0*x + 0*y + 0*z + 0 """ hyperplanes = [] for h in self.hyperplanes(): @@ -3458,32 +3461,48 @@ def hyperplane_section(self, proj=True): sage: A. = hyperplane_arrangements.braid(4); A Arrangement of 6 hyperplanes of dimension 4 and rank 3 - sage: [_.coefficients() for _ in A] - [[0, 0, 0, 1, -1], - [0, 0, 1, -1, 0], - [0, 0, 1, 0, -1], - [0, 1, -1, 0, 0], - [0, 1, 0, -1, 0], - [0, 1, 0, 0, -1]] + sage: M = A.matroid() sage: A1, P = A.hyperplane_section() sage: A1 Arrangement of 6 hyperplanes of dimension 3 and rank 3 - sage: [_.coefficients() for _ in A1] - [[0, 0, 1, -1], - [0, 1, -1, 0], - [0, 1, 0, -1], - [0, 3, 3, 10], - [0, 3, 10, 3], - [0, 10, 3, 3]] sage: P [1, 2, 3, 6, 5, 4] + sage: M1 = A1.matroid() + sage: M.is_isomorphism(M1, {j: P(j + 1) - 1 for j in M.groundset()}) + True + sage: A2, Q = A1.hyperplane_section(); A2 + Arrangement of 6 hyperplanes of dimension 2 and rank 2 + sage: M2 = A2.matroid() + sage: T1 = M1.truncation() + sage: T1.is_isomorphism(M2, {j: Q(j + 1) - 1 for j in T1.groundset()}) + True sage: a = hyperplane_arrangements.semiorder(3); a Arrangement of 6 hyperplanes of dimension 3 and rank 2 + sage: ca, p0 = a.cone(permutation=True) + sage: m = ca.matroid() sage: a1, p = a.hyperplane_section(proj=False) sage: a1 Arrangement of 6 hyperplanes of dimension 2 and rank 2 - sage: p - [2, 1, 5, 3, 6, 4] + sage: p = Permutation([p(j) for j in [1 .. 6]] + [7]); p + [6, 5, 2, 1, 4, 3, 7] + sage: ca1, p1 = a1.cone(permutation=True) + sage: m1 = ca1.matroid() + sage: q = p0.inverse() * p * p1 + sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) + True + sage: a = hyperplane_arrangements.Shi(4).hyperplane_section(proj=False)[0]; a + Arrangement of 12 hyperplanes of dimension 3 and rank 3 + sage: ca, p1 = a.cone(permutation=True) + sage: m = ca.matroid().truncation() + sage: a1, p = a.hyperplane_section(proj=False); a1 + Arrangement of 12 hyperplanes of dimension 2 and rank 2 + sage: p = Permutation([p(j) for j in [1 .. 12]] + [13]); p + [1, 5, 9, 12, 10, 11, 7, 8, 3, 4, 2, 6, 13] + sage: ca1, p2 = a1.cone(permutation=True) + sage: m1 = ca1.matroid() + sage: q = p1.inverse() * p * p2 + sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) + True """ from sage.matrix.constructor import Matrix if proj and not self.is_central(): @@ -3496,18 +3515,25 @@ def hyperplane_section(self, proj=True): H1, perm1 = H.hyperplane_section() perm = perm0 * perm1 k = perm(r + 1) - h0 = H1[k - 1] - c0 = h0.coefficients()[1:] - j = next((i for i, x in enumerate(c0)), None) - H1a = H1.deletion(h0) - c1a = [h.coefficients()[1:] for h in H1a] - mat = Matrix([c0] + c1a) - mat.swap_columns(0, j) - for j in range(1, n0): + mat = Matrix(h.coefficients()[1:] for h in H1) + mat.swap_rows(0, k - 1) + for j in range(mat.ncols()): + if mat[0, j] != 0: + mat.swap_columns(0, j) + break + for j in range(1, mat.ncols()): mat.add_multiple_of_column(j, 0, -mat[0, j] / mat[0, 0]) vrs = H1.parent().variable_names()[1:] A1 = HyperplaneArrangements(self.base_ring(), names=vrs) - H1b = A1(mat.rows()[1:]) + mat_rows = mat.rows()[1:] + H1b = A1(mat_rows) + L1b = [_ for _ in H1b] + L2 = [] + for h in mat_rows: + h0 = A1(h)[0] + j = L1b.index(h0) + L2.append(j + 1) + perm2 = Permutation(L2) L = [] for j in range(1, r + 1): k0 = perm(j) @@ -3515,7 +3541,7 @@ def hyperplane_section(self, proj=True): L.append(k0) elif k0 > k: L.append(k0 - 1) - return (H1b, Permutation(L)) + return (H1b, Permutation(L) * perm2) P = self.intersection_poset(element_label="subspace") n1 = self.center().dimension() U = [p.linear_part().basis()[0] for p in P if p.dimension() == n1 + 1] From 4f19314a83cd5585b40678d2e981145536a51910 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 18 Apr 2023 17:49:18 +0200 Subject: [PATCH 087/122] solved hyperplane_section --- .../hyperplane_arrangement/arrangement.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index d73dc154980..2a46575bcff 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3497,7 +3497,7 @@ def hyperplane_section(self, proj=True): sage: a1, p = a.hyperplane_section(proj=False); a1 Arrangement of 12 hyperplanes of dimension 2 and rank 2 sage: p = Permutation([p(j) for j in [1 .. 12]] + [13]); p - [1, 5, 9, 12, 10, 11, 7, 8, 3, 4, 2, 6, 13] + [1, 5, 11, 12, 9, 10, 7, 8, 3, 4, 2, 6, 13] sage: ca1, p2 = a1.cone(permutation=True) sage: m1 = ca1.matroid() sage: q = p1.inverse() * p * p2 @@ -3517,6 +3517,12 @@ def hyperplane_section(self, proj=True): k = perm(r + 1) mat = Matrix(h.coefficients()[1:] for h in H1) mat.swap_rows(0, k - 1) + if k == 1: + trans = Permutation(range(1, r + 2)) + else: + trans = Permutation([k] + list(range(2, k)) + [1] + list(range(k + 1, r + 2))) + perm = perm * trans + perm = Permutation([perm(j) - 1 for j in range(1, r + 1)]) for j in range(mat.ncols()): if mat[0, j] != 0: mat.swap_columns(0, j) @@ -3534,14 +3540,7 @@ def hyperplane_section(self, proj=True): j = L1b.index(h0) L2.append(j + 1) perm2 = Permutation(L2) - L = [] - for j in range(1, r + 1): - k0 = perm(j) - if k0 < k: - L.append(k0) - elif k0 > k: - L.append(k0 - 1) - return (H1b, Permutation(L) * perm2) + return (H1b, perm * perm2) P = self.intersection_poset(element_label="subspace") n1 = self.center().dimension() U = [p.linear_part().basis()[0] for p in P if p.dimension() == n1 + 1] From bfd7180c827c9d9d133c6acf0dcdade606ea4ae4 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 24 Apr 2023 14:03:34 +0200 Subject: [PATCH 088/122] allow gfortran 13 --- build/pkgs/gfortran/spkg-configure.m4 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/pkgs/gfortran/spkg-configure.m4 b/build/pkgs/gfortran/spkg-configure.m4 index 1a2d6ba767c..d2841f740aa 100644 --- a/build/pkgs/gfortran/spkg-configure.m4 +++ b/build/pkgs/gfortran/spkg-configure.m4 @@ -86,8 +86,8 @@ SAGE_SPKG_CONFIGURE([gfortran], [ # Install our own gfortran if the system-provided one is older than gcc-4.8. SAGE_SHOULD_INSTALL_GFORTRAN([$FC is version $GFORTRAN_VERSION, which is quite old]) ], - [1[[3-9]].*], [ - # Install our own gfortran if the system-provided one is newer than 12.x. + [1[[4-9]].*], [ + # Install our own gfortran if the system-provided one is newer than 13.x. # See https://github.com/sagemath/sage/issues/29456, https://github.com/sagemath/sage/issues/31838 SAGE_MUST_INSTALL_GFORTRAN([$FC is version $GFORTRAN_VERSION, which is too recent for this version of Sage]) ]) From f0805349c4d6c4fc3ad36b962f7920a9155d0f77 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 25 Apr 2023 18:50:55 +0200 Subject: [PATCH 089/122] More permutations for hyperplane arrangements --- .../hyperplane_arrangement/arrangement.py | 102 +++++++++++++----- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 2a46575bcff..88a71e29377 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -633,7 +633,7 @@ def _richcmp_(self, other, op): """ return richcmp(self._hyperplanes, other._hyperplanes, op) - def union(self, other): + def union(self, other, permutation=False): r""" The union of ``self`` with ``other``. @@ -642,9 +642,18 @@ def union(self, other): - ``other`` -- a hyperplane arrangement or something that can be converted into a hyperplane arrangement + + - ``permutation`` -- (optional, default ``False``) If ``True`` + it computes the permutation relating the order of hyperplanes + in the input and in the output. + OUTPUT: - A new hyperplane arrangement. + A new hyperplane arrangement `L=(H'_1,\dots,H'_n)` if not ``permutation``. + If it is ``True``, a 2-tuple where the first term is `L` and the second is + a permutation `\sigma`. If ``self`` is composed by `H_1,\dots,H_r` and + ``other`` by `H_{r+1},\dots,H_{n}` (its order if it is a hyperplane + arrangement, or the list order otherwise) then `H_i=H'_{i^\sigma}`. EXAMPLES:: @@ -655,6 +664,17 @@ def union(self, other): Arrangement of 8 hyperplanes of dimension 2 and rank 2 sage: A | B # syntactic sugar Arrangement of 8 hyperplanes of dimension 2 and rank 2 + sage: A1 = H([1,1,0], [4,5,3]); A1 + Arrangement + sage: B =[(1, 1, 1), [2, 0, -1]] + sage: C, p = A1.union(B, permutation=True); C + Arrangement <-y + 2 | x + 1 | x + y + 1 | 5*x + 3*y + 4> + sage: p + [2, 4, 3, 1] + sage: C1,p1=A1.union(H(B), permutation=True); C == C1 + True + sage: p1 + [2, 4, 1, 3] A single hyperplane is coerced into a hyperplane arrangement if necessary:: @@ -670,9 +690,27 @@ def union(self, other): Arrangement of 6 hyperplanes of dimension 2 and rank 2 """ P = self.parent() - other = P(other) - hyperplanes = self._hyperplanes + other._hyperplanes - return P(*hyperplanes, backend=self._backend) + other_h = P(other) + if permutation: + r = self.n_hyperplanes() + L = list(range(1, r + 1)) + L1 = [_ for _ in other_h] + for h in other: + h0 = P(h)[0] + j = L1.index(h0) + L.append(r + j + 1) + p0 = Permutation(L) + hyperplanes = self._hyperplanes + other_h._hyperplanes + result = P(*hyperplanes, backend=self._backend) + if permutation: + L1 = [_ for _ in result] + L = [] + for h in hyperplanes: + j = L1.index(h) + L.append(j + 1) + p1 = Permutation(L) + return (result, p0 * p1) + return result add_hyperplane = union @@ -697,7 +735,7 @@ def plot(self, **kwds): def cone(self, variable='t', permutation=False): r""" - Return the cone over the hyperplane arrangement. + Return the cone over the hyperplane arrangement `H_1,\dots,H_n`. INPUT: @@ -709,14 +747,15 @@ def cone(self, variable='t', permutation=False): OUTPUT: - A new hyperplane arrangement. Its equations consist of - `[0, -d, a_1, \ldots, a_n]` for each `[d, a_1, \ldots, a_n]` in the - original arrangement and the equation `[0, 1, 0, \ldots, 0]`. - If ``permutation`` is set to ``True``, also a permutation - ``P`` such that if if the cone of the ``j``-th hyperplane of ``self`` - occupies the ``k``-th position in the cone, then ``P(j)=k``. In the source - the hyperplane at infinity is the last one. - + If permutation is ``True`` A new hyperplane arrangement `L`. + Its equations consist of `[0, -d, a_1, \ldots, a_n]` for each + `[d, a_1, \ldots, a_n]` in the original arrangement and the + equation `[0, 1, 0, \ldots, 0]` (maybe not in this order). + If ``permutation`` is set to ``True``, a tuple whose first term + is `L` and whose second term is a permutation `\sigma`. If the + cone is `L=(H'_1,\dots,H'_{n+1})` then for `1 \leq i \leq n` the + hyperplane `H_i` is associated to `H'_{i^\sigma}` and + `H'_{(n + 1)^\sigma}` is the hyperplane at infinity . .. WARNING:: @@ -776,8 +815,7 @@ def cone(self, variable='t', permutation=False): L.append(j + 1) P = Permutation(L) return (result, P) - else: - return result + return result @cached_method def intersection_poset(self, element_label="int"): @@ -1075,12 +1113,14 @@ def restriction(self, hyperplane, permutation=False): OUTPUT: - The restriction of the hyperplane arrangement to the given - ``hyperplane``. If ``permutation`` is set to ``True``, also a permutation - ``P`` such that if if the restriction of the ``j``-th hyperplane of ``self`` - (forgetting ``hyperplane``) occupies the ``k``-th position in the restriction, - then ``P(j)=k``. If several hyperplanes of ``self`` produce the same hyperplane - in the restriction, the second output is ``None``. + If ``permutation`` is ``False``, the restriction `\mathcal{A}_H` of the + hyperplane arrangement `\mathcal{A}` to the given ``hyperplane`` `H`. + If ``permutation`` is set to ``True``, also a permutation + `\sigma` defined as follows. If `\mathcal{A}=\{H_1 ,\dots, H_{i - 1}, H, + H_{i}, \dots, H_{n}\}` and `\mathcal{A}_H=\{H'_1 ,\dots, H'_{n}\}` + then the restriction of `H_i` is `H'_{i^\sigma}`. If several hyperplanes + of ``self`` produce the same hyperplane in the restriction, + the second output is ``None``. EXAMPLES:: @@ -1533,9 +1573,20 @@ def essentialization(self, permutation=False): has characteristic 0 is obtained by intersecting the hyperplanes by the space spanned by their normal vectors. + INPUT: + + - ``permutation`` -- (optional, default ``False``) If ``True`` + it computes the permutation relating the order of hyperplanes + in the input and in the output. + OUTPUT: - The essentialization as a new hyperplane arrangement. + The essentialization `\mathcal{A}'` of `\mathcal{A}` as a + new hyperplane arrangement if not ``permutation``. If it is + ``True``, a 2-tuple where the first term is `\mathcal{A}'` and + the second is a permutation `\sigma` as follows. If + `\mathcal{A} = \{H_1, \dots, H_n\}` and + `\mathcal{A}' = \{H'_1, \dots, H'_r\}` then `H_i=H'_{i^\sigma}`. EXAMPLES:: @@ -3455,7 +3506,10 @@ def hyperplane_section(self, proj=True): OUTPUT: - An arrangement obtained by intersecting with a generic hyperplane. + An arrangement `\mathcal{A}` obtained by intersecting with a + generic hyperplane and a permutation `\sigma`. If ``self`` is + `\{H_1 ,\dots, H_{n}\}` and `\mathcal{A}=\{H'_1 ,\dots, H'_{n}\}` + then the restriction of `H_i` is `H'_{i^\sigma}`. EXAMPLES:: From bbf00de7231acb480cf2f33362ce5029e0045283 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 1 May 2023 01:24:59 +0200 Subject: [PATCH 090/122] add optionals --- .../hyperplane_arrangement/arrangement.py | 934 +++++++++--------- 1 file changed, 477 insertions(+), 457 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 88a71e29377..b60fc115765 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -60,41 +60,42 @@ The default base field is `\QQ`, the rational numbers. Finite fields are also supported:: - sage: H. = HyperplaneArrangements(GF(5)) # optional - sage.libs.pari - sage: a = H([(1,2,3), 4], [(5,6,7), 8]); a # optional - sage.libs.pari + sage: H. = HyperplaneArrangements(GF(5)) # optional - sage.rings.finite_rings + sage: a = H([(1,2,3), 4], [(5,6,7), 8]); a # optional - sage.rings.finite_rings Arrangement Number fields are also possible:: - sage: x = var('x') - sage: NF. = NumberField(x**4 - 5*x**2 + 5, embedding=1.90) # optional - sage.rings.number_field - sage: H. = HyperplaneArrangements(NF) # optional - sage.rings.number_field - sage: A = H([[(-a**3 + 3*a, -a**2 + 4), 1], [(a**3 - 4*a, -1), 1], # optional - sage.rings.number_field + sage: x = polygen(QQ, 'x') + sage: NF. = NumberField(x**4 - 5*x**2 + 5, embedding=1.90) # optional - sage.rings.number_field + sage: H. = HyperplaneArrangements(NF) # optional - sage.rings.number_field + sage: A = H([[(-a**3 + 3*a, -a**2 + 4), 1], [(a**3 - 4*a, -1), 1], # optional - sage.rings.number_field ....: [(0, 2*a**2 - 6), 1], [(-a**3 + 4*a, -1), 1], ....: [(a**3 - 3*a, -a**2 + 4), 1]]) - sage: A # optional - sage.rings.number_field + sage: A # optional - sage.rings.number_field Arrangement of 5 hyperplanes of dimension 2 and rank 2 - sage: A.base_ring() # optional - sage.rings.number_field - Number Field in a with defining polynomial x^4 - 5*x^2 + 5 with a = 1.902113032590308? + sage: A.base_ring() # optional - sage.rings.number_field + Number Field in a with defining polynomial x^4 - 5*x^2 + 5 + with a = 1.902113032590308? Notation (iii): a list or tuple of hyperplanes:: - sage: H. = HyperplaneArrangements(GF(5)) # optional - sage.libs.pari - sage: k = [x+i for i in range(4)]; k # optional - sage.libs.pari + sage: H. = HyperplaneArrangements(GF(5)) # optional - sage.rings.finite_rings + sage: k = [x+i for i in range(4)]; k # optional - sage.rings.finite_rings [Hyperplane x + 0*y + 0*z + 0, Hyperplane x + 0*y + 0*z + 1, Hyperplane x + 0*y + 0*z + 2, Hyperplane x + 0*y + 0*z + 3] - sage: H(k) # optional - sage.libs.pari + sage: H(k) # optional - sage.rings.finite_rings Arrangement Notation (iv): using the library of arrangements:: - sage: hyperplane_arrangements.braid(4) + sage: hyperplane_arrangements.braid(4) # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 4 and rank 3 - sage: hyperplane_arrangements.semiorder(3) + sage: hyperplane_arrangements.semiorder(3) # optional - sage.combinat Arrangement of 6 hyperplanes of dimension 3 and rank 2 - sage: hyperplane_arrangements.graphical(graphs.PetersenGraph()) + sage: hyperplane_arrangements.graphical(graphs.PetersenGraph()) # optional - sage.graphs Arrangement of 15 hyperplanes of dimension 10 and rank 9 - sage: hyperplane_arrangements.Ish(5) + sage: hyperplane_arrangements.Ish(5) # optional - sage.combinat Arrangement of 20 hyperplanes of dimension 5 and rank 4 Notation (v): from the bounding hyperplanes of a polyhedron:: @@ -106,23 +107,23 @@ New arrangements from old:: - sage: a = hyperplane_arrangements.braid(3) - sage: b = a.add_hyperplane([4, 1, 2, 3]) - sage: b + sage: a = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: b = a.add_hyperplane([4, 1, 2, 3]) # optional - sage.graphs + sage: b # optional - sage.graphs Arrangement - sage: c = b.deletion([4, 1, 2, 3]) - sage: a == c + sage: c = b.deletion([4, 1, 2, 3]) # optional - sage.graphs + sage: a == c # optional - sage.graphs True - sage: a = hyperplane_arrangements.braid(3) - sage: b = a.union(hyperplane_arrangements.semiorder(3)) - sage: b == a | hyperplane_arrangements.semiorder(3) # alternate syntax + sage: a = hyperplane_arrangements.braid(3) # optional - sage.graphs sage.combinat + sage: b = a.union(hyperplane_arrangements.semiorder(3)) # optional - sage.graphs sage.combinat + sage: b == a | hyperplane_arrangements.semiorder(3) # alternate syntax # optional - sage.graphs sage.combinat True - sage: b == hyperplane_arrangements.Catalan(3) + sage: b == hyperplane_arrangements.Catalan(3) # optional - sage.graphs sage.combinat True - - sage: a + sage: a # optional - sage.graphs sage.combinat Arrangement + sage: a = hyperplane_arrangements.coordinate(4) sage: h = a.hyperplanes()[0] sage: b = a.restriction(h) @@ -138,24 +139,24 @@ normal space (actually, it is a bit more complicated over finite fields):: - sage: a = hyperplane_arrangements.braid(4); a + sage: a = hyperplane_arrangements.braid(4); a # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 4 and rank 3 - sage: a.is_essential() + sage: a.is_essential() # optional - sage.graphs False - sage: a.rank() < a.dimension() # double-check + sage: a.rank() < a.dimension() # double-check # optional - sage.graphs True - sage: a.essentialization() + sage: a.essentialization() # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 3 and rank 3 The connected components of the complement of the hyperplanes of an arrangement in `\RR^n` are called the *regions* of the arrangement:: - sage: a = hyperplane_arrangements.semiorder(3) - sage: b = a.essentialization(); b + sage: a = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: b = a.essentialization(); b # optional - sage.combinat Arrangement of 6 hyperplanes of dimension 2 and rank 2 - sage: b.n_regions() + sage: b.n_regions() # optional - sage.combinat 19 - sage: b.regions() + sage: b.regions() # optional - sage.combinat (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 6 vertices, A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, @@ -183,9 +184,9 @@ A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices, A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices) - sage: b.n_bounded_regions() + sage: b.n_bounded_regions() # optional - sage.combinat 7 - sage: a.unbounded_regions() + sage: a.unbounded_regions() # optional - sage.combinat (A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices, 1 ray, 1 line, A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, @@ -202,13 +203,13 @@ The distance between regions is defined as the number of hyperplanes separating them. For example:: - sage: r1 = b.regions()[0] - sage: r2 = b.regions()[1] - sage: b.distance_between_regions(r1, r2) + sage: r1 = b.regions()[0] # optional - sage.combinat + sage: r2 = b.regions()[1] # optional - sage.combinat + sage: b.distance_between_regions(r1, r2) # optional - sage.combinat 1 - sage: [hyp for hyp in b if b.is_separating_hyperplane(r1, r2, hyp)] + sage: [hyp for hyp in b if b.is_separating_hyperplane(r1, r2, hyp)] # optional - sage.combinat [Hyperplane 2*t1 + t2 + 1] - sage: b.distance_enumerator(r1) # generating function for distances from r1 + sage: b.distance_enumerator(r1) # generating function for distances from r1 # optional - sage.combinat 6*x^3 + 6*x^2 + 6*x + 1 .. NOTE:: @@ -222,11 +223,11 @@ ordered by reverse inclusion. It includes the ambient space of the arrangement (as the intersection over the empty set):: - sage: a = hyperplane_arrangements.braid(3) - sage: p = a.intersection_poset() - sage: p.is_ranked() + sage: a = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: p = a.intersection_poset() # optional - sage.graphs + sage: p.is_ranked() # optional - sage.graphs True - sage: p.order_polytope() + sage: p.order_polytope() # optional - sage.graphs A 5-dimensional polyhedron in ZZ^5 defined as the convex hull of 10 vertices The characteristic polynomial is a basic invariant of a hyperplane @@ -262,12 +263,12 @@ Miscellaneous methods (see documentation for an explanation):: sage: a = hyperplane_arrangements.semiorder(3) - sage: a.has_good_reduction(5) + sage: a.has_good_reduction(5) # optional - sage.rings.finite_rings True - sage: b = a.change_ring(GF(5)) # optional - sage.libs.pari + sage: b = a.change_ring(GF(5)) # optional - sage.rings.finite_rings sage: pa = a.intersection_poset() - sage: pb = b.intersection_poset() # optional - sage.libs.pari - sage: pa.is_isomorphic(pb) # optional - sage.libs.pari + sage: pb = b.intersection_poset() # optional - sage.rings.finite_rings + sage: pa.is_isomorphic(pb) # optional - sage.rings.finite_rings True sage: a.face_vector() (0, 12, 30, 19) @@ -394,13 +395,13 @@ def __init__(self, parent, hyperplanes, check=True, backend=None): It is possible to specify a backend for polyhedral computations:: - sage: R. = QuadraticField(5) # optional - sage.rings.number_field - sage: H = HyperplaneArrangements(R, names='xyz') # optional - sage.rings.number_field - sage: x, y, z = H.gens() # optional - sage.rings.number_field - sage: A = H(sqrt5*x + 2*y + 3*z, backend='normaliz') # optional - sage.rings.number_field - sage: A.backend() # optional - sage.rings.number_field + sage: R. = QuadraticField(5) # optional - sage.rings.number_field + sage: H = HyperplaneArrangements(R, names='xyz') # optional - sage.rings.number_field + sage: x, y, z = H.gens() # optional - sage.rings.number_field + sage: A = H(sqrt5*x + 2*y + 3*z, backend='normaliz') # optional - sage.rings.number_field + sage: A.backend() # optional - sage.rings.number_field 'normaliz' - sage: A.regions()[0].backend() # optional - pynormaliz # optional - sage.rings.number_field + sage: A.regions()[0].backend() # optional - pynormaliz sage.rings.number_field 'normaliz' """ super().__init__(parent) @@ -424,8 +425,8 @@ def _first_ngens(self, n): EXAMPLES:: - sage: a. = hyperplane_arrangements.braid(3) # indirect doctest - sage: (x, y) == a._first_ngens(2) + sage: a. = hyperplane_arrangements.braid(3) # indirect doctest # optional - sage.graphs + sage: (x, y) == a._first_ngens(2) # optional - sage.graphs True """ return self.parent()._first_ngens(n) @@ -570,14 +571,14 @@ def rank(self): sage: A.rank() 2 - sage: B = hyperplane_arrangements.braid(3) - sage: B.hyperplanes() + sage: B = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: B.hyperplanes() # optional - sage.graphs (Hyperplane 0*t0 + t1 - t2 + 0, Hyperplane t0 - t1 + 0*t2 + 0, Hyperplane t0 + 0*t1 - t2 + 0) - sage: B.dimension() + sage: B.dimension() # optional - sage.graphs 3 - sage: B.rank() + sage: B.rank() # optional - sage.graphs 2 sage: p = polytopes.simplex(5, project=True) @@ -727,7 +728,7 @@ def plot(self, **kwds): EXAMPLES:: sage: L. = HyperplaneArrangements(QQ) - sage: L(x, y, x+y-2).plot() # optional - sage.plot + sage: L(x, y, x+y-2).plot() # optional - sage.plot Graphics object consisting of 3 graphics primitives """ from sage.geometry.hyperplane_arrangement.plot import plot @@ -768,20 +769,20 @@ def cone(self, variable='t', permutation=False): EXAMPLES:: - sage: a. = hyperplane_arrangements.semiorder(3) - sage: b = a.cone() - sage: a.characteristic_polynomial().factor() + sage: a. = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: b = a.cone() # optional - sage.combinat + sage: a.characteristic_polynomial().factor() # optional - sage.combinat x * (x^2 - 6*x + 12) - sage: b.characteristic_polynomial().factor() + sage: b.characteristic_polynomial().factor() # optional - sage.combinat (x - 1) * x * (x^2 - 6*x + 12) - sage: a.hyperplanes() + sage: a.hyperplanes() # optional - sage.combinat (Hyperplane 0*x + y - z - 1, Hyperplane 0*x + y - z + 1, Hyperplane x - y + 0*z - 1, Hyperplane x - y + 0*z + 1, Hyperplane x + 0*y - z - 1, Hyperplane x + 0*y - z + 1) - sage: b.hyperplanes() + sage: b.hyperplanes() # optional - sage.combinat (Hyperplane -t + 0*x + y - z + 0, Hyperplane -t + x - y + 0*z + 0, Hyperplane -t + x + 0*y - z + 0, @@ -789,12 +790,12 @@ def cone(self, variable='t', permutation=False): Hyperplane t + 0*x + y - z + 0, Hyperplane t + x - y + 0*z + 0, Hyperplane t + x + 0*y - z + 0) - sage: b1, P = a.cone(permutation=True) - sage: b1 == b + sage: b1, P = a.cone(permutation=True) # optional - sage.combinat + sage: b1 == b # optional - sage.combinat True - sage: P + sage: P # optional - sage.combinat [1, 5, 2, 6, 3, 7, 4] - sage: b1[P(b1.n_hyperplanes())-1] + sage: b1[P(b1.n_hyperplanes())-1] # optional - sage.combinat Hyperplane t + 0*x + 0*y + 0*z + 0 """ hyperplanes = [] @@ -846,21 +847,21 @@ def intersection_poset(self, element_label="int"): of hyperplanes of the arrangement. :: sage: A = hyperplane_arrangements.coordinate(2) - sage: L = A.intersection_poset(); L + sage: L = A.intersection_poset(); L # optional - sage.combinat Finite poset containing 4 elements - sage: sorted(L) + sage: sorted(L) # optional - sage.combinat [0, 1, 2, 3] - sage: L.level_sets() + sage: L.level_sets() # optional - sage.combinat [[0], [1, 2], [3]] :: - sage: A = hyperplane_arrangements.semiorder(3) - sage: L = A.intersection_poset(); L + sage: A = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: L = A.intersection_poset(); L # optional - sage.combinat Finite poset containing 19 elements - sage: sorted(L) + sage: sorted(L) # optional - sage.combinat [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18] - sage: [sorted(level_set) for level_set in L.level_sets()] + sage: [sorted(level_set) for level_set in L.level_sets()] # optional - sage.combinat [[0], [1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]] By passing the argument ``element_label="subset"``, each element of the @@ -868,9 +869,9 @@ def intersection_poset(self, element_label="int"): whose intersection is said element. The index of a hyperplane is its index in ``self.hyperplanes()``. :: - sage: A = hyperplane_arrangements.semiorder(3) - sage: L = A.intersection_poset(element_label='subset') - sage: [sorted(level, key=sorted) for level in L.level_sets()] + sage: A = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: L = A.intersection_poset(element_label='subset') # optional - sage.combinat + sage: [sorted(level, key=sorted) for level in L.level_sets()] # optional - sage.combinat [[{}], [{0}, {1}, {2}, {3}, {4}, {5}], [{0, 2}, {0, 3}, {0, 4}, {0, 5}, {1, 2}, {1, 3}, {1, 4}, {1, 5}, {2, 4}, {2, 5}, {3, 4}, {3, 5}]] @@ -878,31 +879,32 @@ def intersection_poset(self, element_label="int"): :: sage: H. = HyperplaneArrangements(QQ) - sage: A = H((y , (y-1) , (y+1) , (x - y) , (x + y))) - sage: L = A.intersection_poset(element_label='subset') - sage: sorted(L, key=sorted) + sage: A = H((y, y-1, y+1, x-y, x+y)) + sage: L = A.intersection_poset(element_label='subset') # optional - sage.combinat + sage: sorted(L, key=sorted) # optional - sage.combinat [{}, {0}, {0, 3}, {0, 4}, {1}, {1, 3, 4}, {2}, {2, 3}, {2, 4}, {3}, {4}] One can instead use affine subspaces as elements, which is what is used to compute the poset in the first place:: sage: A = hyperplane_arrangements.coordinate(2) - sage: L = A.intersection_poset(element_label='subspace'); L + sage: L = A.intersection_poset(element_label='subspace'); L # optional - sage.combinat Finite poset containing 4 elements - sage: sorted(L, key=lambda S: (S.dimension(), S.linear_part().basis_matrix())) + sage: sorted(L, key=lambda S: (S.dimension(), # optional - sage.combinat + ....: S.linear_part().basis_matrix())) [Affine space p + W where: p = (0, 0) W = Vector space of degree 2 and dimension 0 over Rational Field - Basis matrix: - [], Affine space p + W where: + Basis matrix: [], + Affine space p + W where: p = (0, 0) W = Vector space of degree 2 and dimension 1 over Rational Field - Basis matrix: - [0 1], Affine space p + W where: + Basis matrix: [0 1], + Affine space p + W where: p = (0, 0) W = Vector space of degree 2 and dimension 1 over Rational Field - Basis matrix: - [1 0], Affine space p + W where: + Basis matrix: [1 0], + Affine space p + W where: p = (0, 0) W = Vector space of dimension 2 over Rational Field] """ @@ -969,7 +971,7 @@ def _slow_characteristic_polynomial(self): EXAMPLES:: sage: a = hyperplane_arrangements.coordinate(2) - sage: a._slow_characteristic_polynomial() + sage: a._slow_characteristic_polynomial() # optional - sage.combinat x^2 - 2*x + 1 """ from sage.rings.polynomial.polynomial_ring import polygen @@ -1082,9 +1084,9 @@ def deletion(self, hyperplanes): Checks that deletion preserves the backend:: - sage: H = HyperplaneArrangements(QQ,names='xyz') + sage: H = HyperplaneArrangements(QQ, names='xyz') sage: x,y,z = H.gens() - sage: h1,h2 = [1*x+2*y+3*z,3*x+2*y+1*z] + sage: h1,h2 = [1*x+2*y+3*z, 3*x+2*y+1*z] sage: A = H(h1,h2,backend='normaliz') sage: A.deletion(h2).backend() 'normaliz' @@ -1124,26 +1126,26 @@ def restriction(self, hyperplane, permutation=False): EXAMPLES:: - sage: A. = hyperplane_arrangements.braid(4); A + sage: A. = hyperplane_arrangements.braid(4); A # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 4 and rank 3 - sage: H = A[0]; H + sage: H = A[0]; H # optional - sage.graphs Hyperplane 0*u + 0*x + y - z + 0 - sage: R = A.restriction(H); R + sage: R = A.restriction(H); R # optional - sage.graphs Arrangement - sage: A.restriction(H, permutation=True) + sage: A.restriction(H, permutation=True) # optional - sage.graphs (Arrangement , None) - sage: A.add_hyperplane(z).restriction(z, permutation=True) + sage: A.add_hyperplane(z).restriction(z, permutation=True) # optional - sage.graphs (Arrangement of 6 hyperplanes of dimension 3 and rank 3, [1, 2, 3, 4, 5, 6]) - sage: A.add_hyperplane(u).restriction(u, permutation=True) + sage: A.add_hyperplane(u).restriction(u, permutation=True) # optional - sage.graphs (Arrangement of 6 hyperplanes of dimension 3 and rank 3, [2, 4, 5, 6, 3, 1]) - sage: D = A.deletion(H); D + sage: D = A.deletion(H); D # optional - sage.graphs Arrangement of 5 hyperplanes of dimension 4 and rank 3 - sage: ca = A.characteristic_polynomial() - sage: cr = R.characteristic_polynomial() - sage: cd = D.characteristic_polynomial() - sage: ca + sage: ca = A.characteristic_polynomial() # optional - sage.graphs + sage: cr = R.characteristic_polynomial() # optional - sage.graphs + sage: cd = D.characteristic_polynomial() # optional - sage.graphs + sage: ca # optional - sage.graphs x^4 - 6*x^3 + 11*x^2 - 6*x - sage: cd - cr + sage: cd - cr # optional - sage.graphs x^4 - 6*x^3 + 11*x^2 - 6*x .. SEEALSO:: @@ -1154,9 +1156,9 @@ def restriction(self, hyperplane, permutation=False): Checks that restriction preserves the backend:: - sage: H = HyperplaneArrangements(QQ,names='xyz') + sage: H = HyperplaneArrangements(QQ, names='xyz') sage: x,y,z = H.gens() - sage: h1,h2 = [1*x+2*y+3*z,3*x+2*y+1*z] + sage: h1,h2 = [1*x+2*y+3*z, 3*x+2*y+1*z] sage: A = H(h1, h2, backend='normaliz') sage: A.restriction(h2).backend() 'normaliz' @@ -1224,16 +1226,16 @@ def change_ring(self, base_ring): sage: H. = HyperplaneArrangements(QQ) sage: A = H([(1,1), 0], [(2,3), -1]) - sage: A.change_ring(FiniteField(2)) # optional - sage.libs.pari + sage: A.change_ring(FiniteField(2)) # optional - sage.rings.finite_rings Arrangement TESTS: Checks that changing the ring preserves the backend:: - sage: H = HyperplaneArrangements(QQ,names='xyz') + sage: H = HyperplaneArrangements(QQ, names='xyz') sage: x,y,z = H.gens() - sage: h1,h2 = [1*x+2*y+3*z,3*x+2*y+1*z] + sage: h1, h2 = [1*x+2*y+3*z, 3*x+2*y+1*z] sage: A = H(h1, h2, backend='normaliz') sage: A.change_ring(RDF).backend() 'normaliz' @@ -1252,16 +1254,16 @@ def n_regions(self): EXAMPLES:: - sage: A = hyperplane_arrangements.semiorder(3) - sage: A.n_regions() + sage: A = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: A.n_regions() # optional - sage.combinat 19 TESTS:: sage: H. = HyperplaneArrangements(QQ) sage: A = H([(1,1), 0], [(2,3), -1], [(4,5), 3]) - sage: B = A.change_ring(FiniteField(7)) # optional - sage.libs.pari - sage: B.n_regions() # optional - sage.libs.pari + sage: B = A.change_ring(FiniteField(7)) # optional - sage.rings.finite_rings + sage: B.n_regions() # optional - sage.rings.finite_rings Traceback (most recent call last): ... TypeError: base field must have characteristic zero @@ -1303,16 +1305,16 @@ def n_bounded_regions(self): EXAMPLES:: - sage: A = hyperplane_arrangements.semiorder(3) - sage: A.n_bounded_regions() + sage: A = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: A.n_bounded_regions() # optional - sage.combinat 7 TESTS:: sage: H. = HyperplaneArrangements(QQ) sage: A = H([(1,1),0], [(2,3),-1], [(4,5),3]) - sage: B = A.change_ring(FiniteField(7)) # optional - sage.libs.pari - sage: B.n_bounded_regions() # optional - sage.libs.pari + sage: B = A.change_ring(FiniteField(7)) # optional - sage.rings.finite_rings + sage: B.n_bounded_regions() # optional - sage.rings.finite_rings Traceback (most recent call last): ... TypeError: base field must have characteristic zero @@ -1342,15 +1344,15 @@ def has_good_reduction(self, p): EXAMPLES:: - sage: a = hyperplane_arrangements.semiorder(3) - sage: a.has_good_reduction(5) # optional - sage.libs.pari + sage: a = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: a.has_good_reduction(5) # optional - sage.combinat sage.rings.finite_rings True - sage: a.has_good_reduction(3) # optional - sage.libs.pari + sage: a.has_good_reduction(3) # optional - sage.combinat sage.rings.finite_rings False - sage: b = a.change_ring(GF(3)) # optional - sage.libs.pari - sage: a.characteristic_polynomial() + sage: b = a.change_ring(GF(3)) # optional - sage.combinat sage.rings.finite_rings + sage: a.characteristic_polynomial() # optional - sage.combinat sage.rings.finite_rings x^3 - 6*x^2 + 12*x - sage: b.characteristic_polynomial() # not equal to that for a # optional - sage.libs.pari + sage: b.characteristic_polynomial() # not equal to that for a # optional - sage.combinat sage.rings.finite_rings x^3 - 6*x^2 + 10*x """ if self.base_ring() != QQ: @@ -1373,11 +1375,11 @@ def is_linear(self): EXAMPLES:: - sage: a = hyperplane_arrangements.semiorder(3) - sage: a.is_linear() + sage: a = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: a.is_linear() # optional - sage.combinat False - sage: b = hyperplane_arrangements.braid(3) - sage: b.is_linear() + sage: b = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: b.is_linear() # optional - sage.graphs True sage: H. = HyperplaneArrangements(QQ) @@ -1439,8 +1441,8 @@ def is_central(self, certificate=False): EXAMPLES:: - sage: a = hyperplane_arrangements.braid(2) - sage: a.is_central() + sage: a = hyperplane_arrangements.braid(2) # optional - sage.graphs + sage: a.is_central() # optional - sage.graphs True The Catalan arrangement in dimension 3 is not central:: @@ -1451,11 +1453,11 @@ def is_central(self, certificate=False): The empty arrangement in dimension 5 is central:: - sage: H = HyperplaneArrangements(QQ,names=tuple(['x'+str(i) for i in range(7)])) + sage: H = HyperplaneArrangements(QQ, names=tuple(['x'+str(i) for i in range(7)])) sage: c = H() sage: c.is_central(certificate=True) - (True, A 7-dimensional polyhedron in QQ^7 defined as the convex - hull of 1 vertex and 7 lines) + (True, A 7-dimensional polyhedron in QQ^7 defined + as the convex hull of 1 vertex and 7 lines) """ R = self.base_ring() # If there are no hyperplanes in the arrangement, @@ -1515,15 +1517,15 @@ def center(self): The Shi arrangement in dimension 3 has an empty center:: - sage: A = hyperplane_arrangements.Shi(3) - sage: A.center() + sage: A = hyperplane_arrangements.Shi(3) # optional - sage.combinat + sage: A.center() # optional - sage.combinat The empty polyhedron in QQ^3 The Braid arrangement in dimension 3 has a center that is neither empty nor full-dimensional:: - sage: A = hyperplane_arrangements.braid(3) - sage: A.center() + sage: A = hyperplane_arrangements.braid(3) # optional - sage.combinat + sage: A.center() # optional - sage.combinat A 1-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 1 line """ return self.is_central(certificate=True)[1] @@ -1544,16 +1546,16 @@ def is_simplicial(self): EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) - sage: A = H([[0,1,1,1],[0,1,2,3]]) + sage: A = H([[0,1,1,1], [0,1,2,3]]) sage: A.is_simplicial() True - sage: A = H([[0,1,1,1],[0,1,2,3],[0,1,3,2]]) + sage: A = H([[0,1,1,1], [0,1,2,3], [0,1,3,2]]) sage: A.is_simplicial() True - sage: A = H([[0,1,1,1],[0,1,2,3],[0,1,3,2],[0,2,1,3]]) + sage: A = H([[0,1,1,1], [0,1,2,3], [0,1,3,2], [0,2,1,3]]) sage: A.is_simplicial() False - sage: hyperplane_arrangements.braid(3).is_simplicial() + sage: hyperplane_arrangements.braid(3).is_simplicial() # optional - sage.graphs True """ # if the arr is not essential, grab the essential version and check there. @@ -1590,10 +1592,10 @@ def essentialization(self, permutation=False): EXAMPLES:: - sage: a = hyperplane_arrangements.braid(3) - sage: a.is_essential() + sage: a = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: a.is_essential() # optional - sage.graphs False - sage: a.essentialization() + sage: a.essentialization() # optional - sage.graphs Arrangement sage: H. = HyperplaneArrangements(QQ) @@ -1606,13 +1608,13 @@ def essentialization(self, permutation=False): Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x - sage: H. = HyperplaneArrangements(GF(2)) # optional - sage.libs.pari - sage: C = H([(1,1),1], [(1,1),0]) # optional - sage.libs.pari - sage: C.essentialization() # optional - sage.libs.pari + sage: H. = HyperplaneArrangements(GF(2)) # optional - sage.rings.finite_rings + sage: C = H([(1,1),1], [(1,1),0]) # optional - sage.rings.finite_rings + sage: C.essentialization() # optional - sage.rings.finite_rings Arrangement - sage: h = hyperplane_arrangements.semiorder(4) - sage: h.essentialization() + sage: h = hyperplane_arrangements.semiorder(4) # optional - sage.combinat + sage: h.essentialization() # optional - sage.combinat Arrangement of 12 hyperplanes of dimension 3 and rank 3 TESTS:: @@ -1709,9 +1711,9 @@ def sign_vector(self, p): TESTS:: - sage: H. = HyperplaneArrangements(GF(3)) # optional - sage.libs.pari - sage: A = H(x, y) # optional - sage.libs.pari - sage: A.sign_vector([1, 2]) # optional - sage.libs.pari + sage: H. = HyperplaneArrangements(GF(3)) # optional - sage.rings.finite_rings + sage: A = H(x, y) # optional - sage.rings.finite_rings + sage: A.sign_vector([1, 2]) # optional - sage.rings.finite_rings Traceback (most recent call last): ... ValueError: characteristic must be zero @@ -1738,8 +1740,8 @@ def face_vector(self): EXAMPLES:: - sage: A = hyperplane_arrangements.Shi(3) - sage: A.face_vector() + sage: A = hyperplane_arrangements.Shi(3) # optional - sage.combinat + sage: A.face_vector() # optional - sage.combinat (0, 6, 21, 16) """ m = self.whitney_data()[0] @@ -1778,7 +1780,7 @@ def _parallel_hyperplanes(self): (Hyperplane x + 2*y + 0, (1, 2), 0), (Hyperplane 2*x + 4*y + 1, (1, 2), 1/2)) - sage: hyperplane_arrangements.Shi(3)._parallel_hyperplanes() + sage: hyperplane_arrangements.Shi(3)._parallel_hyperplanes() # optional - sage.combinat (((Hyperplane 0*t0 + t1 - t2 - 1, (0, 1, -1), -1), (Hyperplane 0*t0 + t1 - t2 + 0, (0, 1, -1), 0)), ((Hyperplane t0 - t1 + 0*t2 - 1, (1, -1, 0), -1), @@ -1821,21 +1823,21 @@ def vertices(self, exclude_sandwiched=False): EXAMPLES:: - sage: A = hyperplane_arrangements.Shi(3).essentialization() - sage: A.dimension() + sage: A = hyperplane_arrangements.Shi(3).essentialization() # optional - sage.combinat + sage: A.dimension() # optional - sage.combinat 2 - sage: A.face_vector() + sage: A.face_vector() # optional - sage.combinat (6, 21, 16) - sage: A.vertices() + sage: A.vertices() # optional - sage.combinat ((-2/3, 1/3), (-1/3, -1/3), (0, -1), (0, 0), (1/3, -2/3), (2/3, -1/3)) - sage: point2d(A.vertices(), size=20) + A.plot() # optional - sage.plot + sage: point2d(A.vertices(), size=20) + A.plot() # optional - sage.combinat sage.plot Graphics object consisting of 7 graphics primitives sage: H. = HyperplaneArrangements(QQ) sage: chessboard = [] sage: N = 8 - sage: for x0 in range(N+1): - ....: for y0 in range(N+1): + sage: for x0 in range(N + 1): + ....: for y0 in range(N + 1): ....: chessboard.extend([x-x0, y-y0]) sage: chessboard = H(chessboard) sage: len(chessboard.vertices()) @@ -1898,8 +1900,8 @@ def _make_region(self, hyperplanes): Checks that it creates the regions with the appropriate backend:: - sage: h = H(x,backend='normaliz') # optional - pynormaliz - sage: h._make_region([x, 1-x, y, 1-y]).backend() # optional - pynormaliz + sage: h = H(x,backend='normaliz') # optional - pynormaliz + sage: h._make_region([x, 1-x, y, 1-y]).backend() # optional - pynormaliz 'normaliz' """ ieqs = [h.dense_coefficient_list() for h in hyperplanes] @@ -1924,23 +1926,29 @@ def regions(self): EXAMPLES:: - sage: a = hyperplane_arrangements.braid(2) - sage: a.regions() - (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line) + sage: a = hyperplane_arrangements.braid(2) # optional - sage.graphs + sage: a.regions() # optional - sage.graphs + (A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, 1 ray, 1 line, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, 1 ray, 1 line) sage: H. = HyperplaneArrangements(QQ) sage: A = H(x, y+1) sage: A.regions() - (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays) + (A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays) sage: chessboard = [] sage: N = 8 - sage: for x0 in range(N+1): - ....: for y0 in range(N+1): + sage: for x0 in range(N + 1): + ....: for y0 in range(N + 1): ....: chessboard.extend([x-x0, y-y0]) sage: chessboard = H(chessboard) sage: len(chessboard.bounded_regions()) # long time, 359 ms on a Core i7 @@ -1973,20 +1981,21 @@ def regions(self): It is possible to specify the backend:: - sage: K. = CyclotomicField(9) - sage: L. = NumberField((q+q**(-1)).minpoly(),embedding = AA(q+q**-1)) - sage: norms = [[1,1/3*(-2*r9**2-r9+1),0], - ....: [1,-r9**2-r9,0], - ....: [1,-r9**2+1,0], - ....: [1,-r9**2,0], - ....: [1,r9**2-4,-r9**2+3]] - sage: H. = HyperplaneArrangements(L) - sage: A = H(backend='normaliz') - sage: for v in norms: + sage: K. = CyclotomicField(9) # optional - sage.rings.number_field + sage: L. = NumberField((q + q**(-1)).minpoly(), # optional - sage.rings.number_field + ....: embedding=AA(q + q**-1)) + sage: norms = [[1, 1/3*(-2*r9**2-r9+1), 0], # optional - sage.rings.number_field + ....: [1, -r9**2 - r9, 0], + ....: [1, -r9**2 + 1, 0], + ....: [1, -r9**2, 0], + ....: [1, r9**2 - 4, -r9**2+3]] + sage: H. = HyperplaneArrangements(L) # optional - sage.rings.number_field + sage: A = H(backend='normaliz') # optional - sage.rings.number_field + sage: for v in norms: # optional - sage.rings.number_field ....: a,b,c = v ....: A = A.add_hyperplane(a*x + b*y + c*z) - sage: R = A.regions() # optional - pynormaliz - sage: R[0].backend() # optional - pynormaliz + sage: R = A.regions() # optional - pynormaliz sage.rings.number_field + sage: R[0].backend() # optional - pynormaliz sage.rings.number_field 'normaliz' TESTS:: @@ -1994,7 +2003,8 @@ def regions(self): sage: K. = HyperplaneArrangements(QQ) sage: A = K() sage: A.regions() - (A 5-dimensional polyhedron in QQ^5 defined as the convex hull of 1 vertex and 5 lines,) + (A 5-dimensional polyhedron in QQ^5 + defined as the convex hull of 1 vertex and 5 lines,) """ if self.base_ring().characteristic() != 0: raise ValueError('base field must have characteristic zero') @@ -2094,24 +2104,24 @@ def poset_of_regions(self, B=None, numbered_labels=True): EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) - sage: A = H([[0,1,1,1],[0,1,2,3]]) - sage: A.poset_of_regions() + sage: A = H([[0,1,1,1], [0,1,2,3]]) + sage: A.poset_of_regions() # optional - sage.combinat Finite poset containing 4 elements - sage: A = hyperplane_arrangements.braid(3) - sage: A.poset_of_regions() + sage: A = hyperplane_arrangements.braid(3) # optional - sage.combinat sage.graphs + sage: A.poset_of_regions() # optional - sage.combinat sage.graphs Finite poset containing 6 elements - sage: A.poset_of_regions(numbered_labels=False) + sage: A.poset_of_regions(numbered_labels=False) # optional - sage.combinat sage.graphs Finite poset containing 6 elements - sage: A = hyperplane_arrangements.braid(4) - sage: A.poset_of_regions() + sage: A = hyperplane_arrangements.braid(4) # optional - sage.combinat sage.graphs + sage: A.poset_of_regions() # optional - sage.combinat sage.graphs Finite poset containing 24 elements sage: H. = HyperplaneArrangements(QQ) - sage: A = H([[0,1,1,1],[0,1,2,3],[0,1,3,2],[0,2,1,3]]) - sage: R = A.regions() - sage: base_region = R[3] - sage: A.poset_of_regions(B=base_region) + sage: A = H([[0,1,1,1], [0,1,2,3], [0,1,3,2], [0,2,1,3]]) + sage: R = A.regions() # optional - sage.combinat + sage: base_region = R[3] # optional - sage.combinat + sage: A.poset_of_regions(B=base_region) # optional - sage.combinat Finite poset containing 14 elements """ from sage.combinat.posets.posets import Poset @@ -2216,109 +2226,87 @@ def closed_faces(self, labelled=True): EXAMPLES:: - sage: a = hyperplane_arrangements.braid(2) - sage: a.hyperplanes() + sage: a = hyperplane_arrangements.braid(2) # optional - sage.graphs + sage: a.hyperplanes() # optional - sage.graphs (Hyperplane t0 - t1 + 0,) - sage: a.closed_faces() - (((0,), - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line), - ((1,), - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line), - ((-1,), - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line)) - sage: a.closed_faces(labelled=False) - (A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line) - sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] - [((0,), - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 line, - (0, 0)), - ((1,), - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, - (0, -1)), - ((-1,), - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, 1 ray, 1 line, - (-1, 0))] + sage: a.closed_faces() # optional - sage.graphs + (((0,), A 1-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 1 line), + ((1,), A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, 1 ray, 1 line), + ((-1,), A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, 1 ray, 1 line)) + sage: a.closed_faces(labelled=False) # optional - sage.graphs + (A 1-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 1 line, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, 1 ray, 1 line, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, 1 ray, 1 line) + sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] # optional - sage.graphs + [((0,), A 1-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 1 line, (0, 0)), + ((1,), A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, 1 ray, 1 line, (0, -1)), + ((-1,), A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, 1 ray, 1 line, (-1, 0))] sage: H. = HyperplaneArrangements(QQ) sage: a = H(x, y+1) sage: a.hyperplanes() (Hyperplane 0*x + y + 1, Hyperplane x + 0*y + 0) sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] - [((0, 0), - A 0-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex, - (0, -1)), - ((0, 1), - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray, - (1, -1)), - ((0, -1), - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray, - (-1, -1)), - ((1, 0), - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray, - (0, 0)), - ((1, 1), - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - (1, 0)), - ((1, -1), - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - (-1, 0)), - ((-1, 0), - A 1-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 1 ray, - (0, -2)), - ((-1, 1), - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - (1, -2)), - ((-1, -1), - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - (-1, -2))] - - sage: a = hyperplane_arrangements.braid(3) - sage: a.hyperplanes() + [((0, 0), A 0-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex, (0, -1)), + ((0, 1), A 1-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 1 ray, (1, -1)), + ((0, -1), A 1-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 1 ray, (-1, -1)), + ((1, 0), A 1-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 1 ray, (0, 0)), + ((1, 1), A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, (1, 0)), + ((1, -1), A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, (-1, 0)), + ((-1, 0), A 1-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 1 ray, (0, -2)), + ((-1, 1), A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, (1, -2)), + ((-1, -1), A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, (-1, -2))] + + sage: a = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: a.hyperplanes() # optional - sage.graphs (Hyperplane 0*t0 + t1 - t2 + 0, Hyperplane t0 - t1 + 0*t2 + 0, Hyperplane t0 + 0*t1 - t2 + 0) - sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] - [((0, 0, 0), - A 1-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex and 1 line, - (0, 0, 0)), - ((0, 1, 1), - A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, - (0, -1, -1)), - ((0, -1, -1), - A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, - (-1, 0, 0)), - ((1, 0, 1), - A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, - (1, 1, 0)), - ((1, 1, 1), - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, - (0, -1, -2)), - ((1, -1, 0), - A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, - (-1, 0, -1)), - ((1, -1, 1), - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, - (1, 2, 0)), - ((1, -1, -1), - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, - (-2, 0, -1)), - ((-1, 0, -1), - A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, - (0, 0, 1)), - ((-1, 1, 0), - A 2-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 1 ray, 1 line, - (1, 0, 1)), - ((-1, 1, 1), - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, - (0, -2, -1)), - ((-1, 1, -1), - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, - (1, 0, 2)), - ((-1, -1, -1), - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 1 vertex, 2 rays, 1 line, - (-1, 0, 1))] + sage: [(v, F, F.representative_point()) for v, F in a.closed_faces()] # optional - sage.graphs + [((0, 0, 0), A 1-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex and 1 line, (0, 0, 0)), + ((0, 1, 1), A 2-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 1 ray, 1 line, (0, -1, -1)), + ((0, -1, -1), A 2-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 1 ray, 1 line, (-1, 0, 0)), + ((1, 0, 1), A 2-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 1 ray, 1 line, (1, 1, 0)), + ((1, 1, 1), A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 2 rays, 1 line, (0, -1, -2)), + ((1, -1, 0), A 2-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 1 ray, 1 line, (-1, 0, -1)), + ((1, -1, 1), A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 2 rays, 1 line, (1, 2, 0)), + ((1, -1, -1), A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 2 rays, 1 line, (-2, 0, -1)), + ((-1, 0, -1), A 2-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 1 ray, 1 line, (0, 0, 1)), + ((-1, 1, 0), A 2-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 1 ray, 1 line, (1, 0, 1)), + ((-1, 1, 1), A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 2 rays, 1 line, (0, -2, -1)), + ((-1, 1, -1), A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 2 rays, 1 line, (1, 0, 2)), + ((-1, -1, -1), A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 1 vertex, 2 rays, 1 line, (-1, 0, 1))] Let us check that the number of closed faces with a given dimension computed using ``self.closed_faces()`` equals the one @@ -2330,10 +2318,10 @@ def closed_faces(self, labelled=True): ....: LHS = Qx.sum(x ** F[1].dim() for F in a.closed_faces()) ....: return LHS == RHS sage: a = hyperplane_arrangements.Catalan(2) - sage: test_number(a) + sage: test_number(a) # optional - sage.combinat True - sage: a = hyperplane_arrangements.Shi(3) - sage: test_number(a) # long time + sage: a = hyperplane_arrangements.Shi(3) # optional - sage.combinat + sage: test_number(a) # long time # optional - sage.combinat True TESTS: @@ -2468,27 +2456,27 @@ def face_product(self, F, G, normalize=True): EXAMPLES:: - sage: a = hyperplane_arrangements.braid(3) - sage: a.hyperplanes() + sage: a = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: a.hyperplanes() # optional - sage.graphs (Hyperplane 0*t0 + t1 - t2 + 0, Hyperplane t0 - t1 + 0*t2 + 0, Hyperplane t0 + 0*t1 - t2 + 0) - sage: faces = {F0: F1 for F0, F1 in a.closed_faces()} - sage: xGyEz = faces[(0, 1, 1)] # closed face x >= y = z - sage: xGyEz.representative_point() + sage: faces = {F0: F1 for F0, F1 in a.closed_faces()} # optional - sage.graphs + sage: xGyEz = faces[(0, 1, 1)] # closed face x >= y = z # optional - sage.graphs + sage: xGyEz.representative_point() # optional - sage.graphs (0, -1, -1) - sage: xGyEz = faces[(0, 1, 1)] # closed face x >= y = z - sage: xGyEz.representative_point() + sage: xGyEz = faces[(0, 1, 1)] # closed face x >= y = z # optional - sage.graphs + sage: xGyEz.representative_point() # optional - sage.graphs (0, -1, -1) - sage: yGxGz = faces[(1, -1, 1)] # closed face y >= x >= z - sage: xGyGz = faces[(1, 1, 1)] # closed face x >= y >= z - sage: a.face_product(xGyEz, yGxGz) == xGyGz + sage: yGxGz = faces[(1, -1, 1)] # closed face y >= x >= z # optional - sage.graphs + sage: xGyGz = faces[(1, 1, 1)] # closed face x >= y >= z # optional - sage.graphs + sage: a.face_product(xGyEz, yGxGz) == xGyGz # optional - sage.graphs True - sage: a.face_product(yGxGz, xGyEz) == yGxGz + sage: a.face_product(yGxGz, xGyEz) == yGxGz # optional - sage.graphs True - sage: xEzGy = faces[(-1, 1, 0)] # closed face x = z >= y - sage: xGzGy = faces[(-1, 1, 1)] # closed face x >= z >= y - sage: a.face_product(xEzGy, yGxGz) == xGzGy + sage: xEzGy = faces[(-1, 1, 0)] # closed face x = z >= y # optional - sage.graphs + sage: xGzGy = faces[(-1, 1, 1)] # closed face x >= z >= y # optional - sage.graphs + sage: a.face_product(xEzGy, yGxGz) == xGzGy # optional - sage.graphs True """ f = F.representative_point() @@ -2563,8 +2551,8 @@ def face_semigroup_algebra(self, field=None, names='e'): EXAMPLES:: - sage: a = hyperplane_arrangements.braid(3) - sage: [(i, F[0]) for i, F in enumerate(a.closed_faces())] + sage: a = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: [(i, F[0]) for i, F in enumerate(a.closed_faces())] # optional - sage.graphs [(0, (0, 0, 0)), (1, (0, 1, 1)), (2, (0, -1, -1)), @@ -2578,44 +2566,44 @@ def face_semigroup_algebra(self, field=None, names='e'): (10, (-1, 1, 1)), (11, (-1, 1, -1)), (12, (-1, -1, -1))] - sage: U = a.face_semigroup_algebra(); U + sage: U = a.face_semigroup_algebra(); U # optional - sage.graphs Finite-dimensional algebra of degree 13 over Rational Field - sage: e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 = U.basis() - sage: e0 * e1 + sage: e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 = U.basis() # optional - sage.graphs + sage: e0 * e1 # optional - sage.graphs e1 - sage: e0 * e5 + sage: e0 * e5 # optional - sage.graphs e5 - sage: e5 * e0 + sage: e5 * e0 # optional - sage.graphs e5 - sage: e3 * e2 + sage: e3 * e2 # optional - sage.graphs e6 - sage: e7 * e12 + sage: e7 * e12 # optional - sage.graphs e7 - sage: e3 * e12 + sage: e3 * e12 # optional - sage.graphs e6 - sage: e4 * e8 + sage: e4 * e8 # optional - sage.graphs e4 - sage: e8 * e4 + sage: e8 * e4 # optional - sage.graphs e11 - sage: e8 * e1 + sage: e8 * e1 # optional - sage.graphs e11 - sage: e5 * e12 + sage: e5 * e12 # optional - sage.graphs e7 - sage: (e3 + 2*e4) * (e1 - e7) + sage: (e3 + 2*e4) * (e1 - e7) # optional - sage.graphs e4 - e6 - sage: U3 = a.face_semigroup_algebra(field=GF(3)); U3 # optional - sage.libs.pari + sage: U3 = a.face_semigroup_algebra(field=GF(3)); U3 # optional - sage.graphs sage.rings.finite_rings Finite-dimensional algebra of degree 13 over Finite Field of size 3 TESTS: The ``names`` keyword works:: - sage: a = hyperplane_arrangements.braid(3) - sage: U = a.face_semigroup_algebra(names='x'); U + sage: a = hyperplane_arrangements.braid(3) # optional - sage.graphs + sage: U = a.face_semigroup_algebra(names='x'); U # optional - sage.graphs Finite-dimensional algebra of degree 13 over Rational Field - sage: e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 = U.basis() - sage: e0 * e1 + sage: e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12 = U.basis() # optional - sage.graphs + sage: e0 * e1 # optional - sage.graphs x1 """ if field is None: @@ -2669,13 +2657,14 @@ def region_containing_point(self, p): sage: H. = HyperplaneArrangements(QQ) sage: A = H([(1,0), 0], [(0,1), 1], [(0,1), -1], [(1,-1), 0], [(1,1), 0]) sage: A.region_containing_point([1,2]) - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 2 vertices and 2 rays + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 2 vertices and 2 rays TESTS:: sage: A = H([(1,1),0], [(2,3),-1], [(4,5),3]) - sage: B = A.change_ring(FiniteField(7)) # optional - sage.libs.pari - sage: B.region_containing_point((1,2)) # optional - sage.libs.pari + sage: B = A.change_ring(FiniteField(7)) # optional - sage.rings.finite_rings + sage: B.region_containing_point((1,2)) # optional - sage.rings.finite_rings Traceback (most recent call last): ... ValueError: base field must have characteristic zero @@ -2713,8 +2702,8 @@ def _bounded_region_indices(self): EXAMPLES:: - sage: a = hyperplane_arrangements.semiorder(3) - sage: a._bounded_region_indices() + sage: a = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: a._bounded_region_indices() # optional - sage.combinat (2, 7, 8, 9, 10, 11, 16) """ from sage.geometry.polyhedron.constructor import Polyhedron @@ -2749,18 +2738,25 @@ def bounded_regions(self): EXAMPLES:: - sage: A = hyperplane_arrangements.semiorder(3) - sage: A.bounded_regions() - (A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices and 1 line, - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices and 1 line, - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices and 1 line, - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 6 vertices and 1 line, - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices and 1 line, - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices and 1 line, - A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 3 vertices and 1 line) - sage: A.bounded_regions()[0].is_compact() # the regions are only *relatively* bounded + sage: A = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: A.bounded_regions() # optional - sage.combinat + (A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 3 vertices and 1 line, + A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 3 vertices and 1 line, + A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 3 vertices and 1 line, + A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 6 vertices and 1 line, + A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 3 vertices and 1 line, + A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 3 vertices and 1 line, + A 3-dimensional polyhedron in QQ^3 defined + as the convex hull of 3 vertices and 1 line) + sage: A.bounded_regions()[0].is_compact() # the regions are only *relatively* bounded # optional - sage.combinat False - sage: A.is_essential() + sage: A.is_essential() # optional - sage.combinat False """ return tuple(self.regions()[i] for i in self._bounded_region_indices()) @@ -2781,23 +2777,35 @@ def unbounded_regions(self): EXAMPLES:: - sage: A = hyperplane_arrangements.semiorder(3) - sage: B = A.essentialization() - sage: B.n_regions() - B.n_bounded_regions() + sage: A = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: B = A.essentialization() # optional - sage.combinat + sage: B.n_regions() - B.n_bounded_regions() # optional - sage.combinat 12 - sage: B.unbounded_regions() - (A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 3 vertices and 1 ray, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays, - A 2-dimensional polyhedron in QQ^2 defined as the convex hull of 1 vertex and 2 rays) + sage: B.unbounded_regions() # optional - sage.combinat + (A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 3 vertices and 1 ray, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays, + A 2-dimensional polyhedron in QQ^2 defined + as the convex hull of 1 vertex and 2 rays) """ s = set(range(self.n_regions())).difference(set(self._bounded_region_indices())) return tuple(self.regions()[i] for i in s) @@ -2821,8 +2829,8 @@ def whitney_data(self): EXAMPLES:: - sage: A = hyperplane_arrangements.Shi(3) - sage: A.whitney_data() + sage: A = hyperplane_arrangements.Shi(3) # optional - sage.combinat + sage: A.whitney_data() # optional - sage.combinat ( [ 1 -6 9] [ 1 6 6] [ 0 6 -15] [ 0 6 15] @@ -2871,12 +2879,12 @@ def doubly_indexed_whitney_number(self, i, j, kind=1): EXAMPLES:: - sage: A = hyperplane_arrangements.Shi(3) - sage: A.doubly_indexed_whitney_number(0, 2) + sage: A = hyperplane_arrangements.Shi(3) # optional - sage.combinat + sage: A.doubly_indexed_whitney_number(0, 2) # optional - sage.combinat 9 - sage: A.whitney_number(2) + sage: A.whitney_number(2) # optional - sage.combinat 9 - sage: A.doubly_indexed_whitney_number(1, 2) + sage: A.doubly_indexed_whitney_number(1, 2) # optional - sage.combinat -15 REFERENCES: @@ -2921,20 +2929,20 @@ def whitney_number(self, k, kind=1): EXAMPLES:: - sage: A = hyperplane_arrangements.Shi(3) - sage: A.whitney_number(0) + sage: A = hyperplane_arrangements.Shi(3) # optional - sage.combinat + sage: A.whitney_number(0) # optional - sage.combinat 1 - sage: A.whitney_number(1) + sage: A.whitney_number(1) # optional - sage.combinat -6 - sage: A.whitney_number(2) + sage: A.whitney_number(2) # optional - sage.combinat 9 - sage: A.characteristic_polynomial() + sage: A.characteristic_polynomial() # optional - sage.combinat x^3 - 6*x^2 + 9*x - sage: A.whitney_number(1,kind=2) + sage: A.whitney_number(1, kind=2) # optional - sage.combinat 6 - sage: p = A.intersection_poset() - sage: r = p.rank_function() - sage: len([i for i in p if r(i) == 1]) + sage: p = A.intersection_poset() # optional - sage.combinat + sage: r = p.rank_function() # optional - sage.combinat + sage: len([i for i in p if r(i) == 1]) # optional - sage.combinat 6 """ if k >= 0 and k <= self.dimension(): @@ -3114,7 +3122,7 @@ def matroid(self): We check the lattice of flats is isomorphic to the intersection lattice:: - sage: f = sum([list(M.flats(i)) for i in range(M.rank()+1)], []) + sage: f = sum([list(M.flats(i)) for i in range(M.rank() + 1)], []) sage: PF = Poset([f, lambda x, y: x < y]) # optional - sage.combinat sage: PF.is_isomorphic(A.intersection_poset()) # optional - sage.combinat True @@ -3206,13 +3214,13 @@ def minimal_generated_number(self): Check that :trac:`26705` is fixed:: - sage: w = WeylGroup(['A',4]).from_reduced_word([3,4,2,1]) - sage: I = w.inversion_arrangement() - sage: I + sage: w = WeylGroup(['A', 4]).from_reduced_word([3, 4, 2, 1]) # optional - sage.combinat sage.groups + sage: I = w.inversion_arrangement() # optional - sage.combinat sage.groups + sage: I # optional - sage.combinat sage.groups Arrangement - sage: I.minimal_generated_number() + sage: I.minimal_generated_number() # optional - sage.combinat sage.groups 0 - sage: I.is_formal() + sage: I.is_formal() # optional - sage.combinat sage.groups True """ V = VectorSpace(self.base_ring(), self.dimension()) @@ -3294,9 +3302,9 @@ def derivation_module_free_chain(self): EXAMPLES:: - sage: W = WeylGroup(['A',3], prefix='s') - sage: A = W.long_element().inversion_arrangement() - sage: for M in A.derivation_module_free_chain(): print("%s\n"%M) + sage: W = WeylGroup(['A',3], prefix='s') # optional - sage.combinat sage.groups + sage: A = W.long_element().inversion_arrangement() # optional - sage.combinat sage.groups + sage: for M in A.derivation_module_free_chain(): print("%s\n"%M) # optional - sage.combinat sage.groups [ 1 0 0] [ 0 1 0] [ 0 0 a3] @@ -3370,8 +3378,8 @@ def is_free(self, algorithm="singular"): For type `A` arrangements, chordality is equivalent to freeness. We verify that in type `A_3`:: - sage: W = WeylGroup(['A',3], prefix='s') - sage: for x in W: + sage: W = WeylGroup(['A', 3], prefix='s') # optional - sage.combinat sage.groups + sage: for x in W: # optional - sage.combinat sage.groups ....: A = x.inversion_arrangement() ....: assert A.matroid().is_chordal() == A.is_free() @@ -3379,8 +3387,8 @@ def is_free(self, algorithm="singular"): We check that the algorithms agree:: - sage: W = WeylGroup(['B',3], prefix='s') - sage: for x in W: # long time + sage: W = WeylGroup(['B', 3], prefix='s') # optional - sage.combinat sage.groups + sage: for x in W: # long time # optional - sage.combinat sage.groups ....: A = x.inversion_arrangement() ....: assert (A.is_free(algorithm="BC") ....: == A.is_free(algorithm="singular")) @@ -3439,19 +3447,19 @@ def derivation_module_basis(self, algorithm="singular"): EXAMPLES:: - sage: W = WeylGroup(['A',2], prefix='s') - sage: A = W.long_element().inversion_arrangement() - sage: A.derivation_module_basis() + sage: W = WeylGroup(['A', 2], prefix='s') # optional - sage.combinat sage.groups + sage: A = W.long_element().inversion_arrangement() # optional - sage.combinat sage.groups + sage: A.derivation_module_basis() # optional - sage.combinat sage.groups [(a1, a2), (0, a1*a2 + a2^2)] TESTS: We check the algorithms produce a basis with the same exponents:: - sage: W = WeylGroup(['A',2], prefix='s') - sage: exponents = lambda B: sorted([max(x.degree() for x in b) - ....: for b in B]) - sage: for x in W: # long time + sage: W = WeylGroup(['A', 2], prefix='s') # optional - sage.combinat sage.groups + sage: def exponents(B): # optional - sage.combinat sage.groups + ....: return sorted([max(x.degree() for x in b) for b in B]) + sage: for x in W: # long time # optional - sage.combinat sage.groups ....: A = x.inversion_arrangement() ....: B = A.derivation_module_basis(algorithm="singular") ....: Bp = A.derivation_module_basis(algorithm="BC") @@ -3513,49 +3521,50 @@ def hyperplane_section(self, proj=True): EXAMPLES:: - sage: A. = hyperplane_arrangements.braid(4); A + sage: A. = hyperplane_arrangements.braid(4); A # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 4 and rank 3 - sage: M = A.matroid() - sage: A1, P = A.hyperplane_section() - sage: A1 + sage: M = A.matroid() # optional - sage.graphs + sage: A1, P = A.hyperplane_section() # optional - sage.graphs + sage: A1 # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 3 and rank 3 - sage: P + sage: P # optional - sage.graphs [1, 2, 3, 6, 5, 4] - sage: M1 = A1.matroid() - sage: M.is_isomorphism(M1, {j: P(j + 1) - 1 for j in M.groundset()}) + sage: M1 = A1.matroid() # optional - sage.graphs + sage: M.is_isomorphism(M1, {j: P(j + 1) - 1 for j in M.groundset()}) # optional - sage.graphs True - sage: A2, Q = A1.hyperplane_section(); A2 + sage: A2, Q = A1.hyperplane_section(); A2 # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 2 and rank 2 - sage: M2 = A2.matroid() - sage: T1 = M1.truncation() - sage: T1.is_isomorphism(M2, {j: Q(j + 1) - 1 for j in T1.groundset()}) + sage: M2 = A2.matroid() # optional - sage.graphs + sage: T1 = M1.truncation() # optional - sage.graphs + sage: T1.is_isomorphism(M2, {j: Q(j + 1) - 1 for j in T1.groundset()}) # optional - sage.graphs True - sage: a = hyperplane_arrangements.semiorder(3); a + sage: a = hyperplane_arrangements.semiorder(3); a # optional - sage.combinat Arrangement of 6 hyperplanes of dimension 3 and rank 2 - sage: ca, p0 = a.cone(permutation=True) - sage: m = ca.matroid() - sage: a1, p = a.hyperplane_section(proj=False) - sage: a1 + sage: ca, p0 = a.cone(permutation=True) # optional - sage.combinat + sage: m = ca.matroid() # optional - sage.combinat + sage: a1, p = a.hyperplane_section(proj=False) # optional - sage.combinat + sage: a1 # optional - sage.combinat Arrangement of 6 hyperplanes of dimension 2 and rank 2 - sage: p = Permutation([p(j) for j in [1 .. 6]] + [7]); p + sage: p = Permutation([p(j) for j in [1 .. 6]] + [7]); p # optional - sage.combinat [6, 5, 2, 1, 4, 3, 7] - sage: ca1, p1 = a1.cone(permutation=True) - sage: m1 = ca1.matroid() - sage: q = p0.inverse() * p * p1 - sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) + sage: ca1, p1 = a1.cone(permutation=True) # optional - sage.combinat + sage: m1 = ca1.matroid() # optional - sage.combinat + sage: q = p0.inverse() * p * p1 # optional - sage.combinat + sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) # optional - sage.combinat True - sage: a = hyperplane_arrangements.Shi(4).hyperplane_section(proj=False)[0]; a + sage: a0 = hyperplane_arrangements.Shi(4) # optional - sage.combinat + sage: a = a0.hyperplane_section(proj=False)[0]; a # optional - sage.combinat Arrangement of 12 hyperplanes of dimension 3 and rank 3 - sage: ca, p1 = a.cone(permutation=True) - sage: m = ca.matroid().truncation() - sage: a1, p = a.hyperplane_section(proj=False); a1 + sage: ca, p1 = a.cone(permutation=True) # optional - sage.combinat + sage: m = ca.matroid().truncation() # optional - sage.combinat + sage: a1, p = a.hyperplane_section(proj=False); a1 # optional - sage.combinat Arrangement of 12 hyperplanes of dimension 2 and rank 2 - sage: p = Permutation([p(j) for j in [1 .. 12]] + [13]); p + sage: p = Permutation([p(j) for j in [1 .. 12]] + [13]); p # optional - sage.combinat [1, 5, 11, 12, 9, 10, 7, 8, 3, 4, 2, 6, 13] - sage: ca1, p2 = a1.cone(permutation=True) + sage: ca1, p2 = a1.cone(permutation=True) # optional - sage.combinat sage: m1 = ca1.matroid() - sage: q = p1.inverse() * p * p2 - sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) + sage: q = p1.inverse() * p * p2 # optional - sage.combinat + sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) # optional - sage.combinat True """ from sage.matrix.constructor import Matrix @@ -3629,7 +3638,7 @@ def _fundamental_group_(self, proj=False): sage: A. = HyperplaneArrangements(QQ) sage: L = [y + x, y + x - 1] sage: H = A(L) - sage: G, dic = H._fundamental_group_(); G # optional - sirocco + sage: G, dic = H._fundamental_group_(); G # optional - sirocco Finitely presented group < x0, x1 | > sage: L = [x, y, x + 1, y + 1, x - y] sage: H = A(L); list(H) @@ -3638,25 +3647,29 @@ def _fundamental_group_(self, proj=False): Hyperplane x - y + 0, Hyperplane x + 0*y + 0, Hyperplane x + 0*y + 1] - sage: G, dic = H._fundamental_group_() # optional - sirocco - sage: G # optional - sirocco - Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, x2^-1*x0^-1*x2*x4*x0*x4^-1, - x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, + sage: G, dic = H._fundamental_group_() # optional - sirocco + sage: G # optional - sirocco + Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, + x2^-1*x0^-1*x2*x4*x0*x4^-1, + x0*x1*x3*x0^-1*x3^-1*x1^-1, + x0*x2*x4*x2^-1*x0^-1*x4^-1, x0*x1^-1*x0^-1*x3^-1*x1*x3, x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > - sage: dic # optional - sirocco + sage: dic # optional - sirocco {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} sage: H=A(x,y,x+y) - sage: H._fundamental_group_() + sage: H._fundamental_group_() # optional - sirocco (Finitely presented group < x0, x1, x2 | x0*x1^-1*x0^-1*x2^-1*x1*x2, x0*x1*x2*x1^-1*x0^-1*x2^-1 >, {1: (3,), 2: (1,), 3: (2,), 4: (-3, -2, -1)}) - sage: H._fundamental_group_(proj=True) # optional - sirocco + sage: H._fundamental_group_(proj=True) # optional - sirocco (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) - sage: H = hyperplane_arrangements.braid(4).essentialization() - sage: H._fundamental_group_(proj=True) # optional - sirocco + sage: H = hyperplane_arrangements.braid(4).essentialization() # optional - sage.graphs + sage: H._fundamental_group_(proj=True) # optional - sage.graphs, sirocco (Finitely presented group < x0, x1, x2, x3, x4 | x1*x3*x1^-1*x3^-1, - x0*x2*x0^-1*x2^-1, x4^-1*x1^-1*x0^-1*x4*x0*x1, - x3*x4*x2*x3^-1*x2^-1*x4^-1, x3*x4^-1*x3^-1*x2^-1*x4*x2, + x0*x2*x0^-1*x2^-1, + x4^-1*x1^-1*x0^-1*x4*x0*x1, + x3*x4*x2*x3^-1*x2^-1*x4^-1, + x3*x4^-1*x3^-1*x2^-1*x4*x2, x4*x0*x1^-1*x0^-1*x4^-1*x1 >, {1: (5,), 2: (1,), 3: (2,), 4: (3,), 5: (4,), 6: (-2, -1, -4, -3, -5)}) @@ -3724,7 +3737,7 @@ def fundamental_group(self, projective=False): sage: A. = HyperplaneArrangements(QQ) sage: L = [y + x, y + x - 1] sage: H = A(L) - sage: G, dic = H.fundamental_group(); G # optional - sirocco + sage: G, dic = H.fundamental_group(); G # optional - sirocco Finitely presented group < x0, x1 | > sage: L = [x, y, x + 1, y + 1, x - y] sage: H = A(L); list(H) @@ -3733,28 +3746,34 @@ def fundamental_group(self, projective=False): Hyperplane x - y + 0, Hyperplane x + 0*y + 0, Hyperplane x + 0*y + 1] - sage: G, dic = H.fundamental_group() # optional - sirocco - sage: G # optional - sirocco - Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, x2^-1*x0^-1*x2*x4*x0*x4^-1, - x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, - x0*x1^-1*x0^-1*x3^-1*x1*x3, - x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > - sage: dic # optional - sirocco + sage: G, dic = H.fundamental_group() # optional - sirocco + sage: G # optional - sirocco + Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, + x2^-1*x0^-1*x2*x4*x0*x4^-1, + x0*x1*x3*x0^-1*x3^-1*x1^-1, + x0*x2*x4*x2^-1*x0^-1*x4^-1, + x0*x1^-1*x0^-1*x3^-1*x1*x3, + x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > + sage: dic # optional - sirocco {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} sage: H=A(x,y,x+y) - sage: H.fundamental_group() + sage: H.fundamental_group() # optional - sirocco (Finitely presented group < x0, x1, x2 | x0*x1^-1*x0^-1*x2^-1*x1*x2, x0*x1*x2*x1^-1*x0^-1*x2^-1 >, {1: (3,), 2: (1,), 3: (2,), 4: (-3, -2, -1)}) - sage: H.fundamental_group(projective=True) # optional - sirocco + sage: H.fundamental_group(projective=True) # optional - sirocco (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) - sage: H = hyperplane_arrangements.braid(4) - sage: H.fundamental_group(projective=True) # optional - sirocco - (Finitely presented group < x0, x1, x2, x3, x4 | x0*x3*x0^-1*x3^-1, x2*x4^-1*x2^-1*x4, x2*x3*x1*x3^-1*x2^-1*x1^-1, - x1*x4*x0*x1^-1*x0^-1*x4^-1, x3^-1*x2^-1*x1^-1*x3*x1*x2, x0^-1*x1*x4*x0*x4^-1*x1^-1 >, + sage: H = hyperplane_arrangements.braid(4) # optional - sage.groups + sage: H.fundamental_group(projective=True) # optional - sirocco, sage.groups + (Finitely presented group < x0, x1, x2, x3, x4 | x0*x3*x0^-1*x3^-1, + x2*x4^-1*x2^-1*x4, + x2*x3*x1*x3^-1*x2^-1*x1^-1, + x1*x4*x0*x1^-1*x0^-1*x4^-1, + x3^-1*x2^-1*x1^-1*x3*x1*x2, + x0^-1*x1*x4*x0*x4^-1*x1^-1 >, {1: (5,), 2: (1,), 3: (2,), 4: (3,), 5: (-4, -5, -3, -2, -1), 6: (4,)}) sage: H = hyperplane_arrangements.coordinate(5) - sage: g = H.fundamental_group(projective=True)[0] - sage: g.is_abelian(), g.abelian_invariants() + sage: g = H.fundamental_group(projective=True)[0] # optional - sirocco + sage: g.is_abelian(), g.abelian_invariants() # optional - sirocco (True, (0, 0, 0, 0)) .. WARNING:: @@ -4058,7 +4077,8 @@ def gen(self, i): EXAMPLES:: sage: L. = HyperplaneArrangements(QQ); L - Hyperplane arrangements in 3-dimensional linear space over Rational Field with coordinates x, y, z + Hyperplane arrangements in + 3-dimensional linear space over Rational Field with coordinates x, y, z sage: L.gen(0) Hyperplane x + 0*y + 0*z + 0 """ From 08125eca073c16c55050253f0795d96381949bb0 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 22 May 2023 16:39:01 +0200 Subject: [PATCH 091/122] make faster the computation of Puiseux relations --- src/sage/schemes/curves/zariski_vankampen.py | 176 ++++++++++++------- 1 file changed, 108 insertions(+), 68 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 5b015103acf..8ac4f36b12d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -45,13 +45,14 @@ from sage.sets.set import Set 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 rightnormalform +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 @@ -59,6 +60,7 @@ 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 @@ -1205,80 +1207,115 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): return roots_base @parallel -def braid2rels(L, d): +def conjugate_positive_form(braid): r""" - Return a minimal set of elements of ``F = FreeGroup(d)`` for a quasi-positive braid ``b=BraidGroup(d)(L)`` as relations - of the group ``F / [(b * F([j])) / F([j]) for j in (1..d)]``. One starts from the non-trivial relations determined by the - positive braid and transform them in relations determined by ``b``. + For a ``braid`` which is conjugate to a positive braid such a decomposition is given. INPUT: - - ``L`` -- a list of integers in ``[-d..-1] + [1..d]`` which is the Tietze word of a quasi-positive braid. + - ``braid`` -- a braid. - - ``d`` -- a positive integer + OUTPUT: + + A list with two elements, a positive braid `\sigma` and a list of permutation braids `\tau_1,\dots,\tau_n` + such that if `\tau=\prod_{j=1}^n \tau_j` then `\tau\sigma\tau^{-1}` is the input ``braid``. + + 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]]] + """ + B = braid.parent() + d = B.strands() + braid1 = braid.super_summit_set()[0] + L1 = braid1.Tietze() + sg0 = braid.conjugating_braid(braid1) + hilos = list(Set(L1)) + hilos.sort() + bloques = [] + while len(hilos) > 0: + a = [hilos[0]] + hilos = hilos[1:] + corte = False + while len(hilos) > 0 and not corte: + b = hilos[0] + corte = b - a[-1] > 1 + if not corte: + a.append(b) + hilos = hilos[1:] + bloques.append(a) + trenzabloque = [[a for a in L1 if a in b] for b in bloques] + cortas = [] + for a in trenzabloque: + 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] + cortas.append(res0) + return cortas + + +@parallel +def braid2rels(L): + r""" + Return a minimal set of elements of ``F = FreeGroup(d)`` for a braid ``b`` which is the conjugate of + a positive braid as relations of the group ``F / [(b * F([j])) / F([j]) for j in (1..d)]``. 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 / [(b * F([j])) / F([j]) for j in (1..d)]``. + 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 # optional - sirocco - sage: L = (1, 3, 2, -3, 1, 1) - sage: braid2rels(L, 4) # optional - sirocco + 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)] """ - B = BraidGroup(d) + br = L[0] + L1 = L[1] + B = br.parent() + d = B.strands() F = FreeGroup(d) - L1 = copy(L) - low = True - while low: - L2 = copy(L1) - j = 0 - other = False - while j < len(L2) - 1 and not other: - try: - l = L2[j] - k = L2[j:].index(-l)+j - A = L2[j + 1: k] - Bn = next((_ for _ in A if (abs(_) - abs(l)) ** 2 == 1), None) - if Bn is None: - other = True - L2 = L2[:j] + A + L2[k + 1:] - else: - j += 1 - except ValueError: - j += 1 - low = len(L2) < len(L1) - L1 = copy(L2) - c0 = () - b0 = L1 - while b0[0] + b0[-1] == 0: - c0= c0 + (b0[0],) - b0 = b0[1: -1] - A = B(b0).super_summit_set() - A.sort() - res = None - for tau in A: - sg = B(c0) * B(b0).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]) - U0 = list(Set(res[0].Tietze())) - U0.sort() - U = [(F([j]) * B(res[0])) / F([j]) for j in U0] - U = [_.Tietze() for _ in U] - pasos = [B.one()] + [_ for _ in reversed(res[1])] + T = br.Tietze() + T1 = list(Set(T)) + T1.sort() + m = len(T1) + 1 + k = min(T1) - 1 + B0 = BraidGroup(m) + F0 = FreeGroup(m) + br0 = B0([sign(_)*(abs(_)-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]] + #U0 = [(f0 * br0) / 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 ** (-1)).Tietze() for a in U] ga = F / U @@ -1293,6 +1330,7 @@ def braid2rels(L, d): U = [_.Tietze() for _ in gb.relations()] return U + def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid_mon=None): r""" Return a presentation of the fundamental group of the complement of @@ -1421,16 +1459,18 @@ def relation(x, b): if not puiseux: relations = (relation([(x, b) for x in F.gens() for b in bm])) - R = [r[1] for r in relations] + rel = [r[1] for r in relations] else: simplified = False - relations = (braid2rels([(b.Tietze(), d) for b in bm])) - R = [] - for r in relations: - R += r[1] + conjugate_desc = conjugate_positive_form(bm) + trenzas_desc = [b1[-1] for b1 in conjugate_desc] + trenzas_desc_1 = flatten(trenzas_desc, max_level=1) + relations = braid2rels(trenzas_desc_1) + rel = [r[1] for r in relations] + rel = flatten(rel, max_level=1) if projective: - R.append(prod(F.gens())) - G = F / R + rel.append(prod(F.gens())) + G = F / rel if simplified: return G.simplified() return G From 79a8ac5dd91a8bbad6ba0718c7b9f909e0986eec Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 23 May 2023 17:24:47 +0200 Subject: [PATCH 092/122] Move parallel functions to appear in doc --- src/sage/schemes/curves/zariski_vankampen.py | 33 ++++++++------------ 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 8ac4f36b12d..bc2899024a8 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1206,10 +1206,10 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): elif not computebm0 and holdstrand: return roots_base -@parallel + def conjugate_positive_form(braid): r""" - For a ``braid`` which is conjugate to a positive braid such a decomposition is given. + For a ``braid`` which is conjugate to a product of *disjoint* positive braids a list of such decompositions is given. INPUT: @@ -1217,8 +1217,8 @@ def conjugate_positive_form(braid): OUTPUT: - A list with two elements, a positive braid `\sigma` and a list of permutation braids `\tau_1,\dots,\tau_n` - such that if `\tau=\prod_{j=1}^n \tau_j` then `\tau\sigma\tau^{-1}` is the input ``braid``. + A list of lists where each element is a list with two elements, a positive braid `\sigma` and a list of permutation braids + `\tau_1,\dots,\tau_n` such that if `\tau=\prod_{j=1}^n \tau_j` then `\tau\sigma\tau^{-1}` is the input ``braid``. EXAMPLES:: @@ -1271,8 +1271,10 @@ def conjugate_positive_form(braid): cortas.append(res0) return cortas - @parallel +def conjugate_positive_form_p(braid): + return conjugate_positive_form(braid) + def braid2rels(L): r""" Return a minimal set of elements of ``F = FreeGroup(d)`` for a braid ``b`` which is the conjugate of @@ -1313,7 +1315,6 @@ def braid2rels(L): 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]] - #U0 = [(f0 * br0) / 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: @@ -1330,6 +1331,10 @@ def braid2rels(L): U = [_.Tietze() for _ in gb.relations()] return U +@parallel +def braid2rels_p(L): + return braid2rels(L) + def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid_mon=None): r""" @@ -1439,10 +1444,6 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid if projective: while g.degree(y) < g.degree(): g = g.subs({x: x + y}) - # if g.degree(y) == 1: - # if projective: - # return FreeGroup(1) / [[g.degree()]] - # return FreeGroup(1)/[] if braid_mon is None: bm = braid_monodromy(g) else: @@ -1462,10 +1463,10 @@ def relation(x, b): rel = [r[1] for r in relations] else: simplified = False - conjugate_desc = conjugate_positive_form(bm) + conjugate_desc = conjugate_positive_form_p(bm) trenzas_desc = [b1[-1] for b1 in conjugate_desc] trenzas_desc_1 = flatten(trenzas_desc, max_level=1) - relations = braid2rels(trenzas_desc_1) + relations = braid2rels_p(trenzas_desc_1) rel = [r[1] for r in relations] rel = flatten(rel, max_level=1) if projective: @@ -1627,14 +1628,6 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis for i in range(len(flist1)): j = [j1 for j1 in dic.keys() if dic[j1] == i + 1][0] dic1[i + 1] = hom(g([j])).Tietze() - # if projective: - # Lg = [j+1 for j in range(len(flist1)) if j+1 not in dic1.keys()] - # if len(Lg) == 1: - # j = Lg[0] - # i = [i + 1 for i in range(g.ngens()) if dic[i + 1] == j][0] - # t = hom(g([i])).Tietze() - # dic1[j] = t - # elif f.degree(y) == f.degree(): if not projective and f.degree(y) == f.degree(): t = prod(hom(x) for x in g.gens()).inverse().Tietze() dic1[len(flist1) + 1] = t From e5708513a172903954140f16174c80396c95fb63 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 25 May 2023 18:34:37 +0200 Subject: [PATCH 093/122] vertical braid monodromy --- src/sage/schemes/curves/zariski_vankampen.py | 118 +++++++++++++++---- 1 file changed, 94 insertions(+), 24 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index bc2899024a8..c585b950b4b 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1335,6 +1335,99 @@ def braid2rels(L): def braid2rels_p(L): return braid2rels(L) +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: ``[]``); a list of integers in ``[1..r]`` where + ``r`` is the number of braids; the corresponding vertical lines are taking out from the complement + of the curve. + + 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 > + """ + vertical.sort() + v = len(vertical) + 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 vertical] + + @parallel + def relation(x, b): + return x * b / x + + 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] + else: + simplified = 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(vertical): + l = d + j + 1 + br = bm[k - 1] + for gen in F.gens(): + j0 = gen.Tietze()[0] + rl = (l,) + (gen * br).Tietze() + (-l, -j0) + rel_v.append(rl) + rel = rel_h + rel_v + if projective: + rel.append(prod(Fv.gens()).Tietze()) + G = Fv / rel + if simplified: + return G.simplified() + return G + def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid_mon=None): r""" @@ -1452,30 +1545,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid d = g.degree(y) else: d = bm[0].parent().strands() - F = FreeGroup(d) - - @parallel - def relation(x, b): - return x * b / x - - if not puiseux: - relations = (relation([(x, b) for x in F.gens() for b in bm])) - rel = [r[1] for r in relations] - else: - simplified = False - conjugate_desc = conjugate_positive_form_p(bm) - trenzas_desc = [b1[-1] for b1 in conjugate_desc] - trenzas_desc_1 = flatten(trenzas_desc, max_level=1) - relations = braid2rels_p(trenzas_desc_1) - rel = [r[1] for r in relations] - rel = flatten(rel, max_level=1) - if projective: - rel.append(prod(F.gens())) - G = F / rel - if simplified: - return G.simplified() - return G - + return fundamental_group_from_braid_mon(bm, degree=d, simplified=simplified, projective=projective, puiseux=puiseux) def braid_monodromy_arrangement(flist): r""" From 47a87323a96f72333031674ef48d7c38d0d18598 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 31 May 2023 00:03:46 +0200 Subject: [PATCH 094/122] correcting geometric basis --- src/sage/schemes/curves/zariski_vankampen.py | 79 ++++++++++++-------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index c585b950b4b..c59ea6ee7c2 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -977,57 +977,77 @@ def geometric_basis(G, E, EC, p, dual_graph): 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) - if not Internal: # Creo que se puede quitar - for v in E: - if len(E.neighbors(v)) > 2: - Internal.add_vertex(v) + Internal1 = G.subgraph(vertices=G.vertices(sort=True), edges=InternalEdges) for i, ECi in enumerate(EC): # q and r are the points we will cut through if ECi in Internal: - q = ECi - connecting_path = EC[:i] - break - if EC[-i] in Internal: # creo que sobra - q = EC[-i] - connecting_path = list(reversed(EC[-i:])) - break - distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in E - if v in Internal.connected_component_containing_vertex(q) and not v == q] + EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi) and v != ECi] + if len(EI) > 0: + q = ECi + connecting_path = EC[:i] + break + if EC[-i] in Internal: + EI = [v for v in E if v in Internal.connected_component_containing_vertex(EC[-i]) and v != EC[-i]] + if len(EI) > 0: + q = EC[-i] + connecting_path = list(reversed(EC[-i:])) + break + distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in EI] r = max(distancequotients)[1] qi = EC.index(q) ri = EC.index(r) cutpath = Internal.shortest_path(q, r) - #Gcut = copy(G) Ecut = copy(E) Ecut.delete_vertices([q, r]) + 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 + + Gd = copy(dual_graph) cp1 = [(e, cutpath[i + 1], None) for i, e in enumerate(cutpath[: -1])] cp1 += [(cutpath[i + 1], e, None) for i, e in enumerate(cutpath[: -1])] borra = [_ for _ in Gd.edges(sort=True) if _[2] in cp1] Gd.delete_edges(borra) Gd1, Gd2 = Gd.connected_components_subgraphs() - GL2 = [v for r in Gd2.vertices(sort=True) for v in r[1]] - GL2 += [v for e in Gd2.edges(sort=True) for v in e[2]] + [v for v in cutpath] - G2 = G.subgraph(GL2) - GL1 = [v for r in Gd1.vertices(sort=True) for v in r[1]] - GL1 += [v for e in Gd1.edges(sort=True) for v in e[2]] + [v for v in cutpath] - G1 = G.subgraph(GL1) + edges_2 = [] + vertices_2 = [] + for reg in Gd2.vertices(sort=True): + vertices_2 += reg[1] + reg_circuit = reg[1] + (reg[1][0],) + 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 - E1, E2 = Ecut.connected_components_subgraphs() - if EC[qi + 1] in E2: - E1, E2 = E2, E1 - - 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) for v in [q, r]: for n in E.neighbors(v): - if n in E1: + if n in E1 and n not in (q, r): E1.add_edge(v, n, None) - if n in E2: + 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) + if qi < ri: EC1 = [EC[j] for j in range(qi, ri)] + [_ for _ in reversed(cutpath)] EC2 = cutpath + EC[ri + 1 : -1] + EC[ : qi + 1] @@ -1053,7 +1073,6 @@ def geometric_basis(G, E, EC, p, dual_graph): i += 1 return resul - def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. From 7d9dda84bfd14d01fa03abf301471304a99b3e61 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 31 May 2023 00:50:04 +0200 Subject: [PATCH 095/122] lint changes --- .../geometry/hyperplane_arrangement/arrangement.py | 2 -- src/sage/schemes/curves/zariski_vankampen.py | 11 +++-------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 34114da0f62..5f59595b7d6 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -358,8 +358,6 @@ from sage.structure.richcmp import richcmp from sage.structure.unique_representation import UniqueRepresentation - - class HyperplaneArrangementElement(Element): """ A hyperplane arrangement. diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index c59ea6ee7c2..65675a4d411 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -162,8 +162,6 @@ def sgn(x, y): B = BraidGroup(len(L)) return B(braid) - - def discrim(pols): r""" Return the points in the discriminant of the product of the polynomials of a list or tuple ``pols``. @@ -380,8 +378,6 @@ def voronoi_cells(V): DG.add_edge(crd[e] + (e,)) return (G, E, p, EC, DG) - - def followstrand(f, factors, x0, x1, y0a, prec=53): r""" Return a piecewise linear approximation of the homotopy continuation @@ -918,8 +914,8 @@ def geometric_basis(G, E, EC, p, dual_graph): - ``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 ordering and the second one is the - list of vertices in the region and ``E``. + by a tuple whose first element is the an integer for the position and the second one is the + 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 @@ -1008,9 +1004,8 @@ def geometric_basis(G, E, EC, p, dual_graph): E2 = Graph() E2.add_edge(q, r, None) if r == EC[qi + 1]: - E1, E2 = E2, E1 + E1, E2 = E2, E1 - Gd = copy(dual_graph) cp1 = [(e, cutpath[i + 1], None) for i, e in enumerate(cutpath[: -1])] cp1 += [(cutpath[i + 1], e, None) for i, e in enumerate(cutpath[: -1])] From e551674c075feeb5595c3346224ae894112fc19f Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 31 May 2023 00:55:03 +0200 Subject: [PATCH 096/122] more lint changes --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 65675a4d411..f713e55ace8 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -977,7 +977,7 @@ def geometric_basis(G, E, EC, p, dual_graph): for i, ECi in enumerate(EC): # q and r are the points we will cut through if ECi in Internal: EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi) and v != ECi] - if len(EI) > 0: + if len(EI) > 0: q = ECi connecting_path = EC[:i] break From d84c3c0115e7a96b33d3698eed30d370b7d72dd2 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 31 May 2023 08:00:05 +0200 Subject: [PATCH 097/122] update z_vk --- src/sage/schemes/curves/zariski_vankampen.py | 86 ++++++++++++-------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index c585b950b4b..f713e55ace8 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -162,8 +162,6 @@ def sgn(x, y): B = BraidGroup(len(L)) return B(braid) - - def discrim(pols): r""" Return the points in the discriminant of the product of the polynomials of a list or tuple ``pols``. @@ -380,8 +378,6 @@ def voronoi_cells(V): DG.add_edge(crd[e] + (e,)) return (G, E, p, EC, DG) - - def followstrand(f, factors, x0, x1, y0a, prec=53): r""" Return a piecewise linear approximation of the homotopy continuation @@ -918,8 +914,8 @@ def geometric_basis(G, E, EC, p, dual_graph): - ``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 ordering and the second one is the - list of vertices in the region and ``E``. + by a tuple whose first element is the an integer for the position and the second one is the + 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 @@ -977,57 +973,76 @@ def geometric_basis(G, E, EC, p, dual_graph): 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) - if not Internal: # Creo que se puede quitar - for v in E: - if len(E.neighbors(v)) > 2: - Internal.add_vertex(v) + Internal1 = G.subgraph(vertices=G.vertices(sort=True), edges=InternalEdges) for i, ECi in enumerate(EC): # q and r are the points we will cut through if ECi in Internal: - q = ECi - connecting_path = EC[:i] - break - if EC[-i] in Internal: # creo que sobra - q = EC[-i] - connecting_path = list(reversed(EC[-i:])) - break - distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in E - if v in Internal.connected_component_containing_vertex(q) and not v == q] + EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi) and v != ECi] + if len(EI) > 0: + q = ECi + connecting_path = EC[:i] + break + if EC[-i] in Internal: + EI = [v for v in E if v in Internal.connected_component_containing_vertex(EC[-i]) and v != EC[-i]] + if len(EI) > 0: + q = EC[-i] + connecting_path = list(reversed(EC[-i:])) + break + distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in EI] r = max(distancequotients)[1] qi = EC.index(q) ri = EC.index(r) cutpath = Internal.shortest_path(q, r) - #Gcut = copy(G) Ecut = copy(E) Ecut.delete_vertices([q, r]) + 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 + Gd = copy(dual_graph) cp1 = [(e, cutpath[i + 1], None) for i, e in enumerate(cutpath[: -1])] cp1 += [(cutpath[i + 1], e, None) for i, e in enumerate(cutpath[: -1])] borra = [_ for _ in Gd.edges(sort=True) if _[2] in cp1] Gd.delete_edges(borra) Gd1, Gd2 = Gd.connected_components_subgraphs() - GL2 = [v for r in Gd2.vertices(sort=True) for v in r[1]] - GL2 += [v for e in Gd2.edges(sort=True) for v in e[2]] + [v for v in cutpath] - G2 = G.subgraph(GL2) - GL1 = [v for r in Gd1.vertices(sort=True) for v in r[1]] - GL1 += [v for e in Gd1.edges(sort=True) for v in e[2]] + [v for v in cutpath] - G1 = G.subgraph(GL1) + edges_2 = [] + vertices_2 = [] + for reg in Gd2.vertices(sort=True): + vertices_2 += reg[1] + reg_circuit = reg[1] + (reg[1][0],) + 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 - E1, E2 = Ecut.connected_components_subgraphs() - if EC[qi + 1] in E2: - E1, E2 = E2, E1 - - 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) for v in [q, r]: for n in E.neighbors(v): - if n in E1: + if n in E1 and n not in (q, r): E1.add_edge(v, n, None) - if n in E2: + 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) + if qi < ri: EC1 = [EC[j] for j in range(qi, ri)] + [_ for _ in reversed(cutpath)] EC2 = cutpath + EC[ri + 1 : -1] + EC[ : qi + 1] @@ -1053,7 +1068,6 @@ def geometric_basis(G, E, EC, p, dual_graph): i += 1 return resul - def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. From 12e1cce7028bdd86fa544790c887b0f1e845769b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 31 May 2023 13:10:29 +0200 Subject: [PATCH 098/122] some cases for geometrical basis --- src/sage/schemes/curves/zariski_vankampen.py | 91 ++++++++++---------- 1 file changed, 47 insertions(+), 44 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index f713e55ace8..c375068f0aa 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -325,29 +325,21 @@ def voronoi_cells(V): sage: edg = DG.edges(sort=True)[0]; edg ((0, (A vertex at (9/2, -9/2), - A vertex at (2000001/2000000, -24500001/7000000), - A vertex at (2000001/2000000, 500001/1000000), - A vertex at (2000001/2000000, -24500001/7000000), - A vertex at (11/4, 4), - A vertex at (2000001/2000000, 500001/1000000), A vertex at (9/2, 9/2), A vertex at (11/4, 4), - A vertex at (9/2, -9/2), - A vertex at (9/2, 9/2))), - (1, - (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 (-49000001/14000000, 1000001/2000000), - A vertex at (1000001/2000000, 1000001/2000000), - A vertex at (-4, 4), A vertex at (11/4, 4), - A vertex at (-49000001/14000000, 1000001/2000000), - A vertex at (-4, 4))), - (A vertex at (2000001/2000000, 500001/1000000), A vertex at (11/4, 4), None)) - sage: list(Ge).index(edg[-1]) - 13 + A vertex at (-4, 4), + A vertex at (-49000001/14000000, 1000001/2000000))), + (A vertex at (11/4, 4), A vertex at (2000001/2000000, 500001/1000000), None)) + sage: edg[-1] in Ge + True """ V0 = [_ for _ in V.regions().values() if _.is_compact()] Vnc = [_ for _ in V.regions().values() if not _.is_compact()] @@ -356,14 +348,17 @@ def voronoi_cells(V): p = next(E.vertex_iterator()) EC0 = orient_circuit(E.eulerian_circuit()) EC = [EC0[0][0]] + [e[1] for e in EC0] - Vreg1 = {V0.index(_) : (V0.index(_), tuple(v for e in _.facets() for v in e.vertices())) for _ in V0} - DG = Graph(len(V0)) - DG.relabel(Vreg1) + Vreg1 = {} Edges = [] crd = {} - for i, v in enumerate(V0): - r = [e.vertices() + (None, ) for e in v.facets()] - for e in r: + reg_graphs = {} + for i, reg in enumerate(V0): + reg_graphs[i] = reg.graph() + Greg0 = orient_circuit(reg_graphs[i].eulerian_circuit()) + Greg = (Greg0[0][0],) + tuple(e[1] for e in Greg0) + Vreg1[i] = (i, Greg) + for i, reg in enumerate(V0): + for e in reg_graphs[i].edges(sort=True): a, b = e[:2] e1 = (b, a, None) if e not in Edges: @@ -374,6 +369,8 @@ def voronoi_cells(V): crd[e] += (Vreg1[i],) crd[e1] += (Vreg1[i],) EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] + DG = Graph(len(V0)) + DG.relabel(Vreg1) for e in EdgesDual: DG.add_edge(crd[e] + (e,)) return (G, E, p, EC, DG) @@ -543,19 +540,18 @@ def fieldI(F0): I0 = QQbar.gen() if I0 in F0: return F0 - else: - F0a = F0[I0] - F1a = F0a.absolute_field('b0') - b0 = F1a.gen() - q = b0.minpoly() - qembd = F1a.embeddings(QQbar) - for h1 in qembd: - b1 = h1(b0) - b2 = h1(F1a(F0a.gen(0))) - b3 = F0.gen(0) - F1 = NumberField(q, 'b', embedding=b1) - if b3 in F1 and b2.imag() > 0: - return F1 + F0a = F0[I0] + F1a = F0a.absolute_field('b0') + b0 = F1a.gen() + q = b0.minpoly() + qembd = F1a.embeddings(QQbar) + for h1 in qembd: + b1 = h1(b0) + b2 = h1(F1a(F0a.gen(0))) + b3 = F0.gen(0) + F1 = NumberField(q, 'b', embedding=b1) + if b3 in F1 and b2.imag() > 0: + return F1 @parallel @@ -915,7 +911,7 @@ def geometric_basis(G, E, EC, p, dual_graph): ``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 - ordered list of vertices in the region. + 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 @@ -973,7 +969,6 @@ def geometric_basis(G, E, EC, p, dual_graph): 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) - Internal1 = G.subgraph(vertices=G.vertices(sort=True), edges=InternalEdges) for i, ECi in enumerate(EC): # q and r are the points we will cut through if ECi in Internal: EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi) and v != ECi] @@ -989,9 +984,17 @@ def geometric_basis(G, E, EC, p, dual_graph): break distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in EI] r = max(distancequotients)[1] + cutpath = Internal.shortest_path(q, r) + cutpath_in_E = False + i = 1 + while not cutpath_in_E and i < len(cutpath) - 1: + cutpath_in_E = cutpath[i] in EC + if cutpath_in_E: + r = cutpath[i] + cutpath = cutpath[:i+1] + i += i+1 qi = EC.index(q) ri = EC.index(r) - cutpath = Internal.shortest_path(q, r) Ecut = copy(E) Ecut.delete_vertices([q, r]) subgraphs = Ecut.connected_components_subgraphs() @@ -1015,8 +1018,8 @@ def geometric_basis(G, E, EC, p, dual_graph): edges_2 = [] vertices_2 = [] for reg in Gd2.vertices(sort=True): - vertices_2 += reg[1] - reg_circuit = reg[1] + (reg[1][0],) + 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) @@ -1213,11 +1216,11 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): computebm0 = computebm or len(disc) == 0 if computebm0 and not holdstrand: return (result, strands) - elif computebm0 and holdstrand: + if computebm0 and holdstrand: return (result,roots_base) - elif not computebm0 and not holdstrand: + if not computebm0 and not holdstrand: return strands - elif not computebm0 and holdstrand: + if not computebm0 and holdstrand: return roots_base From 4ab061ede01ad8aeb50a39448f92a71c13d75161 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 31 May 2023 18:55:17 +0200 Subject: [PATCH 099/122] cleaning --- src/sage/schemes/curves/zariski_vankampen.py | 25 ++++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index c375068f0aa..3af4e93007c 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -42,8 +42,6 @@ import itertools from copy import copy from sage.combinat.combination import Combinations -from sage.sets.set import Set - from sage.combinat.permutation import Permutation from sage.functions.generalized import sign from sage.geometry.voronoi_diagram import VoronoiDiagram @@ -66,6 +64,7 @@ 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 = {} @@ -193,14 +192,14 @@ def discrim(pols): """ flist = tuple(pols) x, y = flist[0].parent().gens() - F = flist[0].base_ring() - R = PolynomialRing(F,(x,)) + field = flist[0].base_ring() + pol_ring = PolynomialRing(field,(x,)) @parallel def discrim_pairs(f, g): if f == g: - return R(f.discriminant(y)) - return R(f.resultant(g, y)) + return pol_ring(f.discriminant(y)) + return pol_ring(f.resultant(g, y)) pairs = [(f, f) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) @@ -341,10 +340,10 @@ def voronoi_cells(V): sage: edg[-1] in Ge True """ - V0 = [_ for _ in V.regions().values() if _.is_compact()] - Vnc = [_ for _ in V.regions().values() if not _.is_compact()] - G = Graph([u.vertices() for v in V0 for u in v.faces(1)], format = 'list_of_edges') - E = Graph([u.vertices() for v in Vnc for u in v.faces(1) if u.is_compact()], format = 'list_of_edges') + 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()) EC0 = orient_circuit(E.eulerian_circuit()) EC = [EC0[0][0]] + [e[1] for e in EC0] @@ -352,12 +351,12 @@ def voronoi_cells(V): Edges = [] crd = {} reg_graphs = {} - for i, reg in enumerate(V0): + for i, reg in enumerate(compact_regions): reg_graphs[i] = reg.graph() Greg0 = orient_circuit(reg_graphs[i].eulerian_circuit()) Greg = (Greg0[0][0],) + tuple(e[1] for e in Greg0) Vreg1[i] = (i, Greg) - for i, reg in enumerate(V0): + for i, reg in enumerate(compact_regions): for e in reg_graphs[i].edges(sort=True): a, b = e[:2] e1 = (b, a, None) @@ -369,7 +368,7 @@ def voronoi_cells(V): crd[e] += (Vreg1[i],) crd[e1] += (Vreg1[i],) EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] - DG = Graph(len(V0)) + DG = Graph(len(compact_regions)) DG.relabel(Vreg1) for e in EdgesDual: DG.add_edge(crd[e] + (e,)) From 4a18673e97bd722b3b462acd2f134ef999d4f709 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 10 Jun 2023 00:35:10 +0200 Subject: [PATCH 100/122] mistake in geometric_basis --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 3af4e93007c..09f8d054343 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -991,7 +991,7 @@ def geometric_basis(G, E, EC, p, dual_graph): if cutpath_in_E: r = cutpath[i] cutpath = cutpath[:i+1] - i += i+1 + i = i+1 qi = EC.index(q) ri = EC.index(r) Ecut = copy(E) From 69ff829220e752cde55661bf5830344a2259d4b6 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 27 Jun 2023 14:05:12 +0200 Subject: [PATCH 101/122] cleaning up --- src/sage/schemes/curves/zariski_vankampen.py | 116 ++++++++++--------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 09f8d054343..ef141cfe795 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -161,6 +161,7 @@ def sgn(x, y): B = BraidGroup(len(L)) return B(braid) + def discrim(pols): r""" Return the points in the discriminant of the product of the polynomials of a list or tuple ``pols``. @@ -193,15 +194,15 @@ def discrim(pols): flist = tuple(pols) x, y = flist[0].parent().gens() field = flist[0].base_ring() - pol_ring = PolynomialRing(field,(x,)) + pol_ring = PolynomialRing(field, (x,)) @parallel def discrim_pairs(f, g): - if f == g: + if g is None: return pol_ring(f.discriminant(y)) return pol_ring(f.resultant(g, y)) - pairs = [(f, f) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] + pairs = [(f, None) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) rts = [] poly = 1 @@ -270,6 +271,7 @@ def corrected_voronoi_diagram(points): break return V + def voronoi_cells(V): r""" Compute the graph, the boundary graph, a base point, a positive orientation of the boundary graph, @@ -339,41 +341,27 @@ def voronoi_cells(V): (A vertex at (11/4, 4), A vertex at (2000001/2000000, 500001/1000000), None)) sage: edg[-1] in Ge True -""" + """ 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') + 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()) EC0 = orient_circuit(E.eulerian_circuit()) EC = [EC0[0][0]] + [e[1] for e in EC0] - Vreg1 = {} - Edges = [] - crd = {} - reg_graphs = {} + DG = Graph() for i, reg in enumerate(compact_regions): - reg_graphs[i] = reg.graph() - Greg0 = orient_circuit(reg_graphs[i].eulerian_circuit()) + Greg0 = orient_circuit(reg.graph().eulerian_circuit()) Greg = (Greg0[0][0],) + tuple(e[1] for e in Greg0) - Vreg1[i] = (i, Greg) - for i, reg in enumerate(compact_regions): - for e in reg_graphs[i].edges(sort=True): - a, b = e[:2] - e1 = (b, a, None) - if e not in Edges: - Edges += [e, e1] - crd[e] = (Vreg1[i],) - crd[e1] = (Vreg1[i],) - else: - crd[e] += (Vreg1[i],) - crd[e1] += (Vreg1[i],) - EdgesDual = [_ for _ in Edges if len(crd[_]) == 2] - DG = Graph(len(compact_regions)) - DG.relabel(Vreg1) - for e in EdgesDual: - DG.add_edge(crd[e] + (e,)) + DG.add_vertex((i, Greg)) + 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): r""" Return a piecewise linear approximation of the homotopy continuation @@ -505,6 +493,7 @@ def newton(f, x0, i0): """ return x0 - f(x0) / f.derivative()(i0) + def fieldI(F0): r""" Return the (either double or trivial) extension of a number field which contains ``I``. @@ -524,7 +513,8 @@ def fieldI(F0): 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 + 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 Another example where ``F`` and ``F0`` coincide. @@ -601,7 +591,6 @@ def roots_interval(pols, x0): flist = tuple(pols) f = prod(flist) F1 = f.base_ring() - I1 = F1(QQbar.gen()) x, y = f.parent().gens() Ux = [F1[y](_.subs({x: F1(x0)})) for _ in flist] fx = F1[y](f.subs({x: F1(x0)})) @@ -759,13 +748,12 @@ def braid_in_segment(glist, x0, x1, precision={}): """ g = prod(glist) F1 = g.base_ring() - I1 = F1(QQbar.gen()) x, y = g.parent().gens() X0 = F1(x0) X1 = F1(x1) intervals = {} - if precision == {}: # new - precision ={f: 53 for f in glist} # new + if precision == {}: # new + precision = {f: 53 for f in glist} # new y0s = [] for f in glist: if f.variables() == (y,): @@ -791,18 +779,18 @@ def braid_in_segment(glist, x0, x1, precision={}): finalstrands = [] initialintervals = roots_interval_cached(glist, X0) finalintervals = roots_interval_cached(glist, X1) - I = QQbar.gen() + 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 != 1: - precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision=precision) # new + precision = {f: precision[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision=precision) # new matched = 0 for center, interval in finalintervals.items(): @@ -810,8 +798,8 @@ def braid_in_segment(glist, x0, x1, precision={}): finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())]) matched += 1 if matched != 1: - precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision=precision) # new + precision = {f: precision[f] * 2 for f in glist} # new + return braid_in_segment(glist, x0, x1, precision=precision) # new initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) @@ -873,12 +861,15 @@ def orient_circuit(circuit, convex=False): """ vectors = [v[1].vector() - v[0].vector() for v in circuit] + circuit_vertex = [circuit[0][0]] + [e[1] for e in circuit] 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 [_ for _ in reversed(circuit_vertex)] prec = 53 while True: CIF = ComplexIntervalField(prec) @@ -886,8 +877,10 @@ def orient_circuit(circuit, convex=False): for i in range(len(vectors))) if totalangle < 0: return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) + # return [_ for _ in reversed(circuit_vertex)] if totalangle > 0: return circuit + # return circuit_vertex prec *= 2 @@ -1047,10 +1040,10 @@ def geometric_basis(G, E, EC, p, dual_graph): if qi < ri: EC1 = [EC[j] for j in range(qi, ri)] + [_ for _ in reversed(cutpath)] - EC2 = cutpath + EC[ri + 1 : -1] + EC[ : qi + 1] + EC2 = cutpath + EC[ri + 1: -1] + EC[: qi + 1] else: - EC1 = EC[qi : ] + EC[1 : ri] + [_ for _ in reversed(cutpath)] - EC2 = cutpath + EC[ri + 1 : qi + 1] + EC1 = EC[qi:] + EC[1:ri] + [_ for _ in reversed(cutpath)] + EC2 = cutpath + EC[ri + 1:qi + 1] gb1 = geometric_basis(G1, E1, EC1, q, Gd1) gb2 = geometric_basis(G2, E2, EC2, q, Gd2) @@ -1070,6 +1063,7 @@ def geometric_basis(G, E, EC, p, dual_graph): i += 1 return resul + def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -1150,7 +1144,7 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): d = g.degree(y) 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: + if d > 0: disc = discrim(glist) else: disc = [] @@ -1169,8 +1163,8 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): 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] + I1 = QQbar.gen() + segs = [(a[0] + I1 * a[1], b[0] + I1 * b[1]) for a, b in segs] vertices = list(set(flatten(segs))) tocacheverts = tuple([(glist, v) for v in vertices]) populate_roots_interval_cache(tocacheverts) @@ -1190,7 +1184,7 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): end_braid_computation = True except ChildProcessError: # hack to deal with random fails first time pass - B=BraidGroup(d) + B = BraidGroup(d) result = [] for path in geombasis: braidpath = B.one() @@ -1208,15 +1202,15 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): h0 = h.subs({x: p1}) h1 = F[y](h0) rt = h1.roots(QQbar, multiplicities=False) - roots_base += [(_,i) for _ in rt] + roots_base += [(_, i) for _ in rt] if not holdstrand: roots_base.sort() - strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} + strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} computebm0 = computebm or len(disc) == 0 if computebm0 and not holdstrand: return (result, strands) if computebm0 and holdstrand: - return (result,roots_base) + return (result, roots_base) if not computebm0 and not holdstrand: return strands if not computebm0 and holdstrand: @@ -1287,10 +1281,12 @@ def conjugate_positive_form(braid): cortas.append(res0) return cortas + @parallel def conjugate_positive_form_p(braid): return conjugate_positive_form(braid) + def braid2rels(L): r""" Return a minimal set of elements of ``F = FreeGroup(d)`` for a braid ``b`` which is the conjugate of @@ -1347,10 +1343,12 @@ def braid2rels(L): U = [_.Tietze() for _ in gb.relations()] return U + @parallel def braid2rels_p(L): return braid2rels(L) + 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. @@ -1430,11 +1428,11 @@ def relation(x, b): rel_h = flatten(rel_h, max_level=1) rel_v = [] for j, k in enumerate(vertical): - l = d + j + 1 + l1 = d + j + 1 br = bm[k - 1] for gen in F.gens(): j0 = gen.Tietze()[0] - rl = (l,) + (gen * br).Tietze() + (-l, -j0) + rl = (l1,) + (gen * br).Tietze() + (-l1, -j0) rel_v.append(rl) rel = rel_h + rel_v if projective: @@ -1563,6 +1561,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid d = bm[0].parent().strands() return fundamental_group_from_braid_mon(bm, degree=d, simplified=simplified, projective=projective, puiseux=puiseux) + def braid_monodromy_arrangement(flist): r""" Compute the braid monodromy of a projection of the curve @@ -1610,6 +1609,7 @@ def braid_monodromy_arrangement(flist): # return (braid_monodromy(f), {}) return braid_monodromy(f, arrangement=flist, holdstrand=False) + def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, braid_mon=None): r""" Compute the fundamental group of the complement of a curve @@ -1662,7 +1662,10 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis sage: (g, dic) == fundamental_group_arrangement(flist, braid_mon=BM) # optional - sirocco True sage: fundamental_group_arrangement(flist, simplified=False, braid_mon=BM) # optional - sirocco - (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {1: (1,), 2: (2,), 3: (-4, -3, -2, -1)}) + (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, + x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, + x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, + 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {1: (1,), 2: (2,), 3: (-4, -3, -2, -1)}) sage: fundamental_group_arrangement(flist, projective=True, braid_mon=BM) # optional - sirocco (Finitely presented group < x | >, {1: (1,), 2: (-1, -1, -1)}) @@ -1673,7 +1676,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis hyperplane arrangements defined over a number subfield of ``QQbar`` after applying a generic line section. """ - if len(flist)>0: + if len(flist) > 0: f = prod(flist) R = f.parent() else: @@ -1722,6 +1725,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis g1 = FreeGroup(n) / rels return (g1, dic1) + def strand_components(flist): r""" Compute only the assignment from strands to elements of ``flist``. @@ -1746,6 +1750,6 @@ def strand_components(flist): f = prod(flist) if len(flist) == 1: d = f.degree() - dic ={j + 1 : 1 for j in range(d)} + dic = {j + 1: 1 for j in range(d)} return dic return braid_monodromy(f, arrangement=flist, computebm=False) From 384283c7d1dfd42adf4a9bd5e1201043e2e0b0cf Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 27 Jun 2023 16:40:42 +0200 Subject: [PATCH 102/122] cleaning voronoi_cells and orient_circuit --- src/sage/schemes/curves/zariski_vankampen.py | 217 +++++++++---------- 1 file changed, 107 insertions(+), 110 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index ef141cfe795..9611596b5e9 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -176,7 +176,7 @@ def discrim(pols): OUTPUT: - A list with the values of the discriminant in `\QQbar`. + A tuple with the values of the discriminant in `\QQbar`. EXAMPLES:: @@ -184,12 +184,12 @@ def discrim(pols): sage: R. = QQ[] sage: flist = (y^3 + x^3 - 1, 2 * x + y) sage: discrim(flist) - [1, + (1, -0.500000000000000? - 0.866025403784439?*I, -0.500000000000000? + 0.866025403784439?*I, -0.522757958574711?, 0.2613789792873551? - 0.4527216721561923?*I, - 0.2613789792873551? + 0.4527216721561923?*I] + 0.2613789792873551? + 0.4527216721561923?*I) """ flist = tuple(pols) x, y = flist[0].parent().gens() @@ -204,12 +204,12 @@ def discrim_pairs(f, g): pairs = [(f, None) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) - rts = [] + rts = () poly = 1 for u in fdiscrim: h0 = u[1].radical() h1 = h0 // h0.gcd(poly) - rts += h1.roots(QQbar, multiplicities=False) + rts += tuple(h1.roots(QQbar, multiplicities=False)) poly = poly * h1 return rts @@ -272,6 +272,81 @@ def corrected_voronoi_diagram(points): return V +def orient_circuit(circuit, convex=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`` -- a boolean function, if set to ``True`` a simpler computation is made + + 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 + """ + 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(_ for _ in reversed(circuit_vertex)) + prec = 53 + 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(_ for _ in reversed(circuit_vertex)) + if totalangle > 0: + # return circuit + return circuit_vertex + prec *= 2 + + def voronoi_cells(V): r""" Compute the graph, the boundary graph, a base point, a positive orientation of the boundary graph, @@ -311,7 +386,7 @@ def voronoi_cells(V): sage: Ev.index(p) 7 sage: EC - [A vertex at (9/2, -9/2), + (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), @@ -320,25 +395,25 @@ def voronoi_cells(V): 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)] + 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 (11/4, 4), A vertex at (2000001/2000000, 500001/1000000), None)) + (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 """ @@ -347,13 +422,13 @@ def voronoi_cells(V): 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()) - EC0 = orient_circuit(E.eulerian_circuit()) - EC = [EC0[0][0]] + [e[1] for e in EC0] + 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()) - Greg = (Greg0[0][0],) + tuple(e[1] for e in Greg0) - DG.add_vertex((i, Greg)) + 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]] @@ -806,84 +881,6 @@ def braid_in_segment(glist, x0, x1, precision={}): return initialbraid * centralbraid * finalbraid -def orient_circuit(circuit, convex=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`` -- a boolean function, if set to ``True`` a simpler computation is made - - 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: 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)] - - """ - vectors = [v[1].vector() - v[0].vector() for v in circuit] - circuit_vertex = [circuit[0][0]] + [e[1] for e in circuit] - 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 [_ for _ in reversed(circuit_vertex)] - prec = 53 - 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 [_ for _ in reversed(circuit_vertex)] - if totalangle > 0: - return circuit - # return circuit_vertex - prec *= 2 - - def geometric_basis(G, E, EC, p, dual_graph): r""" Return a geometric basis, based on a vertex. @@ -966,7 +963,7 @@ def geometric_basis(G, E, EC, p, dual_graph): EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi) and v != ECi] if len(EI) > 0: q = ECi - connecting_path = EC[:i] + 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]) and v != EC[-i]] @@ -1040,10 +1037,10 @@ def geometric_basis(G, E, EC, p, dual_graph): if qi < ri: EC1 = [EC[j] for j in range(qi, ri)] + [_ for _ in reversed(cutpath)] - EC2 = cutpath + EC[ri + 1: -1] + EC[: qi + 1] + EC2 = cutpath + list(EC[ri + 1: -1] + EC[: qi + 1]) else: - EC1 = EC[qi:] + EC[1:ri] + [_ for _ in reversed(cutpath)] - EC2 = cutpath + EC[ri + 1:qi + 1] + EC1 = list(EC[qi:] + EC[1:ri]) + [_ for _ in reversed(cutpath)] + EC2 = cutpath + list(EC[ri + 1:qi + 1]) gb1 = geometric_basis(G1, E1, EC1, q, Gd1) gb2 = geometric_basis(G2, E2, EC2, q, Gd2) @@ -1163,8 +1160,8 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): for s in zip(p[:-1], p[1:]): if (s[1], s[0]) not in segs: segs.add((s[0], s[1])) - I1 = QQbar.gen() - segs = [(a[0] + I1 * a[1], b[0] + I1 * 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 = tuple([(glist, v) for v in vertices]) populate_roots_interval_cache(tocacheverts) From af31802833ffcfee6c80a62db421fcdcf29516f9 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 28 Jun 2023 11:10:58 +0200 Subject: [PATCH 103/122] lsp changes --- .../hyperplane_arrangement/arrangement.py | 58 +++++++------ src/sage/schemes/curves/affine_curve.py | 87 ++++++++++--------- 2 files changed, 75 insertions(+), 70 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 5f59595b7d6..b955a46c2ae 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -358,6 +358,7 @@ from sage.structure.richcmp import richcmp from sage.structure.unique_representation import UniqueRepresentation + class HyperplaneArrangementElement(Element): """ A hyperplane arrangement. @@ -368,6 +369,7 @@ class HyperplaneArrangementElement(Element): :class:`HyperplaneArrangementElement` instances directly, always use the parent. """ + def __init__(self, parent, hyperplanes, check=True, backend=None): """ Construct a hyperplane arrangement. @@ -907,16 +909,16 @@ def intersection_poset(self, element_label="int"): W = Vector space of dimension 2 over Rational Field] """ if element_label == "int": - def update(mapping, val, I): + def update(mapping, val, I0): mapping[val] = len(mapping) elif element_label == "subset": from sage.sets.set import Set - def update(mapping, val, I): + def update(mapping, val, I0): mapping[val] = Set(val) elif element_label == "subspace": - def update(mapping, val, I): - mapping[val] = I + def update(mapping, val, I0): + mapping[val] = I0 else: raise ValueError("invalid element label type") @@ -939,13 +941,13 @@ def update(mapping, val, I): for label, T in cur_level: edges = [] for i, H in enumerate(hyperplanes): - I = H.intersection(T) - if I is not None and I != T: + I0 = H.intersection(T) + if I0 is not None and I0 != T: try: - target = new_level[I] + target = new_level[I0] except KeyError: target = set(label) - new_level[I] = target + new_level[I0] = target target.add(i) edges.append(target) hasse[label] = edges @@ -2021,7 +2023,7 @@ def regions(self): for hyperplane in self: ieq = vector(R, hyperplane.dense_coefficient_list()) - pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be) + pos_half = Polyhedron(ieqs=[ieq], base_ring=R, backend=be) neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be) if not regions: # See comment above. @@ -2059,8 +2061,8 @@ def regions(self): else: # In this case, at least one of the vertices is not on the hyperplane. # So we check if any ray or line pokes the hyperplane. - if ( any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or - any(ieq[1:]*l[:] != 0 for l in region_lines)): + if (any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or + any(ieq[1:]*ll[:] != 0 for ll in region_lines)): splits = True if splits: @@ -2350,7 +2352,7 @@ def closed_faces(self, labelled=True): zero_half = Polyhedron(eqns=[ieq], base_ring=R, backend=be) # ``zero_half`` is the hyperplane ``hyperplane`` itself # (viewed as a polyhedron). - pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be) + pos_half = Polyhedron(ieqs=[ieq], base_ring=R, backend=be) neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be) subdivided = [] for signs, face in faces: @@ -2378,8 +2380,8 @@ def closed_faces(self, labelled=True): # inequalities are always equalities on it). Check for # this: zero_part_point = zero_part.representative_point() - for l, testhype in enumerate(hypes[:k]): - if signs[l] != 0: + for ll, testhype in enumerate(hypes[:k]): + if signs[ll] != 0: h = testhype.dense_coefficient_list() testval = R.sum(h[i+1] * gi for i, gi in enumerate(zero_part_point)) + h[0] if testval == 0: @@ -2514,9 +2516,9 @@ def face_product(self, F, G, normalize=True): if not normalize: return face # Look for ``I`` in ``self.closed_faces()``: - for I in self.closed_faces(): - if I[0] == tuple(signs): - return I[1] + for I0 in self.closed_faces(): + if I0[0] == tuple(signs): + return I0[1] def face_semigroup_algebra(self, field=None, names='e'): r""" @@ -2626,8 +2628,8 @@ def face_semigroup_algebra(self, field=None, names='e'): matrix_j = [] for i, si in enumerate(Fs): row_i = [zero] * N - sk = [sil if sil != 0 else sj[l] - for l, sil in enumerate(si)] + sk = [sil if sil != 0 else sj[ll] + for ll, sil in enumerate(si)] k = Fdict[tuple(sk)] row_i[k] = one matrix_j += row_i @@ -3154,7 +3156,7 @@ def orlik_solomon_algebra(self, base_ring=None, ordering=None, **kwds): """ if base_ring is None: base_ring = self.base_ring() - return self.matroid().orlik_solomon_algebra(base_ring, ordering,**kwds) + return self.matroid().orlik_solomon_algebra(base_ring, ordering, **kwds) def orlik_terao_algebra(self, base_ring=None, ordering=None, **kwds): """ @@ -3466,13 +3468,13 @@ def derivation_module_basis(self, algorithm="singular"): ....: else: ....: assert exponents(B) == exponents(Bp) """ - alg = algorithm # prevent possible changes to a global variable + alg = algorithm # prevent possible changes to a global variable if alg == "singular": # import sage.libs.singular.function_factory # syz = sage.libs.singular.function_factory.ff.syz f = self.defining_polynomial() - I = f + f.jacobian_ideal() - IS = I._singular_() + I0 = f + f.jacobian_ideal() + IS = I0._singular_() ISS = IS.syz() MSTD = ISS.mstd() basis = MSTD[2]._sage_().transpose().submatrix(0, 1) @@ -3481,7 +3483,7 @@ def derivation_module_basis(self, algorithm="singular"): # Check using Saito's criterion if det / f in f.parent().base_ring() and not det.is_zero(): return basis.rows() - except ValueError: # Non-square matrix or det = 0 + except ValueError: # Non-square matrix or det = 0 pass # Check if it is free if not self.is_free(algorithm=alg): @@ -3492,7 +3494,7 @@ def derivation_module_basis(self, algorithm="singular"): if alg == "BC": C = self.derivation_module_free_chain() if C is not None: - if not C: # C is an empty list + if not C: # C is an empty list S = self.parent().ambient_space().symmetric_space() return matrix.identity(S, self.dimension()).rows() from sage.misc.misc_c import prod @@ -3570,7 +3572,6 @@ def hyperplane_section(self, proj=True): raise TypeError('The arrangement is not central') n0 = self.dimension() r = self.n_hyperplanes() - A = self.parent() if not proj: H, perm0 = self.cone(permutation=True) H1, perm1 = H.hyperplane_section() @@ -3681,7 +3682,7 @@ def _fundamental_group_(self, proj=False): n = self.dimension() r = len(self) affine = n == 2 and not proj - projective = n==3 and self.is_central() and proj + projective = n == 3 and self.is_central() and proj if (n == 1 and not proj) or (n == 2 and proj and self.is_central()): r1 = r - proj G = FreeGroup(r1) / [] @@ -3709,7 +3710,7 @@ def _fundamental_group_(self, proj=False): L.append(p) G, dic = fundamental_group_arrangement(L, puiseux=True, projective=projective and not infinity) if infinity: - p = Permutation([r] + [j for j in range(1, r)] ) + p = Permutation([r] + [j for j in range(1, r)]) dic = {j: dic[p(j)] for j in range(1, r + 1)} return (G, dic) @@ -3788,6 +3789,7 @@ def fundamental_group(self, projective=False): dic = {j: dic[P(j)] for j in dic.keys()} return (H2, dic) + class HyperplaneArrangements(Parent, UniqueRepresentation): """ Hyperplane arrangements. diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index f16d0131b63..69b7268c3bb 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -180,6 +180,7 @@ class AffineCurve(Curve_generic, AlgebraicScheme_subscheme_affine): sage: C = Curve([x^2 - z, z - 8*x], A); C # optional - sage.rings.finite_rings Affine Curve over Finite Field of size 7 defined by x^2 - z, -x + z """ + def __init__(self, A, X): r""" Initialize. @@ -271,6 +272,7 @@ class AffinePlaneCurve(AffineCurve): """ Affine plane curves. """ + def __init__(self, A, f): r""" Initialize. @@ -391,14 +393,14 @@ def local_coordinates(self, pt, n): p = F.characteristic() x0 = F(pt[0]) y0 = F(pt[1]) - astr = ["a"+str(i) for i in range(1,2*n)] - x,y = R.gens() + astr = ["a"+str(i) for i in range(1, 2*n)] + x, y = R.gens() R0 = PolynomialRing(F, 2 * n + 2, names=[str(x), str(y), "t"] + astr) vars0 = R0.gens() t = vars0[2] - yt = y0*t**0+add([vars0[i]*t**(i-2) for i in range(3,2*n+2)]) + yt = y0*t**0+add([vars0[i]*t**(i-2) for i in range(3, 2*n+2)]) xt = x0+t - ft = f(xt,yt) + ft = f(xt, yt) S = singular S.eval('ring s = '+str(p)+','+str(R0.gens())+',lp;') S.eval('poly f = '+str(ft) + ';') @@ -419,13 +421,13 @@ def local_coordinates(self, pt, n): if str(y) in x: if x.replace(str(y), ""): i = x.find("-") - if i>0: - vals.append([eval(x[1:i]),x[:i],F(eval(x[i+1:]))]) + if i > 0: + vals.append([eval(x[1:i]), x[:i], F(eval(x[i+1:]))]) i = x.find("+") - if i>0: - vals.append([eval(x[1:i]),x[:i],-F(eval(x[i+1:]))]) + if i > 0: + vals.append([eval(x[1:i]), x[:i], -F(eval(x[i+1:]))]) else: - vals.append([eval(str(y)[1:]),str(y),F(0)]) + vals.append([eval(str(y)[1:]), str(y), F(0)]) vals.sort() return [x0 + t, y0 + add(v[2] * t**(j+1) for j, v in enumerate(vals))] @@ -465,8 +467,8 @@ def plot(self, *args, **kwds): sage: C.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""" @@ -657,9 +659,9 @@ def tangents(self, P, factor=True): vars = self.ambient_space().gens() coords = [vars[0] + P[0], vars[1] + P[1]] f = f(coords) - coords = [vars[0] - P[0], vars[1] - P[1]] # coords to change back with - deriv = [f.derivative(vars[0],i).derivative(vars[1], r-i)([0,0]) for i in range(r+1)] - T = sum([binomial(r,i)*deriv[i]*(vars[0])**i*(vars[1])**(r-i) for i in range(r+1)]) + coords = [vars[0] - P[0], vars[1] - P[1]] # coords to change back with + deriv = [f.derivative(vars[0], i).derivative(vars[1], r-i)([0, 0]) for i in range(r+1)] + T = sum([binomial(r, i)*deriv[i]*(vars[0])**i*(vars[1])**(r-i) for i in range(r+1)]) if not factor: return [T(coords)] if self.base_ring() == QQbar: @@ -670,13 +672,13 @@ def tangents(self, P, factor=True): if t > 0: fact.append(vars[0]) # divide T by that power of vars[0] - T = self.ambient_space().coordinate_ring()(dict([((v[0] - t,v[1]), h) for (v,h) in T.dict().items()])) + T = self.ambient_space().coordinate_ring()(dict([((v[0] - t, v[1]), h) for (v, h) in T.dict().items()])) t = min([e[1] for e in T.exponents()]) # vars[1] divides T if t > 0: fact.append(vars[1]) # divide T by that power of vars[1] - T = self.ambient_space().coordinate_ring()(dict([((v[0],v[1] - t), h) for (v,h) in T.dict().items()])) + T = self.ambient_space().coordinate_ring()(dict([((v[0], v[1] - t), h) for (v, h) in T.dict().items()])) # T is homogeneous in var[0], var[1] if nonconstant, so dehomogenize if T not in self.base_ring(): if T.degree(vars[0]) > 0: @@ -689,7 +691,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""" @@ -741,7 +743,7 @@ def is_ordinary_singularity(self, P): """ r = self.multiplicity(P) if r < 2: - raise TypeError("(=%s) is not a singular point of (=%s)" % (P,self)) + raise TypeError("(=%s) is not a singular point of (=%s)" % (P, self)) T = self.tangents(P, factor=False)[0] vars = self.ambient_space().gens() @@ -990,7 +992,7 @@ def projection(self, indices, AS=None): indices = [AA.gens().index(f) for f in indices] indices.sort() else: - indices = [int(i) for i in indices] # type checking + indices = [int(i) for i in indices] # type checking indices.sort() if indices[0] < 0 or indices[-1] > n - 1: raise ValueError("index values must be between 0 and one " @@ -1009,10 +1011,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) @@ -1081,7 +1083,7 @@ def plane_projection(self, AP=None): # possible projections for i in range(0, n - 1): for j in range(i + 1, n): - L = self.projection([i,j], AP) + L = self.projection([i, j], AP) if isinstance(L[1], Curve_generic): return L @@ -1519,6 +1521,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 +1723,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): @@ -1857,7 +1860,7 @@ def riemann_surface(self, **kwargs): with 53 bits of precision """ from sage.schemes.riemann_surfaces.riemann_surface import RiemannSurface - S = RiemannSurface(self.defining_polynomial(),**kwargs) + S = RiemannSurface(self.defining_polynomial(), **kwargs) S._curve = self return S @@ -1975,7 +1978,7 @@ def rational_points(self, algorithm="enum"): if algorithm == "enum": f = self.defining_polynomial() K = f.parent().base_ring() - return sorted((self((x,y)) for x in K for y in K if f(x,y) == 0)) + return sorted((self((x, y)) for x in K for y in K if f(x, y) == 0)) F = self.base_ring() if not F.is_prime_field(): @@ -2174,18 +2177,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 @@ -2255,7 +2258,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 @@ -2345,7 +2348,7 @@ def _singularities(self): if p == q: places.append(place) break - else: # new singularity + else: # new singularity points.append((p, [place])) return points @@ -2406,18 +2409,18 @@ def place_to_closed_point(self, place): # implement an FGLM-like algorithm e = [0 for i in range(R.ngens())] basis = [R.one()] - basis_vecs = [to_V(k.one())] # represent as a vector + basis_vecs = [to_V(k.one())] # represent as a vector gens = [] gens_lts = [] terminate = False - while True: # check FGLM termination condition + while True: # check FGLM termination condition # compute next exponent in degree reverse lexicographical order j = R.ngens() - 1 while j > 0 and not e[j]: j -= 1 - if not j: # j is zero + if not j: # j is zero if terminate: break terminate = True @@ -2435,11 +2438,11 @@ def place_to_closed_point(self, place): prod = 1 for i in range(R.ngens()): prod *= coords[i]**e[i] - vec = to_V(to_k(prod)) # represent as a vector + vec = to_V(to_k(prod)) # represent as a vector mat = matrix(basis_vecs) try: s = mat.solve_left(vec) - except ValueError: # no solution + except ValueError: # no solution basis.append(m) basis_vecs.append(vec) terminate = False @@ -2688,9 +2691,9 @@ def closed_points(self, degree=1): for place in places_above: try: p = self.place_to_closed_point(place) - except ValueError: # place is at infinity + except ValueError: # place is at infinity continue - assert p.degree() == degree # sanity check + assert p.degree() == degree # sanity check points.append(p) return points From d5502c18edda10e635e343f3fbcb70460810e8b5 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 28 Jun 2023 15:36:15 +0200 Subject: [PATCH 104/122] simplify roots_interval --- src/sage/schemes/curves/zariski_vankampen.py | 72 +++++++++----------- 1 file changed, 34 insertions(+), 38 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9611596b5e9..4080918d12a 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -66,7 +66,7 @@ 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): @@ -332,7 +332,7 @@ def orient_circuit(circuit, convex=False): return circuit_vertex elif pr < 0: # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) - return tuple(_ for _ in reversed(circuit_vertex)) + return tuple(reversed(circuit_vertex)) prec = 53 while True: CIF = ComplexIntervalField(prec) @@ -340,7 +340,7 @@ def orient_circuit(circuit, convex=False): for i in range(len(vectors))) if totalangle < 0: # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) - return tuple(_ for _ in reversed(circuit_vertex)) + return tuple(reversed(circuit_vertex)) if totalangle > 0: # return circuit return circuit_vertex @@ -619,14 +619,14 @@ def fieldI(F0): @parallel -def roots_interval(pols, x0): +def roots_interval(f, x0): """ Find disjoint intervals that isolate the roots of a polynomial for a fixed value of the first variable. INPUT: - - ``pols`` -- a list or a tuple of bivariate squarefree polynomials + - ``f`` -- a bivariate squarefree polynomial - ``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 @@ -643,8 +643,8 @@ def roots_interval(pols, x0): sage: R. = QQ[] sage: K = fieldI(QQ) sage: f = y^3 - x^2 - sage: flist = (f.change_ring(K), ) - sage: ri = roots_interval(flist, 1) + sage: f = f.change_ring(K) + sage: ri = roots_interval(f, 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, 138907099/160396102*I - 1/2: -1.? + 1.?*I, @@ -663,15 +663,10 @@ def roots_interval(pols, x0): -0.933012701892219 + 1.29903810567666*I, -0.0669872981077806 + 0.433012701892219*I)] """ - flist = tuple(pols) - f = prod(flist) F1 = f.base_ring() x, y = f.parent().gens() - Ux = [F1[y](_.subs({x: F1(x0)})) for _ in flist] fx = F1[y](f.subs({x: F1(x0)})) - roots = [] - for pol in Ux: - roots += pol.roots(QQbar, multiplicities=False) + roots = fx.roots(QQbar, multiplicities=False) result = {} for i, r in enumerate(roots): prec = 53 @@ -696,7 +691,7 @@ def roots_interval(pols, x0): return result -def roots_interval_cached(flist, x0): +def roots_interval_cached(f, x0): r""" Cached version of :func:`roots_interval`. @@ -707,22 +702,22 @@ def roots_interval_cached(flist, x0): sage: K = fieldI(QQ) sage: f = y^3 - x^2 sage: f = f.change_ring(K) - sage: ((f, ), 1) in roots_interval_cache + sage: (f, 1) in roots_interval_cache False - sage: ri = roots_interval_cached((f, ), 1) + sage: ri = roots_interval_cached(f, 1) sage: ri {-138907099/160396102*I - 1/2: -1.? - 1.?*I, 138907099/160396102*I - 1/2: -1.? + 1.?*I, 1: 1.? + 0.?*I} - sage: ((f, ), 1) in roots_interval_cache + sage: (f, 1) in roots_interval_cache True """ global roots_interval_cache try: - return roots_interval_cache[(flist, x0)] + return roots_interval_cache[(f, x0)] except KeyError: - result = roots_interval(flist, x0) - roots_interval_cache[(flist, x0)] = result + result = roots_interval(f, x0) + roots_interval_cache[(f, x0)] = result return result @@ -733,7 +728,7 @@ def populate_roots_interval_cache(inputs): INPUT: - - ``inputs`` -- a list of tuples ``(flist, x0)`` + - ``inputs`` -- a list of tuples ``(f, x0)`` EXAMPLES:: @@ -742,12 +737,12 @@ def populate_roots_interval_cache(inputs): sage: K=fieldI(QQ) sage: f = y^5 - x^2 sage: f = f.change_ring(K) - sage: ((f, ), 3) in roots_interval_cache + sage: (f, 3) in roots_interval_cache False - sage: populate_roots_interval_cache([((f, ), 3)]) - sage: ((f, ), 3) in roots_interval_cache + sage: populate_roots_interval_cache([(f, 3)]) + sage: (f, 3) in roots_interval_cache True - sage: roots_interval_cache[((f,), 3)] + sage: roots_interval_cache[(f, 3)] {-1.255469441943070? - 0.9121519421827974?*I: -2.? - 1.?*I, -1.255469441943070? + 0.9121519421827974?*I: -2.? + 1.?*I, 0.4795466549853897? - 1.475892845355996?*I: 1.? - 2.?*I, @@ -769,7 +764,7 @@ def populate_roots_interval_cache(inputs): @parallel -def braid_in_segment(glist, x0, x1, precision={}): +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``. @@ -821,14 +816,15 @@ def braid_in_segment(glist, x0, x1, precision={}): sage: B # optional - sirocco s5*s3^-1 """ + 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 = {} - if precision == {}: # new - precision = {f: 53 for f in glist} # new + if not precision1: # new + precision1 = {f: 53 for f in glist} # new y0s = [] for f in glist: if f.variables() == (y,): @@ -838,22 +834,22 @@ def braid_in_segment(glist, x0, x1, precision={}): y0sf = f0.roots(QQbar, multiplicities=False) y0s += list(y0sf) 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 + 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(), precision[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(glist, X0) - finalintervals = roots_interval_cached(glist, X1) + initialintervals = roots_interval_cached(g, X0) + finalintervals = roots_interval_cached(g, X1) I1 = QQbar.gen() for cs in complexstrands: ip = cs[0][1] + I1 * cs[0][2] @@ -864,8 +860,8 @@ def braid_in_segment(glist, x0, x1, precision={}): initialstrands.append([(0, center.real(), center.imag()), (1, cs[0][1], cs[0][2])]) matched += 1 if matched != 1: - precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision=precision) # new + 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(): @@ -873,8 +869,8 @@ def braid_in_segment(glist, x0, x1, precision={}): finalstrands.append([(0, cs[-1][1], cs[-1][2]), (1, center.real(), center.imag())]) matched += 1 if matched != 1: - precision = {f: precision[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision=precision) # new + 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) @@ -1163,7 +1159,7 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): 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 = tuple([(glist, v) for v in vertices]) + tocacheverts = tuple([(g, v) for v in vertices]) populate_roots_interval_cache(tocacheverts) end_braid_computation = False while not end_braid_computation: From 77bb150d1e5c45d99f0ed1d832b5167323ef1d5b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 28 Jun 2023 21:57:14 +0200 Subject: [PATCH 105/122] small corrections --- src/sage/schemes/curves/zariski_vankampen.py | 21 +++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 4080918d12a..3d72fb4f05b 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1142,14 +1142,15 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): else: disc = [] if len(disc) == 0: - computebm = False + computebm0 = False result = [] p0 = (0, 0) else: + computebm0 = computebm V = corrected_voronoi_diagram(tuple(disc)) G, E, p, EC, DG = voronoi_cells(V) p0 = (p[0], p[1]) - if computebm: + if computebm0: geombasis = geometric_basis(G, E, EC, p, DG) segs = set() for p in geombasis: @@ -1199,7 +1200,7 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): if not holdstrand: roots_base.sort() strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} - computebm0 = computebm or len(disc) == 0 + computebm0 = computebm0 or len(disc) == 0 if computebm0 and not holdstrand: return (result, strands) if computebm0 and holdstrand: @@ -1392,8 +1393,9 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv 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 > """ - vertical.sort() - v = len(vertical) + vertical0 = [_ for _ in vertical] + vertical0.sort() + v = len(vertical0) if bm == []: d = degree else: @@ -1402,7 +1404,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv return None F = FreeGroup(d) Fv = FreeGroup(d+v) - bmh = [br for j, br in enumerate(bm) if j + 1 not in vertical] + bmh = [br for j, br in enumerate(bm) if j + 1 not in vertical0] @parallel def relation(x, b): @@ -1411,8 +1413,9 @@ def relation(x, b): 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: - simplified = False + 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) @@ -1420,7 +1423,7 @@ def relation(x, b): rel_h = [r[1] for r in relations_h] rel_h = flatten(rel_h, max_level=1) rel_v = [] - for j, k in enumerate(vertical): + for j, k in enumerate(vertical0): l1 = d + j + 1 br = bm[k - 1] for gen in F.gens(): @@ -1431,7 +1434,7 @@ def relation(x, b): if projective: rel.append(prod(Fv.gens()).Tietze()) G = Fv / rel - if simplified: + if simplified0: return G.simplified() return G From fd8aa9c03ea817b6a3349daa16e13f661cc9e1e8 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 18 Jul 2023 13:43:56 +0200 Subject: [PATCH 106/122] braid_monodromy --- src/sage/schemes/curves/zariski_vankampen.py | 276 ++++++++----------- 1 file changed, 117 insertions(+), 159 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 3d72fb4f05b..159d9885932 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -877,7 +877,7 @@ def braid_in_segment(glist, x0, x1, precision=dict()): return initialbraid * centralbraid * finalbraid -def geometric_basis(G, E, EC, p, dual_graph): +def geometric_basis(G, E, EC0, p, dual_graph): r""" Return a geometric basis, based on a vertex. @@ -888,7 +888,7 @@ def geometric_basis(G, E, EC, p, dual_graph): - ``E`` -- a subgraph of ``G`` which is a cycle; it corresponds to the bounded edges touching an unbounded region of a Voronoi Diagram - - ``EC`` -- A counterclockwise orientation of the vertices of ``E`` + - ``EC0`` -- A counterclockwise orientation of the vertices of ``E`` - ``p`` -- a vertex of ``E`` @@ -946,8 +946,8 @@ def geometric_basis(G, E, EC, p, dual_graph): A vertex at (-2, 2), A vertex at (-2, -2)]] """ - i = EC.index(p) - EC = EC[i:-1] + EC[:i + 1] # A counterclockwise eulerian circuit on the boundary, starting and ending 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] @@ -970,14 +970,11 @@ def geometric_basis(G, E, EC, p, dual_graph): distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in EI] r = max(distancequotients)[1] cutpath = Internal.shortest_path(q, r) - cutpath_in_E = False - i = 1 - while not cutpath_in_E and i < len(cutpath) - 1: - cutpath_in_E = cutpath[i] in EC - if cutpath_in_E: - r = cutpath[i] + for i, v in enumerate(cutpath): + if i > 0 and v in EC: + r = v cutpath = cutpath[:i+1] - i = i+1 + break qi = EC.index(q) ri = EC.index(r) Ecut = copy(E) @@ -993,12 +990,19 @@ def geometric_basis(G, E, EC, p, dual_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.neighbors(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) - cp1 = [(e, cutpath[i + 1], None) for i, e in enumerate(cutpath[: -1])] - cp1 += [(cutpath[i + 1], e, None) for i, e in enumerate(cutpath[: -1])] - borra = [_ for _ in Gd.edges(sort=True) if _[2] in cp1] - Gd.delete_edges(borra) + to_delete = [e for e in Gd.edges(sort=True) if e[0] in cutpath and e[1] in cutpath] + Gd.delete_edges(to_delete) Gd1, Gd2 = Gd.connected_components_subgraphs() edges_2 = [] vertices_2 = [] @@ -1020,22 +1024,11 @@ def geometric_basis(G, E, EC, p, dual_graph): G1, G2 = G2, G1 Gd1, Gd2 = Gd2, Gd1 - for v in [q, r]: - for n in E.neighbors(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) - if qi < ri: - EC1 = [EC[j] for j in range(qi, ri)] + [_ for _ in reversed(cutpath)] + 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]) + [_ for _ in reversed(cutpath)] + EC1 = list(EC[qi:] + EC[1:ri]) + list(reversed(cutpath)) EC2 = cutpath + list(EC[ri + 1:qi + 1]) gb1 = geometric_basis(G1, E1, EC1, q, Gd1) @@ -1057,7 +1050,46 @@ def geometric_basis(G, E, EC, p, dual_graph): return resul -def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): +def strand_components(f, flist, p1): + r""" + Compute only the assignment from strands to elements of ``flist``. + + INPUT: + + - ``flist`` -- a list of polynomial with two variables, over a number field + with an embedding in the complex numbers + + OUTPUT: + + - A dictionary attaching a number `i` (strand) to a number `j` (a polynomial in the list). + + EXAMPLES:: + + sage: from sage.schemes.curves.zariski_vankampen import strand_components # optional - sirocco + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x + 3 * y - 5] + sage: strand_components(flist) # optional - sirocco + {1: 1, 2: 2, 3: 1, 4: 1} + """ + x, y = f.parent().gens() + F = flist[0].base_ring() + if len(flist) == 1: + d = f.degree() + dic = {j + 1: 1 for j in range(d)} + return dic + 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 + 1: par[1] + 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. @@ -1066,28 +1098,16 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): - ``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``, - in order to provide information for ``braid_monodromy_arrangement``. + - ``arrangement`` -- an optional tuple of polynomials whose product equals ``f``. - - ``computebm`` -- an optional boolean variable (default ``True``) to actually - compute the braid monodromy - - - ``holdstrand`` -- an optional boolean variable (default ``False``) to stop - the computation of string assignment. OUTPUT: - If ``computebm`` is set to ``True`` and ``arrangement`` contains only one element, a list of braids. + A list of braids and a dictionary. 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``. - - If ``computebm`` is set to ``True`` and ``arrangement`` contains more than one element, - either assignment of the roots over the base point to elements of - ''arrangement`` (``holdstrand`` set to ``True``) or - the assignment of strands to elements of ``arrangement`` (``holdstrand`` set to ``True``). - - If ``computebm`` is set to ``False`` the second part of the above is given (depending on ``holdstrand``). + 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:: @@ -1106,28 +1126,20 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): s1*s0*s2*s0^-1*s2*s1^-1] sage: flist = (x^2 - y^3, x + 3 * y - 5) sage: bm1 = braid_monodromy(f, arrangement=flist) # optional - sirocco - sage: bm1[0] == bm # optional - sirocco + sage: bm1[0] == bm[0] # optional - sirocco True sage: bm1[1] # optional - sirocco {1: 1, 2: 2, 3: 1, 4: 1} - sage: braid_monodromy(f, arrangement=flist, computebm=False) == bm1[1] # optional - sirocco - True - sage: bm2 = braid_monodromy(f, arrangement=flist, holdstrand=True) # optional - sirocco - sage: bm2[0] == bm # optional - sirocco - True - sage: bm2[1] == braid_monodromy(f, arrangement=flist, computebm=False, holdstrand=True) # optional - sirocco - True """ global roots_interval_cache - if arrangement == (): - arrangement1 = (f, ) - else: - arrangement1 = arrangement F = fieldI(f.base_ring()) I1 = F(QQbar.gen()) f = f.change_ring(F) - arrangement1 = [_.change_ring(F) for _ in arrangement1] + if arrangement == (): + arrangement1 = (f,) + else: + arrangement1 = tuple(_.change_ring(F) for _ in arrangement) x, y = f.parent().gens() glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor()) g = f.parent()(prod(glist)) @@ -1142,74 +1154,52 @@ def braid_monodromy(f, arrangement=(), computebm=True, holdstrand=False): else: disc = [] if len(disc) == 0: - computebm0 = False - result = [] - p0 = (0, 0) - else: - computebm0 = computebm - V = corrected_voronoi_diagram(tuple(disc)) - G, E, p, EC, DG = voronoi_cells(V) - p0 = (p[0], p[1]) - if computebm0: - 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])) - 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 = tuple([(g, v) for v in vertices]) - populate_roots_interval_cache(tocacheverts) - 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() - for i in range(len(path) - 1): - x0 = tuple(path[i].vector()) - x1 = tuple(path[i + 1].vector()) - braidpath = braidpath * segsbraids[(x0, x1)] - result.append(braidpath) - if len(arrangement1) == 1: - return result + p1 = F(0) + roots_base, strands = strand_components(g, arrangement1, p1) + return ([], strands) + V = corrected_voronoi_diagram(tuple(disc)) + G, E, p, EC, DG = voronoi_cells(V) + p0 = (p[0], p[1]) p1 = p0[0] + I1 * p0[1] - strands = {} - roots_base = [] - for i, h in enumerate(arrangement1): - h0 = h.subs({x: p1}) - h1 = F[y](h0) - rt = h1.roots(QQbar, multiplicities=False) - roots_base += [(_, i) for _ in rt] - if not holdstrand: - roots_base.sort() - strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} - computebm0 = computebm0 or len(disc) == 0 - if computebm0 and not holdstrand: - return (result, strands) - if computebm0 and holdstrand: - return (result, roots_base) - if not computebm0 and not holdstrand: - return strands - if not computebm0 and holdstrand: - return roots_base - + 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])) + 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 = tuple([(g, v) for v in vertices]) + populate_roots_interval_cache(tocacheverts) + 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() + for i in range(len(path) - 1): + x0 = tuple(path[i].vector()) + x1 = tuple(path[i + 1].vector()) + braidpath = braidpath * segsbraids[(x0, x1)] + result.append(braidpath) + return (result, strands) def conjugate_positive_form(braid): r""" @@ -1691,14 +1681,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis flist1 = [g.subs({x: x + y}) for g in flist] f = prod(flist1) if braid_mon is None: - if len(flist1) == 1: - bm = braid_monodromy_arrangement(flist1) - dic = {j + 1: 1 for j in range(len(flist))} - elif len(flist1) == 0: + if len(flist1) == 0: bm = [] dic = dict() else: - bm, dic = braid_monodromy_arrangement(flist1) + bm, dic = braid_monodromy(f, flist1) else: bm, dic = braid_mon g = fundamental_group(f, simplified=False, projective=projective, puiseux=puiseux, braid_mon=bm) @@ -1720,32 +1707,3 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis rels = [_.Tietze() for _ in g1.relations()] g1 = FreeGroup(n) / rels return (g1, dic1) - - -def strand_components(flist): - r""" - Compute only the assignment from strands to elements of ``flist``. - - INPUT: - - - ``flist`` -- a list of polynomial with two variables, over a number field - with an embedding in the complex numbers - - OUTPUT: - - - A dictionary attaching a number `i` (strand) to a number `j` (a polynomial in the list). - - EXAMPLES:: - - sage: from sage.schemes.curves.zariski_vankampen import strand_components # optional - sirocco - sage: R. = QQ[] - sage: flist = [x^2 - y^3, x + 3 * y - 5] - sage: strand_components(flist) # optional - sirocco - {1: 1, 2: 2, 3: 1, 4: 1} - """ - f = prod(flist) - if len(flist) == 1: - d = f.degree() - dic = {j + 1: 1 for j in range(d)} - return dic - return braid_monodromy(f, arrangement=flist, computebm=False) From 7539b55972519132733d51ce7e83feecfb8d1667 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 18 Jul 2023 18:05:25 +0200 Subject: [PATCH 107/122] correction after changing braid_monodromy --- .../hyperplane_arrangement/arrangement.py | 36 +++---- src/sage/schemes/curves/affine_curve.py | 6 +- src/sage/schemes/curves/zariski_vankampen.py | 102 ++++++------------ 3 files changed, 54 insertions(+), 90 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index b955a46c2ae..cb9b88ad64a 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -329,7 +329,7 @@ arrangements. """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2013 David Perkinson # Volker Braun # @@ -338,7 +338,7 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** +# ***************************************************************************** # Possible extensions for hyperplane_arrangement.py: # - the big face lattice @@ -3653,7 +3653,7 @@ def _fundamental_group_(self, proj=False): x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, x0*x1^-1*x0^-1*x3^-1*x1*x3, - x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > + x4^-1*x3^-1*x1*x3*x4*x3^-1*x1^-1*x3 > sage: dic # optional - sirocco {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} sage: H=A(x,y,x+y) @@ -3664,13 +3664,14 @@ def _fundamental_group_(self, proj=False): (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) sage: H = hyperplane_arrangements.braid(4).essentialization() # optional - sage.graphs sage: H._fundamental_group_(proj=True) # optional - sage.graphs, sirocco - (Finitely presented group < x0, x1, x2, x3, x4 | x1*x3*x1^-1*x3^-1, - x0*x2*x0^-1*x2^-1, - x4^-1*x1^-1*x0^-1*x4*x0*x1, - x3*x4*x2*x3^-1*x2^-1*x4^-1, - x3*x4^-1*x3^-1*x2^-1*x4*x2, - x4*x0*x1^-1*x0^-1*x4^-1*x1 >, - {1: (5,), 2: (1,), 3: (2,), 4: (3,), 5: (4,), 6: (-2, -1, -4, -3, -5)}) + (Finitely presented group < x0, x1, x2, x3, x4 | + x0*x2*x0^-1*x2^-1, + x1*x3*x1^-1*x3^-1, + x1*x4*x1^-1*x0^-1*x4^-1*x0, + x4*x2*x3*x2^-1*x4^-1*x3^-1, + x4^-1*x1^-1*x0*x1*x4*x0^-1, + x3*x4^-1*x3^-1*x2^-1*x4*x2 >, + {1: (5,), 2: (1,), 3: (2,), 4: (3,), 5: (4,), 6: (-1, -5, -4, -2, -3)}) .. WARNING:: @@ -3752,7 +3753,7 @@ def fundamental_group(self, projective=False): x0*x1*x3*x0^-1*x3^-1*x1^-1, x0*x2*x4*x2^-1*x0^-1*x4^-1, x0*x1^-1*x0^-1*x3^-1*x1*x3, - x4*x3^-1*x1*x3*x4^-1*x3^-1*x1^-1*x3 > + x4^-1*x3^-1*x1*x3*x4*x3^-1*x1^-1*x3 > sage: dic # optional - sirocco {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} sage: H=A(x,y,x+y) @@ -3763,13 +3764,12 @@ def fundamental_group(self, projective=False): (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) sage: H = hyperplane_arrangements.braid(4) # optional - sage.groups sage: H.fundamental_group(projective=True) # optional - sirocco, sage.groups - (Finitely presented group < x0, x1, x2, x3, x4 | x0*x3*x0^-1*x3^-1, - x2*x4^-1*x2^-1*x4, - x2*x3*x1*x3^-1*x2^-1*x1^-1, - x1*x4*x0*x1^-1*x0^-1*x4^-1, - x3^-1*x2^-1*x1^-1*x3*x1*x2, - x0^-1*x1*x4*x0*x4^-1*x1^-1 >, - {1: (5,), 2: (1,), 3: (2,), 4: (3,), 5: (-4, -5, -3, -2, -1), 6: (4,)}) + (Finitely presented group < x0, x1, x2, x3, x4 | x0*x2*x0^-1*x2^-1, x1*x3*x1^-1*x3^-1, + x1*x4*x1^-1*x0^-1*x4^-1*x0, + x4*x2*x3*x2^-1*x4^-1*x3^-1, + x4^-1*x1^-1*x0*x1*x4*x0^-1, + x3*x4^-1*x3^-1*x2^-1*x4*x2 >, + {1: (5,), 2: (1,), 3: (2,), 4: (-1, -5, -4, -2, -3), 5: (4,), 6: (3,)}) sage: H = hyperplane_arrangements.coordinate(5) sage: g = H.fundamental_group(projective=True)[0] # optional - sirocco sage: g.is_abelian(), g.abelian_invariants() # optional - sirocco diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 69b7268c3bb..d1cef0fbfbf 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -114,14 +114,14 @@ - Kwankyu Lee (2019-05): added integral affine curves """ -#***************************************************************************** +# ***************************************************************************** # Copyright (C) 2005 William Stein # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ -#***************************************************************************** +# ***************************************************************************** from sage.misc.lazy_attribute import lazy_attribute from sage.misc.cachefunc import cached_method @@ -1843,7 +1843,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""" diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 159d9885932..fc25f805524 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -26,7 +26,7 @@ sage: R. = QQ[] sage: f = y^3 + x^3 - 1 sage: braid_monodromy(f) # optional - sirocco - [s1*s0, s1*s0, s1*s0] + ([s1*s0, s1*s0, s1*s0], {1: 1, 2: 1, 3: 1}) sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ @@ -1001,7 +1001,7 @@ def geometric_basis(G, E, EC0, p, dual_graph): 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[0] in cutpath and e[1] in cutpath] + 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 = [] @@ -1056,27 +1056,31 @@ def strand_components(f, flist, p1): INPUT: - - ``flist`` -- a list of polynomial with two variables, over a number field + - ``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 dictionary attaching a number `i` (strand) to a number `j` (a polynomial in the list). + - 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: from sage.schemes.curves.zariski_vankampen import strand_components # optional - sirocco sage: R. = QQ[] sage: flist = [x^2 - y^3, x + 3 * y - 5] - sage: strand_components(flist) # optional - sirocco - {1: 1, 2: 2, 3: 1, 4: 1} + sage: strand_components(prod(flist), flist, 1) # optional - sirocco + ([(-0.500000000000000? - 0.866025403784439?*I, 0), + (-0.500000000000000? + 0.866025403784439?*I, 0), + (1, 0), (1.333333333333334?, 1)], {1: 1, 2: 1, 3: 1, 4: 2}) """ x, y = f.parent().gens() F = flist[0].base_ring() - if len(flist) == 1: - d = f.degree() - dic = {j + 1: 1 for j in range(d)} - return dic strands = {} roots_base = [] for i, h in enumerate(flist): @@ -1114,16 +1118,21 @@ def braid_monodromy(f, arrangement=()): 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: from sage.schemes.curves.zariski_vankampen import braid_monodromy sage: R. = QQ[] sage: f = (x^2 - y^3) * (x + 3 * y - 5) sage: bm = braid_monodromy(f); bm # 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] + ([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], {1: 1, 2: 1, 3: 1, 4: 1}) sage: flist = (x^2 - y^3, x + 3 * y - 5) sage: bm1 = braid_monodromy(f, arrangement=flist) # optional - sirocco sage: bm1[0] == bm[0] # optional - sirocco @@ -1201,6 +1210,7 @@ def braid_monodromy(f, arrangement=()): result.append(braidpath) 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. @@ -1476,8 +1486,8 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid sage: fundamental_group(f, projective=True) # optional - sirocco Finitely presented group < x0 | x0^3 > sage: bm = braid_monodromy(f); bm # optional - sirocco - [(s1*s0)^2] - sage: fundamental_group(f, braid_mon=bm) # optional - sirocco + ([(s1*s0)^2], {1: 1, 2: 1, 3: 1}) + sage: fundamental_group(f, braid_mon=bm[0]) # optional - sirocco Finitely presented group < x1, x2 | x1*x2*x1^-1*x2^-1*x1^-1*x2 > :: @@ -1521,8 +1531,9 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid sage: R. = QQ[] sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) sage: bm = braid_monodromy(f); print(bm) # optional - sirocco - [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(f, projective=True, braid_mon=bm); g # optional - sirocco + ([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], + {1: 1, 2: 1, 3: 1, 4: 1}) + sage: g = fundamental_group(f, projective=True, braid_mon=bm[0]); g # optional - sirocco 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()) # optional - sirocco 12 (4,) @@ -1538,7 +1549,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid while g.degree(y) < g.degree(): g = g.subs({x: x + y}) if braid_mon is None: - bm = braid_monodromy(g) + bm = braid_monodromy(g)[0] else: bm = braid_mon if bm == []: @@ -1548,54 +1559,6 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid return fundamental_group_from_braid_mon(bm, degree=d, simplified=simplified, projective=projective, puiseux=puiseux) -def braid_monodromy_arrangement(flist): - r""" - Compute the braid monodromy of a projection of the curve - defined by a list of polynomials with the extra information about the correspondence of strands - and elements of the list. - - INPUT: - - - ``flist`` -- a tuple of polynomial with two variables, over a number field - with an embedding in the complex numbers - - - OUTPUT: - - - A list of braids. The braids correspond to paths based in the same point; - each of this paths is the conjugated of a loop around one of the points - in the discriminant of the projection of ``f``. - - - A dictionary attaching a number ``i`` (strand) to a number ``j`` (a polynomial in the list). - - .. NOTE:: - - The projection over the `x` axis is used if there are no vertical asymptotes. - Otherwise, a linear change of variables is done to fall into the previous case. - - EXAMPLES:: - - sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy_arrangement # optional - sirocco - sage: R. = QQ[] - sage: flist = [x^2 - y^3, x + 3 * y - 5] - sage: braid_monodromy_arrangement(flist) # 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], - {1: 1, 2: 2, 3: 1, 4: 1}) - - .. TODO:: - - Create a class ``arrangements_of_curves`` with a ``braid_monodromy`` method - it can be also a method for affine line arrangements. - """ - f = prod(flist) - # if len(flist) == 1: - # return (braid_monodromy(f), {}) - return braid_monodromy(f, arrangement=flist, holdstrand=False) - - def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, braid_mon=None): r""" Compute the fundamental group of the complement of a curve @@ -1636,7 +1599,8 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy_arrangement, fundamental_group_arrangement # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy # optional - sirocco + sage: from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement # optional - sirocco sage: R. = QQ[] sage: flist = [x^2 - y^3, x + 3 * y - 5] sage: g, dic = fundamental_group_arrangement(flist) # optional - sirocco @@ -1644,7 +1608,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis Finitely presented group < x0, x1, x2 | x2*x1*x2^-1*x1^-1, x1*x0*x1^-1*x0^-1, x2*x0*x2*x0^-1*x2^-1*x0^-1 > sage: dic # optional - sirocco {1: (1,), 2: (2,), 3: (-1, -3, -2, -1)} - sage: BM = braid_monodromy_arrangement(flist) # optional - sirocco + sage: BM = braid_monodromy(prod(flist), flist) # optional - sirocco sage: (g, dic) == fundamental_group_arrangement(flist, braid_mon=BM) # optional - sirocco True sage: fundamental_group_arrangement(flist, simplified=False, braid_mon=BM) # optional - sirocco From 68287f4ac18367fe564d0e695323322ad2d87c76 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 19 Jul 2023 10:00:11 +0200 Subject: [PATCH 108/122] pre-final corrections zvk --- src/sage/schemes/curves/zariski_vankampen.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index fc25f805524..d0d036719a9 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1231,6 +1231,16 @@ def conjugate_positive_form(braid): 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 """ B = braid.parent() d = B.strands() @@ -1403,7 +1413,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv if d is None: return None F = FreeGroup(d) - Fv = FreeGroup(d+v) + Fv = FreeGroup(d + v) bmh = [br for j, br in enumerate(bm) if j + 1 not in vertical0] @parallel From 3ff14361ffa6910c8881911d974a7762e16a624e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 19 Jul 2023 13:52:00 +0200 Subject: [PATCH 109/122] till fundamental group from bm --- src/sage/schemes/curves/zariski_vankampen.py | 42 +++++++------------- 1 file changed, 15 insertions(+), 27 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index d0d036719a9..d1890ad95ae 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -64,7 +64,7 @@ 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 +# from sage.sets.set import Set roots_interval_cache = dict() @@ -176,7 +176,7 @@ def discrim(pols): OUTPUT: - A tuple with the values of the discriminant in `\QQbar`. + A tuple with the roots of the discriminant in `\QQbar`. EXAMPLES:: @@ -591,7 +591,7 @@ def 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 - Another example where ``F`` and ``F0`` coincide. + Another example where ``F`` and ``F0`` coincide.:: sage: from sage.schemes.curves.zariski_vankampen import fieldI sage: p = QQ[x](x^4 + 1) @@ -1247,23 +1247,13 @@ def conjugate_positive_form(braid): braid1 = braid.super_summit_set()[0] L1 = braid1.Tietze() sg0 = braid.conjugating_braid(braid1) - hilos = list(Set(L1)) - hilos.sort() - bloques = [] - while len(hilos) > 0: - a = [hilos[0]] - hilos = hilos[1:] - corte = False - while len(hilos) > 0 and not corte: - b = hilos[0] - corte = b - a[-1] > 1 - if not corte: - a.append(b) - hilos = hilos[1:] - bloques.append(a) - trenzabloque = [[a for a in L1 if a in b] for b in bloques] - cortas = [] - for a in trenzabloque: + gns = set(L1) + cuts = [j for j in range(d + 1) if j not in gns] + blocks = [] + for i in range(len(cuts) - 1): + blocks.append([j for j in L1 if j > cuts[i] and j < cuts[i + 1]]) + shorts = [] + for a in blocks: A = B(a).super_summit_set() res = None for tau in A: @@ -1282,8 +1272,8 @@ def conjugate_positive_form(braid): r0 = res[0].Tietze() res[0] = B([i.sign() * (d - abs(i)) for i in r0]) res0 = res[:2] - cortas.append(res0) - return cortas + shorts.append(res0) + return shorts @parallel @@ -1319,8 +1309,7 @@ def braid2rels(L): d = B.strands() F = FreeGroup(d) T = br.Tietze() - T1 = list(Set(T)) - T1.sort() + T1 = set(T) m = len(T1) + 1 k = min(T1) - 1 B0 = BraidGroup(m) @@ -1334,7 +1323,7 @@ def braid2rels(L): 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 ** (-1)).Tietze() for a in U] + U = [(F(a) * C.inverse()).Tietze() for a in U] ga = F / U P = ga.gap().PresentationFpGroup() dic = P.TzOptions().sage() @@ -1403,8 +1392,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv 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 > """ - vertical0 = [_ for _ in vertical] - vertical0.sort() + vertical0 = sorted(vertical) v = len(vertical0) if bm == []: d = degree From 59754f3ada363c77b099f44639ef2365d7c29277 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 19 Jul 2023 17:13:07 +0200 Subject: [PATCH 110/122] src/sage/schemes/curves/affine_curve.py --- src/sage/schemes/curves/zariski_vankampen.py | 45 +++++++------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index d1890ad95ae..a074a126afd 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1217,12 +1217,13 @@ def conjugate_positive_form(braid): INPUT: - - ``braid`` -- a braid. + - ``braid`` -- a braid ``\sigma``. OUTPUT: - A list of lists where each element is a list with two elements, a positive braid `\sigma` and a list of permutation braids - `\tau_1,\dots,\tau_n` such that if `\tau=\prod_{j=1}^n \tau_j` then `\tau\sigma\tau^{-1}` is the input ``braid``. + 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_r}` such that if `\gamma_i=\prod_{j=1}^{n_i} \tau_j^i` then + the braids `\tau_i=\gamma_i\alpha_i\gamma_i^{-1}` commute pairwise and `\alpha=\prod_{i=1}^{r} \tau_i`. EXAMPLES:: @@ -1251,7 +1252,9 @@ def conjugate_positive_form(braid): cuts = [j for j in range(d + 1) if j not in gns] blocks = [] for i in range(len(cuts) - 1): - blocks.append([j for j in L1 if j > cuts[i] and j < cuts[i + 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() @@ -1341,6 +1344,10 @@ def braid2rels(L): 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""" @@ -1403,11 +1410,6 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv F = FreeGroup(d) Fv = FreeGroup(d + v) bmh = [br for j, br in enumerate(bm) if j + 1 not in vertical0] - - @parallel - def relation(x, b): - return x * b / x - 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] @@ -1437,7 +1439,7 @@ def relation(x, b): return G -def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid_mon=None): +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``. @@ -1483,9 +1485,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid Finitely presented group < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-1*x1^-1, x0*x1*x0^-1*x2^-1 > sage: fundamental_group(f, projective=True) # optional - sirocco Finitely presented group < x0 | x0^3 > - sage: bm = braid_monodromy(f); bm # optional - sirocco - ([(s1*s0)^2], {1: 1, 2: 1, 3: 1}) - sage: fundamental_group(f, braid_mon=bm[0]) # optional - sirocco + sage: fundamental_group(f) # optional - sirocco Finitely presented group < x1, x2 | x1*x2*x1^-1*x2^-1*x1^-1*x2 > :: @@ -1523,18 +1523,6 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid sage: g = fundamental_group(f, puiseux=True, projective=True); print (g.order(), g.abelian_invariants()) # optional - sirocco 12 (4,) - We compute first a braid monodromy and use it for the computation of the fundamental group:: - - sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy # optional - sirocco - sage: R. = QQ[] - sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) - sage: bm = braid_monodromy(f); print(bm) # optional - sirocco - ([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], - {1: 1, 2: 1, 3: 1, 4: 1}) - sage: g = fundamental_group(f, projective=True, braid_mon=bm[0]); g # optional - sirocco - 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()) # optional - sirocco - 12 (4,) """ g = f x, y = g.parent().gens() @@ -1546,10 +1534,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False, braid if projective: while g.degree(y) < g.degree(): g = g.subs({x: x + y}) - if braid_mon is None: - bm = braid_monodromy(g)[0] - else: - bm = braid_mon + bm = braid_monodromy(g)[0] if bm == []: d = g.degree(y) else: @@ -1650,7 +1635,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis bm, dic = braid_monodromy(f, flist1) else: bm, dic = braid_mon - g = fundamental_group(f, simplified=False, projective=projective, puiseux=puiseux, braid_mon=bm) + g = fundamental_group_from_braid_mon(bm, degree=d, simplified=False, projective=projective, puiseux=puiseux) if simplified: hom = g.simplification_isomorphism() else: From 3cf4c4cf61f2a19d3a7cd0a736060e6c6d24433b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 19 Jul 2023 17:14:09 +0200 Subject: [PATCH 111/122] fundamental group changes --- src/sage/schemes/curves/affine_curve.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index d1cef0fbfbf..077ec274e97 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1736,16 +1736,18 @@ class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): """ _point = AffinePlaneCurvePoint_field - def fundamental_group(self, puiseux=False, braid_mon=None): + def fundamental_group(self, simplified=True, puiseux=False, braid_mon=None): 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. + shortcut. If ``True``, ``simplified`` is set to ``False``. - ``braid_mon`` -- (default: ``False``) If the value is ``False`` it apply first the ``braid_monodromy`` method. If it has been already @@ -1801,14 +1803,16 @@ def fundamental_group(self, puiseux=False, braid_mon=None): This functionality requires the sirocco package to be installed. """ - from sage.schemes.curves.zariski_vankampen import fundamental_group + from sage.schemes.curves.zariski_vankampen import fundamental_group, fundamental_group_from_braid_mon + if braid_mon is not None: + return fundamental_group_from_braid_mon(braid_mon, simplified=simplified, puiseux=puiseux) 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, puiseux=puiseux, braid_mon=braid_mon) + return fundamental_group(f, simplified=simplified, puiseux=puiseux) def braid_monodromy(self): r""" From 29b8787105f1831085522ddc01bc58e801448d44 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 19 Jul 2023 17:25:29 +0200 Subject: [PATCH 112/122] typos in doc of conjugate_positive_form --- src/sage/schemes/curves/zariski_vankampen.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index a074a126afd..cda2166c78c 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1222,8 +1222,8 @@ def conjugate_positive_form(braid): 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_r}` such that if `\gamma_i=\prod_{j=1}^{n_i} \tau_j^i` then - the braids `\tau_i=\gamma_i\alpha_i\gamma_i^{-1}` commute pairwise and `\alpha=\prod_{i=1}^{r} \tau_i`. + 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:: @@ -1344,6 +1344,7 @@ def braid2rels(L): def braid2rels_p(L): return braid2rels(L) + @parallel def relation(x, b): return x * b / x From 53cee22cfe471556c971654e00e9fb3e20f93d16 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 20 Jul 2023 13:58:25 +0200 Subject: [PATCH 113/122] prefinal --- src/sage/schemes/curves/affine_curve.py | 26 ++++++-------------- src/sage/schemes/curves/zariski_vankampen.py | 3 --- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 077ec274e97..103663f081f 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1736,7 +1736,8 @@ class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): """ _point = AffinePlaneCurvePoint_field - def fundamental_group(self, simplified=True, puiseux=False, braid_mon=None): + @cached_method + def fundamental_group(self, simplified=True, puiseux=False): r""" Return a presentation of the fundamental group of the complement of ``self``. @@ -1749,9 +1750,6 @@ def fundamental_group(self, simplified=True, puiseux=False, braid_mon=None): presentation is constructed in the classical way or using Puiseux shortcut. If ``True``, ``simplified`` is set to ``False``. - - ``braid_mon`` -- (default: ``False``) If the value is ``False`` - it apply first the ``braid_monodromy`` method. If it has been already - computed it can be passed as a parameter. OUTPUT: @@ -1777,15 +1775,11 @@ def fundamental_group(self, simplified=True, puiseux=False, braid_mon=None): sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | > sage: bm = C.braid_monodromy() # optional - sirocco - sage: C.fundamental_group(braid_mon=bm) # optional - sirocco - Finitely presented group < x0 | > sage: g = C.fundamental_group(puiseux=True) # optional - sirocco sage: g # optional - sirocco Finitely presented group < x0, x1 | x1*x0^-1, x1*x0*x1^-1*x0^-1 > sage: g.simplified() # optional - sirocco Finitely presented group < x0 | > - sage: g == C.fundamental_group(puiseux=True, braid_mon=bm) # optional - sirocco - True In the case of number fields, they need to have an embedding to the algebraic field:: @@ -1803,17 +1797,10 @@ def fundamental_group(self, simplified=True, puiseux=False, braid_mon=None): This functionality requires the sirocco package to be installed. """ - from sage.schemes.curves.zariski_vankampen import fundamental_group, fundamental_group_from_braid_mon - if braid_mon is not None: - return fundamental_group_from_braid_mon(braid_mon, simplified=simplified, puiseux=puiseux) - 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, simplified=simplified, puiseux=puiseux) + 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. @@ -1847,7 +1834,8 @@ 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)[0] + bm = braid_monodromy(f)[0] + return bm def riemann_surface(self, **kwargs): r""" diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index cda2166c78c..bdf2c0d7e55 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1464,9 +1464,6 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): of the complement of the affine curve will be computed, adding one relation if ``projective`` is set to ``True``. - - ``braid_mon`` -- (default: ``None``); if it is set to a list of braids - braid monodromy is not computed ad this list is used instead. - If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. From 9e3c1cafe95778be36fb430599e0b133f0fafeb8 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 20 Jul 2023 17:47:59 +0200 Subject: [PATCH 114/122] arreglar diccionarios --- .../hyperplane_arrangement/arrangement.py | 62 ++++++++++--------- src/sage/schemes/curves/zariski_vankampen.py | 55 +++++++--------- 2 files changed, 57 insertions(+), 60 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index cb9b88ad64a..093e456f055 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3562,7 +3562,7 @@ def hyperplane_section(self, proj=True): sage: p = Permutation([p(j) for j in [1 .. 12]] + [13]); p # optional - sage.combinat [1, 5, 11, 12, 9, 10, 7, 8, 3, 4, 2, 6, 13] sage: ca1, p2 = a1.cone(permutation=True) # optional - sage.combinat - sage: m1 = ca1.matroid() + sage: m1 = ca1.matroid() # optional - sage.combinat sage: q = p1.inverse() * p * p2 # optional - sage.combinat sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) # optional - sage.combinat True @@ -3647,7 +3647,7 @@ def _fundamental_group_(self, proj=False): Hyperplane x + 0*y + 0, Hyperplane x + 0*y + 1] sage: G, dic = H._fundamental_group_() # optional - sirocco - sage: G # optional - sirocco + sage: G.simplified() # optional - sirocco Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, x2^-1*x0^-1*x2*x4*x0*x4^-1, x0*x1*x3*x0^-1*x3^-1*x1^-1, @@ -3655,23 +3655,25 @@ def _fundamental_group_(self, proj=False): x0*x1^-1*x0^-1*x3^-1*x1*x3, x4^-1*x3^-1*x1*x3*x4*x3^-1*x1^-1*x3 > sage: dic # optional - sirocco - {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} + {0: [x4], 1: [x3], 2: [x0], 3: [x2], 4: [x1], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} sage: H=A(x,y,x+y) sage: H._fundamental_group_() # optional - sirocco - (Finitely presented group < x0, x1, x2 | x0*x1^-1*x0^-1*x2^-1*x1*x2, x0*x1*x2*x1^-1*x0^-1*x2^-1 >, - {1: (3,), 2: (1,), 3: (2,), 4: (-3, -2, -1)}) + (Finitely presented group < x0, x1, x2 | x1*x2*x0*x2^-1*x1^-1*x0^-1, x1*x0^-1*x2^-1*x1^-1*x2*x0 >, + {0: [x2], 1: [x0], 2: [x1], 3: [x2^-1*x1^-1*x0^-1]}) sage: H._fundamental_group_(proj=True) # optional - sirocco (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) sage: H = hyperplane_arrangements.braid(4).essentialization() # optional - sage.graphs - sage: H._fundamental_group_(proj=True) # optional - sage.graphs, sirocco - (Finitely presented group < x0, x1, x2, x3, x4 | - x0*x2*x0^-1*x2^-1, - x1*x3*x1^-1*x3^-1, - x1*x4*x1^-1*x0^-1*x4^-1*x0, - x4*x2*x3*x2^-1*x4^-1*x3^-1, - x4^-1*x1^-1*x0*x1*x4*x0^-1, - x3*x4^-1*x3^-1*x2^-1*x4*x2 >, - {1: (5,), 2: (1,), 3: (2,), 4: (3,), 5: (4,), 6: (-1, -5, -4, -2, -3)}) + sage: G, dic = H._fundamental_group_(proj=True) # optional - sage.graphs, sirocco + sage: h = G.simplification_isomorphism() # optional - sage.graphs, sirocco + sage: G.simplified() # optional - sage.graphs, sirocco + Finitely presented group < x0, x1, x3, x4, x5 | x0*x3*x0^-1*x3^-1, + x1*x4*x1^-1*x4^-1, + x1*x5*x1^-1*x0^-1*x5^-1*x0, + x5*x3*x4*x3^-1*x5^-1*x4^-1, + x5^-1*x1^-1*x0*x1*x5*x0^-1, + x4*x5^-1*x4^-1*x3^-1*x5*x3 > + sage: {j: h(dic[j][0]) for j in dic.keys()} # optional - sage.graphs, sirocco + {0: x5, 1: x0, 2: x1, 3: x3, 4: x4, 5: x0^-1*x5^-1*x4^-1*x1^-1*x3^-1} .. WARNING:: @@ -3709,10 +3711,10 @@ def _fundamental_group_(self, proj=False): p = S.sum(V[i]*c for i, c in enumerate(coeff)) if p.degree() > 0: L.append(p) - G, dic = fundamental_group_arrangement(L, puiseux=True, projective=projective and not infinity) + G, dic = fundamental_group_arrangement(L, puiseux=True, projective=projective and not infinity, simplified=False) if infinity: p = Permutation([r] + [j for j in range(1, r)]) - dic = {j: dic[p(j)] for j in range(1, r + 1)} + dic = {j: dic[p(j + 1) - 1] for j in range(r)} return (G, dic) def fundamental_group(self, projective=False): @@ -3747,7 +3749,7 @@ def fundamental_group(self, projective=False): Hyperplane x + 0*y + 0, Hyperplane x + 0*y + 1] sage: G, dic = H.fundamental_group() # optional - sirocco - sage: G # optional - sirocco + sage: G.simplified() # optional - sirocco Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, x2^-1*x0^-1*x2*x4*x0*x4^-1, x0*x1*x3*x0^-1*x3^-1*x1^-1, @@ -3755,21 +3757,25 @@ def fundamental_group(self, projective=False): x0*x1^-1*x0^-1*x3^-1*x1*x3, x4^-1*x3^-1*x1*x3*x4*x3^-1*x1^-1*x3 > sage: dic # optional - sirocco - {1: (5,), 2: (4,), 3: (1,), 4: (3,), 5: (2,), 6: (-5, -4, -3, -2, -1)} + {0: [x4], 1: [x3], 2: [x0], 3: [x2], 4: [x1], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} sage: H=A(x,y,x+y) sage: H.fundamental_group() # optional - sirocco - (Finitely presented group < x0, x1, x2 | x0*x1^-1*x0^-1*x2^-1*x1*x2, x0*x1*x2*x1^-1*x0^-1*x2^-1 >, - {1: (3,), 2: (1,), 3: (2,), 4: (-3, -2, -1)}) + (Finitely presented group < x0, x1, x2 | x1*x2*x0*x2^-1*x1^-1*x0^-1, + x1*x0^-1*x2^-1*x1^-1*x2*x0 >, + {0: [x2], 1: [x0], 2: [x1], 3: [x2^-1*x1^-1*x0^-1]}) sage: H.fundamental_group(projective=True) # optional - sirocco (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) sage: H = hyperplane_arrangements.braid(4) # optional - sage.groups - sage: H.fundamental_group(projective=True) # optional - sirocco, sage.groups - (Finitely presented group < x0, x1, x2, x3, x4 | x0*x2*x0^-1*x2^-1, x1*x3*x1^-1*x3^-1, - x1*x4*x1^-1*x0^-1*x4^-1*x0, - x4*x2*x3*x2^-1*x4^-1*x3^-1, - x4^-1*x1^-1*x0*x1*x4*x0^-1, - x3*x4^-1*x3^-1*x2^-1*x4*x2 >, - {1: (5,), 2: (1,), 3: (2,), 4: (-1, -5, -4, -2, -3), 5: (4,), 6: (3,)}) + sage: G, dic = H.fundamental_group(projective=True) # optional - sirocco, sage.groups + sage: h = G.simplification_isomorphism() # optional - sirocco, sage.groups + sage: G.simplified() # optional - sirocco, sage.groups + Finitely presented group < x0, x1, x3, x4, x5 | x0*x3*x0^-1*x3^-1, x1*x4*x1^-1*x4^-1, + x1*x5*x1^-1*x0^-1*x5^-1*x0, + x5*x3*x4*x3^-1*x5^-1*x4^-1, + x5^-1*x1^-1*x0*x1*x5*x0^-1, + x4*x5^-1*x4^-1*x3^-1*x5*x3 > + sage: {j: h(dic[j][0]) for j in dic.keys()} # optional - sirocco, sage.groups + {0: x5, 1: x0, 2: x1, 3: x0^-1*x5^-1*x4^-1*x1^-1*x3^-1, 4: x4, 5: x3} sage: H = hyperplane_arrangements.coordinate(5) sage: g = H.fundamental_group(projective=True)[0] # optional - sirocco sage: g.is_abelian(), g.abelian_invariants() # optional - sirocco @@ -3786,7 +3792,7 @@ def fundamental_group(self, projective=False): H2, dic = H1.fundamental_group(projective=projective) if not projective: P = Permutation(list(P) + [self.n_hyperplanes() + 1]) - dic = {j: dic[P(j)] for j in dic.keys()} + dic = {j: dic[P(j + 1) - 1] for j in dic.keys()} return (H2, dic) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index bdf2c0d7e55..6fb789f6903 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -26,7 +26,7 @@ sage: R. = QQ[] sage: f = y^3 + x^3 - 1 sage: braid_monodromy(f) # optional - sirocco - ([s1*s0, s1*s0, s1*s0], {1: 1, 2: 1, 3: 1}) + ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0}) sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > """ @@ -218,7 +218,6 @@ def discrim_pairs(f, g): 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: @@ -227,7 +226,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. @@ -1077,7 +1076,7 @@ def strand_components(f, flist, p1): sage: strand_components(prod(flist), flist, 1) # optional - sirocco ([(-0.500000000000000? - 0.866025403784439?*I, 0), (-0.500000000000000? + 0.866025403784439?*I, 0), - (1, 0), (1.333333333333334?, 1)], {1: 1, 2: 1, 3: 1, 4: 2}) + (1, 0), (1.333333333333334?, 1)], {0: 0, 1: 0, 2: 0, 3: 1}) """ x, y = f.parent().gens() F = flist[0].base_ring() @@ -1089,7 +1088,7 @@ def strand_components(f, flist, p1): rt = h1.roots(QQbar, multiplicities=False) roots_base += [(_, i) for _ in rt] roots_base.sort() - strands = {i + 1: par[1] + 1 for i, par in enumerate(roots_base)} # quitar +1 despues de revision + strands = {i: par[1] for i, par in enumerate(roots_base)} # quitar +1 despues de revision return (roots_base, strands) @@ -1132,13 +1131,13 @@ def braid_monodromy(f, arrangement=()): ([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], {1: 1, 2: 1, 3: 1, 4: 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) # optional - sirocco sage: bm1[0] == bm[0] # optional - sirocco True sage: bm1[1] # optional - sirocco - {1: 1, 2: 2, 3: 1, 4: 1} + {0: 0, 1: 1, 2: 0, 3: 0} """ global roots_interval_cache @@ -1286,8 +1285,8 @@ def conjugate_positive_form_p(braid): def braid2rels(L): r""" - Return a minimal set of elements of ``F = FreeGroup(d)`` for a braid ``b`` which is the conjugate of - a positive braid as relations of the group ``F / [(b * F([j])) / F([j]) for j in (1..d)]``. One starts + 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: @@ -1540,7 +1539,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): return fundamental_group_from_braid_mon(bm, degree=d, simplified=simplified, projective=projective, puiseux=puiseux) -def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, braid_mon=None): +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 @@ -1565,9 +1564,6 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis of the complement of the affine curve will be computed, adding one relation if ``projective`` is set to ``True``. - - ``braid_mon`` -- (default: ``None``); it can be set to the output - of ``braid_monodromy_arrangement`` to avoid an extra computation. - OUTPUT: - A list of braids. The braids correspond to paths based in the same point; @@ -1588,17 +1584,15 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis sage: g # optional - sirocco Finitely presented group < x0, x1, x2 | x2*x1*x2^-1*x1^-1, x1*x0*x1^-1*x0^-1, x2*x0*x2*x0^-1*x2^-1*x0^-1 > sage: dic # optional - sirocco - {1: (1,), 2: (2,), 3: (-1, -3, -2, -1)} - sage: BM = braid_monodromy(prod(flist), flist) # optional - sirocco - sage: (g, dic) == fundamental_group_arrangement(flist, braid_mon=BM) # optional - sirocco - True - sage: fundamental_group_arrangement(flist, simplified=False, braid_mon=BM) # optional - sirocco + {0: [x0, x2, x0], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]} + sage: fundamental_group_arrangement(flist, simplified=False) # optional - sirocco (Finitely presented group < x0, x1, x2, x3 | x0*x1*x0*x1^-1*x0^-2, x0*x1*x2*x3*x2*x3^-1*x2^-1*x1^-1*x0^-2, x0*x1*x0*x1^-1*x0^-2, 1, x0*x1*x0^-1*x1^-1, 1, x0*x1*x0^-1*x1^-1, x1*x2*x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-2, 1, x1^-1*x0*x1*x2*x3*x2^-1*x1^-1*x0^-1*x1*x2^-1, - 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, {1: (1,), 2: (2,), 3: (-4, -3, -2, -1)}) - sage: fundamental_group_arrangement(flist, projective=True, braid_mon=BM) # optional - sirocco - (Finitely presented group < x | >, {1: (1,), 2: (-1, -1, -1)}) + 1, 1, 1, x1^-1*x0*x1*x3^-1, 1, x2^-1*x1*x2*x3*x2^-1*x1^-1*x2*x3^-1 >, + {0: [x0, x2, x3], 1: [x1], 2: [x3^-1*x2^-1*x1^-1*x0^-1]}) + sage: fundamental_group_arrangement(flist, projective=True) # optional - sirocco + (Finitely presented group < x | >, {0: [x0, x0, x0], 1: [x0^-3]}) .. TODO:: @@ -1625,14 +1619,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis while f.degree(y) < f.degree(): flist1 = [g.subs({x: x + y}) for g in flist] f = prod(flist1) - if braid_mon is None: - if len(flist1) == 0: - bm = [] - dic = dict() - else: - bm, dic = braid_monodromy(f, flist1) + if len(flist1) == 0: + bm = [] + dic = dict() else: - bm, dic = braid_mon + bm, dic = braid_monodromy(f, flist1) g = fundamental_group_from_braid_mon(bm, degree=d, simplified=False, projective=projective, puiseux=puiseux) if simplified: hom = g.simplification_isomorphism() @@ -1643,11 +1634,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis return (g1, dict()) dic1 = {} for i in range(len(flist1)): - j = [j1 for j1 in dic.keys() if dic[j1] == i + 1][0] - dic1[i + 1] = hom(g([j])).Tietze() + 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().Tietze() - dic1[len(flist1) + 1] = t + 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 From 8dc33f75d190f2b9cd3b07218bc0142358704213 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 20 Jul 2023 17:56:33 +0200 Subject: [PATCH 115/122] back to original hyperplane, changes to a new branch --- .../hyperplane_arrangement/arrangement.py | 516 ++---------------- 1 file changed, 53 insertions(+), 463 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 093e456f055..7cc2b479573 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -329,7 +329,7 @@ arrangements. """ -# ***************************************************************************** +#***************************************************************************** # Copyright (C) 2013 David Perkinson # Volker Braun # @@ -338,25 +338,25 @@ # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -# ***************************************************************************** +#***************************************************************************** # Possible extensions for hyperplane_arrangement.py: # - the big face lattice # - create ties with the Sage matroid methods # - hyperplane arrangements over other fields -from sage.combinat.permutation import Permutation -from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane -from sage.matrix.constructor import matrix, vector -from sage.misc.cachefunc import cached_method -from sage.modules.free_module import VectorSpace -from sage.rings.integer_ring import ZZ -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.rational_field import QQ from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import richcmp from sage.structure.unique_representation import UniqueRepresentation +from sage.rings.integer_ring import ZZ +from sage.rings.rational_field import QQ +from sage.misc.cachefunc import cached_method +from sage.matrix.constructor import matrix, vector +from sage.modules.free_module import VectorSpace +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane class HyperplaneArrangementElement(Element): @@ -369,7 +369,6 @@ class HyperplaneArrangementElement(Element): :class:`HyperplaneArrangementElement` instances directly, always use the parent. """ - def __init__(self, parent, hyperplanes, check=True, backend=None): """ Construct a hyperplane arrangement. @@ -634,7 +633,7 @@ def _richcmp_(self, other, op): """ return richcmp(self._hyperplanes, other._hyperplanes, op) - def union(self, other, permutation=False): + def union(self, other): r""" The union of ``self`` with ``other``. @@ -643,18 +642,9 @@ def union(self, other, permutation=False): - ``other`` -- a hyperplane arrangement or something that can be converted into a hyperplane arrangement - - - ``permutation`` -- (optional, default ``False``) If ``True`` - it computes the permutation relating the order of hyperplanes - in the input and in the output. - OUTPUT: - A new hyperplane arrangement `L=(H'_1,\dots,H'_n)` if not ``permutation``. - If it is ``True``, a 2-tuple where the first term is `L` and the second is - a permutation `\sigma`. If ``self`` is composed by `H_1,\dots,H_r` and - ``other`` by `H_{r+1},\dots,H_{n}` (its order if it is a hyperplane - arrangement, or the list order otherwise) then `H_i=H'_{i^\sigma}`. + A new hyperplane arrangement. EXAMPLES:: @@ -665,17 +655,6 @@ def union(self, other, permutation=False): Arrangement of 8 hyperplanes of dimension 2 and rank 2 sage: A | B # syntactic sugar Arrangement of 8 hyperplanes of dimension 2 and rank 2 - sage: A1 = H([1,1,0], [4,5,3]); A1 - Arrangement - sage: B =[(1, 1, 1), [2, 0, -1]] - sage: C, p = A1.union(B, permutation=True); C - Arrangement <-y + 2 | x + 1 | x + y + 1 | 5*x + 3*y + 4> - sage: p - [2, 4, 3, 1] - sage: C1,p1=A1.union(H(B), permutation=True); C == C1 - True - sage: p1 - [2, 4, 1, 3] A single hyperplane is coerced into a hyperplane arrangement if necessary:: @@ -691,27 +670,9 @@ def union(self, other, permutation=False): Arrangement of 6 hyperplanes of dimension 2 and rank 2 """ P = self.parent() - other_h = P(other) - if permutation: - r = self.n_hyperplanes() - L = list(range(1, r + 1)) - L1 = [_ for _ in other_h] - for h in other: - h0 = P(h)[0] - j = L1.index(h0) - L.append(r + j + 1) - p0 = Permutation(L) - hyperplanes = self._hyperplanes + other_h._hyperplanes - result = P(*hyperplanes, backend=self._backend) - if permutation: - L1 = [_ for _ in result] - L = [] - for h in hyperplanes: - j = L1.index(h) - L.append(j + 1) - p1 = Permutation(L) - return (result, p0 * p1) - return result + other = P(other) + hyperplanes = self._hyperplanes + other._hyperplanes + return P(*hyperplanes, backend=self._backend) add_hyperplane = union @@ -734,29 +695,19 @@ def plot(self, **kwds): from sage.geometry.hyperplane_arrangement.plot import plot return plot(self, **kwds) - def cone(self, variable='t', permutation=False): + def cone(self, variable='t'): r""" - Return the cone over the hyperplane arrangement `H_1,\dots,H_n`. + Return the cone over the hyperplane arrangement. INPUT: - ``variable`` -- string; the name of the additional variable - - ``permutation`` -- (optional, default ``False``) If ``True`` - it computes the permutation relating the order of hyperplanes - in the input and in the output. - OUTPUT: - If permutation is ``True`` A new hyperplane arrangement `L`. - Its equations consist of `[0, -d, a_1, \ldots, a_n]` for each - `[d, a_1, \ldots, a_n]` in the original arrangement and the - equation `[0, 1, 0, \ldots, 0]` (maybe not in this order). - If ``permutation`` is set to ``True``, a tuple whose first term - is `L` and whose second term is a permutation `\sigma`. If the - cone is `L=(H'_1,\dots,H'_{n+1})` then for `1 \leq i \leq n` the - hyperplane `H_i` is associated to `H'_{i^\sigma}` and - `H'_{(n + 1)^\sigma}` is the hyperplane at infinity . + A new hyperplane arrangement. Its equations consist of + `[0, -d, a_1, \ldots, a_n]` for each `[d, a_1, \ldots, a_n]` in the + original arrangement and the equation `[0, 1, 0, \ldots, 0]`. .. WARNING:: @@ -790,13 +741,6 @@ def cone(self, variable='t', permutation=False): Hyperplane t + 0*x + y - z + 0, Hyperplane t + x - y + 0*z + 0, Hyperplane t + x + 0*y - z + 0) - sage: b1, P = a.cone(permutation=True) # optional - sage.combinat - sage: b1 == b # optional - sage.combinat - True - sage: P # optional - sage.combinat - [1, 5, 2, 6, 3, 7, 4] - sage: b1[P(b1.n_hyperplanes())-1] # optional - sage.combinat - Hyperplane t + 0*x + 0*y + 0*z + 0 """ hyperplanes = [] for h in self.hyperplanes(): @@ -806,17 +750,7 @@ def cone(self, variable='t', permutation=False): P = self.parent() names = (variable,) + P._names H = HyperplaneArrangements(self.parent().base_ring(), names=names) - result = H(*hyperplanes, backend=self._backend) - if permutation: - L1 = [_ for _ in result] - L = [] - for h in hyperplanes: - h0 = H(h)[0] - j = L1.index(h0) - L.append(j + 1) - P = Permutation(L) - return (result, P) - return result + return H(*hyperplanes, backend=self._backend) @cached_method def intersection_poset(self, element_label="int"): @@ -909,16 +843,16 @@ def intersection_poset(self, element_label="int"): W = Vector space of dimension 2 over Rational Field] """ if element_label == "int": - def update(mapping, val, I0): + def update(mapping, val, I): mapping[val] = len(mapping) elif element_label == "subset": from sage.sets.set import Set - def update(mapping, val, I0): + def update(mapping, val, I): mapping[val] = Set(val) elif element_label == "subspace": - def update(mapping, val, I0): - mapping[val] = I0 + def update(mapping, val, I): + mapping[val] = I else: raise ValueError("invalid element label type") @@ -941,13 +875,13 @@ def update(mapping, val, I0): for label, T in cur_level: edges = [] for i, H in enumerate(hyperplanes): - I0 = H.intersection(T) - if I0 is not None and I0 != T: + I = H.intersection(T) + if I is not None and I != T: try: - target = new_level[I0] + target = new_level[I] except KeyError: target = set(label) - new_level[I0] = target + new_level[I] = target target.add(i) edges.append(target) hasse[label] = edges @@ -1101,7 +1035,7 @@ def deletion(self, hyperplanes): raise ValueError('hyperplane is not in the arrangement') return parent(*planes, backend=self._backend) - def restriction(self, hyperplane, permutation=False): + def restriction(self, hyperplane): r""" Return the restriction to a hyperplane. @@ -1109,20 +1043,10 @@ def restriction(self, hyperplane, permutation=False): - ``hyperplane`` -- a hyperplane of the hyperplane arrangement - - ``permutation`` -- (optional, default ``False``) If ``True`` - it computes the permutation relating the order of hyperplanes - in the input and in the output. - OUTPUT: - If ``permutation`` is ``False``, the restriction `\mathcal{A}_H` of the - hyperplane arrangement `\mathcal{A}` to the given ``hyperplane`` `H`. - If ``permutation`` is set to ``True``, also a permutation - `\sigma` defined as follows. If `\mathcal{A}=\{H_1 ,\dots, H_{i - 1}, H, - H_{i}, \dots, H_{n}\}` and `\mathcal{A}_H=\{H'_1 ,\dots, H'_{n}\}` - then the restriction of `H_i` is `H'_{i^\sigma}`. If several hyperplanes - of ``self`` produce the same hyperplane in the restriction, - the second output is ``None``. + The restriction of the hyperplane arrangement to the given + ``hyperplane``. EXAMPLES:: @@ -1132,12 +1056,6 @@ def restriction(self, hyperplane, permutation=False): Hyperplane 0*u + 0*x + y - z + 0 sage: R = A.restriction(H); R # optional - sage.graphs Arrangement - sage: A.restriction(H, permutation=True) # optional - sage.graphs - (Arrangement , None) - sage: A.add_hyperplane(z).restriction(z, permutation=True) # optional - sage.graphs - (Arrangement of 6 hyperplanes of dimension 3 and rank 3, [1, 2, 3, 4, 5, 6]) - sage: A.add_hyperplane(u).restriction(u, permutation=True) # optional - sage.graphs - (Arrangement of 6 hyperplanes of dimension 3 and rank 3, [2, 4, 5, 6, 3, 1]) sage: D = A.deletion(H); D # optional - sage.graphs Arrangement of 5 hyperplanes of dimension 4 and rank 3 sage: ca = A.characteristic_polynomial() # optional - sage.graphs @@ -1182,21 +1100,7 @@ def restriction(self, hyperplane, permutation=False): names = list(parent._names) names.pop(pivot) H = HyperplaneArrangements(parent.base_ring(), names=tuple(names)) - result = H(*hyperplanes, signed=False, backend=self._backend) - if permutation: - L1 = [_ for _ in result] - L = [] - for h in hyperplanes: - h0 = H(h, signed=False)[0] - j = L1.index(h0) - L.append(j + 1) - if len(L) > len(set(L)): - return (result, None) - else: - P = Permutation(L) - return (result, P) - else: - return result + return H(*hyperplanes, signed=False, backend=self._backend) def change_ring(self, base_ring): """ @@ -1567,7 +1471,7 @@ def is_simplicial(self): return all(R.n_facets() == rank for R in self.regions()) @cached_method - def essentialization(self, permutation=False): + def essentialization(self): r""" Return the essentialization of the hyperplane arrangement. @@ -1575,20 +1479,9 @@ def essentialization(self, permutation=False): has characteristic 0 is obtained by intersecting the hyperplanes by the space spanned by their normal vectors. - INPUT: - - - ``permutation`` -- (optional, default ``False``) If ``True`` - it computes the permutation relating the order of hyperplanes - in the input and in the output. - OUTPUT: - The essentialization `\mathcal{A}'` of `\mathcal{A}` as a - new hyperplane arrangement if not ``permutation``. If it is - ``True``, a 2-tuple where the first term is `\mathcal{A}'` and - the second is a permutation `\sigma` as follows. If - `\mathcal{A} = \{H_1, \dots, H_n\}` and - `\mathcal{A}' = \{H'_1, \dots, H'_r\}` then `H_i=H'_{i^\sigma}`. + The essentialization as a new hyperplane arrangement. EXAMPLES:: @@ -1671,18 +1564,7 @@ def echelon_col_iter(row_iter): names = tuple(name for i, name in enumerate(parent._names) if i not in echelon_pivots) # Construct the result restricted_parent = HyperplaneArrangements(R, names=names) - result = restricted_parent(*restricted, signed=False, backend=self._backend) - if permutation: - L1 = [_ for _ in result] - L = [] - for h in restricted: - h0 = restricted_parent(h, signed=False)[0] - j = L1.index(h0) - L.append(j + 1) - P = Permutation(L) - return (result, P) - else: - return result + return restricted_parent(*restricted, signed=False, backend=self._backend) def sign_vector(self, p): r""" @@ -2023,7 +1905,7 @@ def regions(self): for hyperplane in self: ieq = vector(R, hyperplane.dense_coefficient_list()) - pos_half = Polyhedron(ieqs=[ieq], base_ring=R, backend=be) + pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be) neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be) if not regions: # See comment above. @@ -2061,8 +1943,8 @@ def regions(self): else: # In this case, at least one of the vertices is not on the hyperplane. # So we check if any ray or line pokes the hyperplane. - if (any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or - any(ieq[1:]*ll[:] != 0 for ll in region_lines)): + if ( any(ieq[1:]*r[:]*direction < 0 for r in region.rays()) or + any(ieq[1:]*l[:] != 0 for l in region_lines)): splits = True if splits: @@ -2352,7 +2234,7 @@ def closed_faces(self, labelled=True): zero_half = Polyhedron(eqns=[ieq], base_ring=R, backend=be) # ``zero_half`` is the hyperplane ``hyperplane`` itself # (viewed as a polyhedron). - pos_half = Polyhedron(ieqs=[ieq], base_ring=R, backend=be) + pos_half = Polyhedron(ieqs=[ ieq], base_ring=R, backend=be) neg_half = Polyhedron(ieqs=[-ieq], base_ring=R, backend=be) subdivided = [] for signs, face in faces: @@ -2380,8 +2262,8 @@ def closed_faces(self, labelled=True): # inequalities are always equalities on it). Check for # this: zero_part_point = zero_part.representative_point() - for ll, testhype in enumerate(hypes[:k]): - if signs[ll] != 0: + for l, testhype in enumerate(hypes[:k]): + if signs[l] != 0: h = testhype.dense_coefficient_list() testval = R.sum(h[i+1] * gi for i, gi in enumerate(zero_part_point)) + h[0] if testval == 0: @@ -2516,9 +2398,9 @@ def face_product(self, F, G, normalize=True): if not normalize: return face # Look for ``I`` in ``self.closed_faces()``: - for I0 in self.closed_faces(): - if I0[0] == tuple(signs): - return I0[1] + for I in self.closed_faces(): + if I[0] == tuple(signs): + return I[1] def face_semigroup_algebra(self, field=None, names='e'): r""" @@ -2628,8 +2510,8 @@ def face_semigroup_algebra(self, field=None, names='e'): matrix_j = [] for i, si in enumerate(Fs): row_i = [zero] * N - sk = [sil if sil != 0 else sj[ll] - for ll, sil in enumerate(si)] + sk = [sil if sil != 0 else sj[l] + for l, sil in enumerate(si)] k = Fdict[tuple(sk)] row_i[k] = one matrix_j += row_i @@ -3156,7 +3038,7 @@ def orlik_solomon_algebra(self, base_ring=None, ordering=None, **kwds): """ if base_ring is None: base_ring = self.base_ring() - return self.matroid().orlik_solomon_algebra(base_ring, ordering, **kwds) + return self.matroid().orlik_solomon_algebra(base_ring, ordering,**kwds) def orlik_terao_algebra(self, base_ring=None, ordering=None, **kwds): """ @@ -3468,13 +3350,13 @@ def derivation_module_basis(self, algorithm="singular"): ....: else: ....: assert exponents(B) == exponents(Bp) """ - alg = algorithm # prevent possible changes to a global variable + alg = algorithm # prevent possible changes to a global variable if alg == "singular": # import sage.libs.singular.function_factory # syz = sage.libs.singular.function_factory.ff.syz f = self.defining_polynomial() - I0 = f + f.jacobian_ideal() - IS = I0._singular_() + I = f + f.jacobian_ideal() + IS = I._singular_() ISS = IS.syz() MSTD = ISS.mstd() basis = MSTD[2]._sage_().transpose().submatrix(0, 1) @@ -3483,7 +3365,7 @@ def derivation_module_basis(self, algorithm="singular"): # Check using Saito's criterion if det / f in f.parent().base_ring() and not det.is_zero(): return basis.rows() - except ValueError: # Non-square matrix or det = 0 + except ValueError: # Non-square matrix or det = 0 pass # Check if it is free if not self.is_free(algorithm=alg): @@ -3494,7 +3376,7 @@ def derivation_module_basis(self, algorithm="singular"): if alg == "BC": C = self.derivation_module_free_chain() if C is not None: - if not C: # C is an empty list + if not C: # C is an empty list S = self.parent().ambient_space().symmetric_space() return matrix.identity(S, self.dimension()).rows() from sage.misc.misc_c import prod @@ -3503,298 +3385,6 @@ def derivation_module_basis(self, algorithm="singular"): else: raise ValueError("invalid algorithm") - def hyperplane_section(self, proj=True): - r""" - It computes a generic hyperplane section of ``self``, an arrangement - - INPUT: - - - ``proj`` -- (optional, default ``True``). It decides if it the - ambient space is affine or projective - - OUTPUT: - - An arrangement `\mathcal{A}` obtained by intersecting with a - generic hyperplane and a permutation `\sigma`. If ``self`` is - `\{H_1 ,\dots, H_{n}\}` and `\mathcal{A}=\{H'_1 ,\dots, H'_{n}\}` - then the restriction of `H_i` is `H'_{i^\sigma}`. - - EXAMPLES:: - - sage: A. = hyperplane_arrangements.braid(4); A # optional - sage.graphs - Arrangement of 6 hyperplanes of dimension 4 and rank 3 - sage: M = A.matroid() # optional - sage.graphs - sage: A1, P = A.hyperplane_section() # optional - sage.graphs - sage: A1 # optional - sage.graphs - Arrangement of 6 hyperplanes of dimension 3 and rank 3 - sage: P # optional - sage.graphs - [1, 2, 3, 6, 5, 4] - sage: M1 = A1.matroid() # optional - sage.graphs - sage: M.is_isomorphism(M1, {j: P(j + 1) - 1 for j in M.groundset()}) # optional - sage.graphs - True - sage: A2, Q = A1.hyperplane_section(); A2 # optional - sage.graphs - Arrangement of 6 hyperplanes of dimension 2 and rank 2 - sage: M2 = A2.matroid() # optional - sage.graphs - sage: T1 = M1.truncation() # optional - sage.graphs - sage: T1.is_isomorphism(M2, {j: Q(j + 1) - 1 for j in T1.groundset()}) # optional - sage.graphs - True - sage: a = hyperplane_arrangements.semiorder(3); a # optional - sage.combinat - Arrangement of 6 hyperplanes of dimension 3 and rank 2 - sage: ca, p0 = a.cone(permutation=True) # optional - sage.combinat - sage: m = ca.matroid() # optional - sage.combinat - sage: a1, p = a.hyperplane_section(proj=False) # optional - sage.combinat - sage: a1 # optional - sage.combinat - Arrangement of 6 hyperplanes of dimension 2 and rank 2 - sage: p = Permutation([p(j) for j in [1 .. 6]] + [7]); p # optional - sage.combinat - [6, 5, 2, 1, 4, 3, 7] - sage: ca1, p1 = a1.cone(permutation=True) # optional - sage.combinat - sage: m1 = ca1.matroid() # optional - sage.combinat - sage: q = p0.inverse() * p * p1 # optional - sage.combinat - sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) # optional - sage.combinat - True - sage: a0 = hyperplane_arrangements.Shi(4) # optional - sage.combinat - sage: a = a0.hyperplane_section(proj=False)[0]; a # optional - sage.combinat - Arrangement of 12 hyperplanes of dimension 3 and rank 3 - sage: ca, p1 = a.cone(permutation=True) # optional - sage.combinat - sage: m = ca.matroid().truncation() # optional - sage.combinat - sage: a1, p = a.hyperplane_section(proj=False); a1 # optional - sage.combinat - Arrangement of 12 hyperplanes of dimension 2 and rank 2 - sage: p = Permutation([p(j) for j in [1 .. 12]] + [13]); p # optional - sage.combinat - [1, 5, 11, 12, 9, 10, 7, 8, 3, 4, 2, 6, 13] - sage: ca1, p2 = a1.cone(permutation=True) # optional - sage.combinat - sage: m1 = ca1.matroid() # optional - sage.combinat - sage: q = p1.inverse() * p * p2 # optional - sage.combinat - sage: m.is_isomorphism(m1, {j: q(j + 1) - 1 for j in m.groundset()}) # optional - sage.combinat - True - """ - from sage.matrix.constructor import Matrix - if proj and not self.is_central(): - raise TypeError('The arrangement is not central') - n0 = self.dimension() - r = self.n_hyperplanes() - if not proj: - H, perm0 = self.cone(permutation=True) - H1, perm1 = H.hyperplane_section() - perm = perm0 * perm1 - k = perm(r + 1) - mat = Matrix(h.coefficients()[1:] for h in H1) - mat.swap_rows(0, k - 1) - if k == 1: - trans = Permutation(range(1, r + 2)) - else: - trans = Permutation([k] + list(range(2, k)) + [1] + list(range(k + 1, r + 2))) - perm = perm * trans - perm = Permutation([perm(j) - 1 for j in range(1, r + 1)]) - for j in range(mat.ncols()): - if mat[0, j] != 0: - mat.swap_columns(0, j) - break - for j in range(1, mat.ncols()): - mat.add_multiple_of_column(j, 0, -mat[0, j] / mat[0, 0]) - vrs = H1.parent().variable_names()[1:] - A1 = HyperplaneArrangements(self.base_ring(), names=vrs) - mat_rows = mat.rows()[1:] - H1b = A1(mat_rows) - L1b = [_ for _ in H1b] - L2 = [] - for h in mat_rows: - h0 = A1(h)[0] - j = L1b.index(h0) - L2.append(j + 1) - perm2 = Permutation(L2) - return (H1b, perm * perm2) - P = self.intersection_poset(element_label="subspace") - n1 = self.center().dimension() - U = [p.linear_part().basis()[0] for p in P if p.dimension() == n1 + 1] - U0 = sum(U) - for v in ZZ**n0: - v1 = v + U0 - if 0 not in [w * v1 for w in U]: - break - h0 = self.parent()((0,) + tuple(v1)) - H1 = self.add_hyperplane(h0) - return H1.restriction(h0, permutation=True) - - def _fundamental_group_(self, proj=False): - r""" - It computes the fundamental group of the complement of an affine - hyperplane arrangement in `\mathbb{C}^n`, or a projective hyperplane - arrangement in `\mathbb{CP}^n`, `n=1,2`, whose equations have - coefficients in a subfield of ``QQbar`` - - INPUT: - - - ``proj`` -- (optional, default ``False``). It decides if it computes the - fundamental group of the complement in the affine or projective space - - OUTPUT: - - A group finitely presented with the assignation of each hyperplane to - a member of a group (meridian). - - EXAMPLES:: - - sage: A. = HyperplaneArrangements(QQ) - sage: L = [y + x, y + x - 1] - sage: H = A(L) - sage: G, dic = H._fundamental_group_(); G # optional - sirocco - Finitely presented group < x0, x1 | > - sage: L = [x, y, x + 1, y + 1, x - y] - sage: H = A(L); list(H) - [Hyperplane 0*x + y + 0, - Hyperplane 0*x + y + 1, - Hyperplane x - y + 0, - Hyperplane x + 0*y + 0, - Hyperplane x + 0*y + 1] - sage: G, dic = H._fundamental_group_() # optional - sirocco - sage: G.simplified() # optional - sirocco - Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, - x2^-1*x0^-1*x2*x4*x0*x4^-1, - x0*x1*x3*x0^-1*x3^-1*x1^-1, - x0*x2*x4*x2^-1*x0^-1*x4^-1, - x0*x1^-1*x0^-1*x3^-1*x1*x3, - x4^-1*x3^-1*x1*x3*x4*x3^-1*x1^-1*x3 > - sage: dic # optional - sirocco - {0: [x4], 1: [x3], 2: [x0], 3: [x2], 4: [x1], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} - sage: H=A(x,y,x+y) - sage: H._fundamental_group_() # optional - sirocco - (Finitely presented group < x0, x1, x2 | x1*x2*x0*x2^-1*x1^-1*x0^-1, x1*x0^-1*x2^-1*x1^-1*x2*x0 >, - {0: [x2], 1: [x0], 2: [x1], 3: [x2^-1*x1^-1*x0^-1]}) - sage: H._fundamental_group_(proj=True) # optional - sirocco - (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) - sage: H = hyperplane_arrangements.braid(4).essentialization() # optional - sage.graphs - sage: G, dic = H._fundamental_group_(proj=True) # optional - sage.graphs, sirocco - sage: h = G.simplification_isomorphism() # optional - sage.graphs, sirocco - sage: G.simplified() # optional - sage.graphs, sirocco - Finitely presented group < x0, x1, x3, x4, x5 | x0*x3*x0^-1*x3^-1, - x1*x4*x1^-1*x4^-1, - x1*x5*x1^-1*x0^-1*x5^-1*x0, - x5*x3*x4*x3^-1*x5^-1*x4^-1, - x5^-1*x1^-1*x0*x1*x5*x0^-1, - x4*x5^-1*x4^-1*x3^-1*x5*x3 > - sage: {j: h(dic[j][0]) for j in dic.keys()} # optional - sage.graphs, sirocco - {0: x5, 1: x0, 2: x1, 3: x3, 4: x4, 5: x0^-1*x5^-1*x4^-1*x1^-1*x3^-1} - - .. WARNING:: - - This functionality requires the sirocco package to be installed. - """ - from sage.groups.free_group import FreeGroup - from sage.rings.qqbar import QQbar - from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement - n = self.dimension() - r = len(self) - affine = n == 2 and not proj - projective = n == 3 and self.is_central() and proj - if (n == 1 and not proj) or (n == 2 and proj and self.is_central()): - r1 = r - proj - G = FreeGroup(r1) / [] - dic = {j: (j,) for j in range(1, r)} - dic[r] = tuple(-j for j in reversed(range(1, r))) - return (G, dic) - casos = affine or projective - if not casos: - raise TypeError('The method does not apply') - K = self.base_ring() - if not K.is_subring(QQbar): - raise TypeError('the base field is not in QQbar') - S = self.parent().ambient_space().symmetric_space() - if projective: - S = PolynomialRing(K, S.gens()[:-1]) - infinity = [0, 0, 0, 1] == self[0].primitive().coefficients() - L = [] - for h in self: - coeff = h.coefficients() - if projective: - coeff = (coeff[3], coeff[1], coeff[2]) - V = (1,) + S.gens() - p = S.sum(V[i]*c for i, c in enumerate(coeff)) - if p.degree() > 0: - L.append(p) - G, dic = fundamental_group_arrangement(L, puiseux=True, projective=projective and not infinity, simplified=False) - if infinity: - p = Permutation([r] + [j for j in range(1, r)]) - dic = {j: dic[p(j + 1) - 1] for j in range(r)} - return (G, dic) - - def fundamental_group(self, projective=False): - r""" - It computes the fundamental group of the complement of an affine - hyperplane arrangement in `\mathbb{C}^n`, or a projective hyperplane - arrangement in `\mathbb{CP}^n`, whose equations have - coefficients in a subfield of ``QQbar`` - - INPUT: - - - ``projective`` -- (optional, default ``False``). It decides if it computes the - fundamental group of the complement in the affine or projective space - - OUTPUT: - - A group finitely presented with the assignation of each hyperplane to - a member of a group (meridian). - - EXAMPLES:: - - sage: A. = HyperplaneArrangements(QQ) - sage: L = [y + x, y + x - 1] - sage: H = A(L) - sage: G, dic = H.fundamental_group(); G # optional - sirocco - Finitely presented group < x0, x1 | > - sage: L = [x, y, x + 1, y + 1, x - y] - sage: H = A(L); list(H) - [Hyperplane 0*x + y + 0, - Hyperplane 0*x + y + 1, - Hyperplane x - y + 0, - Hyperplane x + 0*y + 0, - Hyperplane x + 0*y + 1] - sage: G, dic = H.fundamental_group() # optional - sirocco - sage: G.simplified() # optional - sirocco - Finitely presented group < x0, x1, x2, x3, x4 | x3*x2*x3^-1*x2^-1, - x2^-1*x0^-1*x2*x4*x0*x4^-1, - x0*x1*x3*x0^-1*x3^-1*x1^-1, - x0*x2*x4*x2^-1*x0^-1*x4^-1, - x0*x1^-1*x0^-1*x3^-1*x1*x3, - x4^-1*x3^-1*x1*x3*x4*x3^-1*x1^-1*x3 > - sage: dic # optional - sirocco - {0: [x4], 1: [x3], 2: [x0], 3: [x2], 4: [x1], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} - sage: H=A(x,y,x+y) - sage: H.fundamental_group() # optional - sirocco - (Finitely presented group < x0, x1, x2 | x1*x2*x0*x2^-1*x1^-1*x0^-1, - x1*x0^-1*x2^-1*x1^-1*x2*x0 >, - {0: [x2], 1: [x0], 2: [x1], 3: [x2^-1*x1^-1*x0^-1]}) - sage: H.fundamental_group(projective=True) # optional - sirocco - (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) - sage: H = hyperplane_arrangements.braid(4) # optional - sage.groups - sage: G, dic = H.fundamental_group(projective=True) # optional - sirocco, sage.groups - sage: h = G.simplification_isomorphism() # optional - sirocco, sage.groups - sage: G.simplified() # optional - sirocco, sage.groups - Finitely presented group < x0, x1, x3, x4, x5 | x0*x3*x0^-1*x3^-1, x1*x4*x1^-1*x4^-1, - x1*x5*x1^-1*x0^-1*x5^-1*x0, - x5*x3*x4*x3^-1*x5^-1*x4^-1, - x5^-1*x1^-1*x0*x1*x5*x0^-1, - x4*x5^-1*x4^-1*x3^-1*x5*x3 > - sage: {j: h(dic[j][0]) for j in dic.keys()} # optional - sirocco, sage.groups - {0: x5, 1: x0, 2: x1, 3: x0^-1*x5^-1*x4^-1*x1^-1*x3^-1, 4: x4, 5: x3} - sage: H = hyperplane_arrangements.coordinate(5) - sage: g = H.fundamental_group(projective=True)[0] # optional - sirocco - sage: g.is_abelian(), g.abelian_invariants() # optional - sirocco - (True, (0, 0, 0, 0)) - - .. WARNING:: - - This functionality requires the sirocco package to be installed. - """ - n = self.dimension() - if n <= 2 or (n == 3 and projective): - return self._fundamental_group_(proj=projective) - H1, P = self.hyperplane_section(proj=projective) - H2, dic = H1.fundamental_group(projective=projective) - if not projective: - P = Permutation(list(P) + [self.n_hyperplanes() + 1]) - dic = {j: dic[P(j + 1) - 1] for j in dic.keys()} - return (H2, dic) - class HyperplaneArrangements(Parent, UniqueRepresentation): """ From 26fc7d72e37873d99723ce866915ec262399a62c Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 21 Jul 2023 15:38:13 +0200 Subject: [PATCH 116/122] typo in docs --- src/sage/schemes/curves/zariski_vankampen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 6fb789f6903..7950d440017 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1222,7 +1222,7 @@ def conjugate_positive_form(braid): 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`. + the braids `\tau_i=\gamma_i\alpha_i\gamma_i^{-1}` pairwise commute and `\alpha=\prod_{i=1}^{r} \tau_i`. EXAMPLES:: From d9568b178d10391dfdab618d0a56806fe4e21f05 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 2 Aug 2023 23:19:07 +0200 Subject: [PATCH 117/122] avoid warnings in test for zariski_vankampen --- src/sage/schemes/curves/zariski_vankampen.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 7950d440017..3c04e37ab5c 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -955,13 +955,13 @@ def geometric_basis(G, E, EC0, p, dual_graph): Internal = G.subgraph(vertices=InternalVertices, edges=InternalEdges) for i, ECi in enumerate(EC): # q and r are the points we will cut through if ECi in Internal: - EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi) and v != ECi] + 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]) and v != EC[-i]] + 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:])) From 9edfab6e5483b070f1f97da65ff827dc5df5d9b7 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 15 Aug 2023 17:05:02 +0200 Subject: [PATCH 118/122] introduce changes from #36092 --- src/sage/schemes/curves/zariski_vankampen.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 3c04e37ab5c..8bed1ed00ff 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -966,7 +966,11 @@ def geometric_basis(G, E, EC0, p, dual_graph): q = EC[-i] connecting_path = list(reversed(EC[-i:])) break - distancequotients = [(E.distance(q, v)**2 / Internal.distance(q, v), v) for v in EI] + # 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 = Internal.shortest_path(q, r) for i, v in enumerate(cutpath): @@ -990,7 +994,7 @@ def geometric_basis(G, E, EC0, p, dual_graph): if r == EC[qi + 1]: E1, E2 = E2, E1 for v in [q, r]: - for n in E.neighbors(v): + 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): From ae015d146f3567305accf19730fc7f40959fc426 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 22 Aug 2023 13:18:05 +0200 Subject: [PATCH 119/122] changes proposed by miguelmarco --- src/sage/schemes/curves/affine_curve.py | 3 +- src/sage/schemes/curves/zariski_vankampen.py | 39 +++++++++++--------- 2 files changed, 22 insertions(+), 20 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index dc98bbb877f..1ef7c301eac 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1834,8 +1834,7 @@ def braid_monodromy(self): raise NotImplementedError("the base field must have an embedding" " to the algebraic field") f = self.defining_polynomial() - bm = braid_monodromy(f)[0] - return bm + return braid_monodromy(f)[0] def riemann_surface(self, **kwargs): r""" diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 8bed1ed00ff..e746c662344 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -357,10 +357,13 @@ def voronoi_cells(V): OUTPUT: - The graph of the 1-skeleton of ``G``, the subgraph ``E`` of the boundary, a vertex ``p`` in ``E``, - a counterclockwise orientation ``EC`` of ``E`` (as an ordered list of vertices with identical - first and last elements), and the dual graph ``DG`` of ``V``, where the vertices are labelled - by the compact regions of ``V`` and the edges by their dual edges. + - ``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:: @@ -568,17 +571,17 @@ def newton(f, x0, i0): return x0 - f(x0) / f.derivative()(i0) -def fieldI(F0): +def fieldI(field): r""" Return the (either double or trivial) extension of a number field which contains ``I``. INPUT: - - ``F0`` -- a number field with an embedding in ``QQbar``. + - ``field`` -- a number field with an embedding in ``QQbar``. OUTPUT: - The extension ``F`` of ``F0`` containing ``I`` with an embedding in ``QQbar``. + The extension ``F`` of ``field`` containing ``I`` with an embedding in ``QQbar``. EXAMPLES:: @@ -590,7 +593,7 @@ def 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 - Another example where ``F`` and ``F0`` coincide.:: + 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) @@ -601,17 +604,17 @@ def fieldI(F0): True """ I0 = QQbar.gen() - if I0 in F0: - return F0 - F0a = F0[I0] - F1a = F0a.absolute_field('b0') - b0 = F1a.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 = F1a.embeddings(QQbar) + qembd = field_b.embeddings(QQbar) for h1 in qembd: b1 = h1(b0) - b2 = h1(F1a(F0a.gen(0))) - b3 = F0.gen(0) + 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 @@ -773,7 +776,7 @@ def braid_in_segment(glist, x0, x1, precision=dict()): - ``glist`` -- a tuple of polynomials in two variables - ``x0`` -- a Gauss rational - ``x1`` -- a Gauss rational - - ``precision`` -- a dictionary (default, an empty one) which will apply to each element of ``glist`` a *precision* + - ``precision`` -- a dictionary (default, an empty one) which assigns a number precision bits to each element of ``glist`` OUTPUT: @@ -1320,7 +1323,7 @@ def braid2rels(L): k = min(T1) - 1 B0 = BraidGroup(m) F0 = FreeGroup(m) - br0 = B0([sign(_)*(abs(_)-k) for _ in T]) + 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:])) From 3ff8247af24cf7358eea4632f00e5151cf932bf0 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 22 Aug 2023 13:37:27 +0200 Subject: [PATCH 120/122] more changes from review and from #36022 --- src/sage/schemes/curves/projective_curve.py | 5 ++++- src/sage/schemes/curves/zariski_vankampen.py | 16 +++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index b27ab071863..85d661c65ee 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -201,6 +201,7 @@ class ProjectiveCurve(Curve_generic, AlgebraicScheme_subscheme_projective): 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. @@ -603,6 +604,7 @@ class ProjectivePlaneCurve(ProjectiveCurve): 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. @@ -1436,6 +1438,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() @@ -1742,7 +1745,7 @@ def fundamental_group(self): 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 + sage: C.fundamental_group() # random # 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 > diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index e746c662344..a48dd341606 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -361,9 +361,9 @@ def voronoi_cells(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) + 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. + by the compact regions of ``V`` and the edges by their dual edges. EXAMPLES:: @@ -1380,9 +1380,8 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv of the complement of the affine curve will be computed, adding one relation if ``projective`` is set to ``True``. - - ``vertical`` -- list of integers (default: ``[]``); a list of integers in ``[1..r]`` where - ``r`` is the number of braids; the corresponding vertical lines are taking out from the complement - of the curve. + - ``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. @@ -1465,10 +1464,9 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): 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``. + 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. @@ -1515,7 +1513,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): sage: fundamental_group(f) # optional - sirocco Finitely presented group < x0 | > - We compute the fundamental group of the complement of a quartic with ``True`` for ``Puiseux``:: + We compute the fundamental group of the complement of a quartic using the ``puiseux`` option:: sage: from sage.schemes.curves.zariski_vankampen import fundamental_group # optional - sirocco sage: R. = QQ[] From c7dadc71105dba9fd4a1ba5a9ee60c1e4484a122 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 23 Aug 2023 14:01:58 +0200 Subject: [PATCH 121/122] some tests --- src/sage/schemes/curves/zariski_vankampen.py | 29 +++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index a48dd341606..d186db2ab4c 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -271,7 +271,7 @@ def corrected_voronoi_diagram(points): return V -def orient_circuit(circuit, convex=False): +def orient_circuit(circuit, convex=False, precision=53, verbose=False): r""" Reverse a circuit if it goes clockwise; otherwise leave it unchanged. @@ -280,7 +280,11 @@ def orient_circuit(circuit, convex=False): - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given by a list of edges - - ``convex`` -- a boolean function, if set to ``True`` a simpler computation is made + - ``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: @@ -320,6 +324,20 @@ def orient_circuit(circuit, convex=False): 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)) """ vectors = [v[1].vector() - v[0].vector() for v in circuit] circuit_vertex = (circuit[0][0],) + tuple(e[1] for e in circuit) @@ -332,7 +350,7 @@ def orient_circuit(circuit, convex=False): elif pr < 0: # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) return tuple(reversed(circuit_vertex)) - prec = 53 + prec = precision while True: CIF = ComplexIntervalField(prec) totalangle = sum((CIF(*vectors[i]) / CIF(*vectors[i - 1])).argument() @@ -344,6 +362,8 @@ def orient_circuit(circuit, convex=False): # return circuit return circuit_vertex prec *= 2 + if verbose: + print(prec) def voronoi_cells(V): @@ -776,7 +796,8 @@ def braid_in_segment(glist, x0, x1, precision=dict()): - ``glist`` -- a tuple of polynomials in two variables - ``x0`` -- a Gauss rational - ``x1`` -- a Gauss rational - - ``precision`` -- a dictionary (default, an empty one) which assigns a number precision bits to each element of ``glist`` + - ``precision`` -- a dictionary (default `dict()`) which assigns a number precision bits + to each element of ``glist`` OUTPUT: From 4e38e31a5ef45113dc73cf3db64e7a24907e9ace Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 23 Aug 2023 17:17:04 +0200 Subject: [PATCH 122/122] another test (codecov) for braid_monodromy --- src/sage/schemes/curves/zariski_vankampen.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index d186db2ab4c..52b2089b8dd 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1166,7 +1166,8 @@ def braid_monodromy(f, arrangement=()): True sage: bm1[1] # optional - sirocco {0: 0, 1: 1, 2: 0, 3: 0} - + sage: braid_monodromy(R(1)) + ([], {}) """ global roots_interval_cache F = fieldI(f.base_ring())