From c7d36cad2e6b01c9803834bae26c24a91db125fc Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 20 Jul 2023 17:57:57 +0200 Subject: [PATCH 01/95] new branch --- .../hyperplane_arrangement/arrangement.py | 516 ++++++++++++++++-- 1 file changed, 463 insertions(+), 53 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 7cc2b479573..093e456f055 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,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. @@ -633,7 +634,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 +643,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 +665,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 +691,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 @@ -695,19 +734,29 @@ 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. + Return the cone over the hyperplane arrangement `H_1,\dots,H_n`. 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: - 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 ``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:: @@ -741,6 +790,13 @@ 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) # 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(): @@ -750,7 +806,17 @@ 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) + return result @cached_method def intersection_poset(self, element_label="int"): @@ -843,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") @@ -875,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 @@ -1035,7 +1101,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. @@ -1043,10 +1109,20 @@ def restriction(self, hyperplane): - ``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``. + 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:: @@ -1056,6 +1132,12 @@ def restriction(self, hyperplane): 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 @@ -1100,7 +1182,21 @@ 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) + if len(L) > len(set(L)): + return (result, None) + else: + P = Permutation(L) + return (result, P) + else: + return result def change_ring(self, base_ring): """ @@ -1471,7 +1567,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. @@ -1479,9 +1575,20 @@ def essentialization(self): 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:: @@ -1564,7 +1671,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""" @@ -1905,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. @@ -1943,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: @@ -2234,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: @@ -2262,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: @@ -2398,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""" @@ -2510,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 @@ -3038,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): """ @@ -3350,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) @@ -3365,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): @@ -3376,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 @@ -3385,6 +3503,298 @@ 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 26c64cc79cff8f441e4662543140f347a484d24e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 21 Jul 2023 19:01:00 +0200 Subject: [PATCH 02/95] Initial changes to deal with ordered hyperplane arrangements --- src/sage/geometry/all.py | 1 + .../hyperplane_arrangement/arrangement.py | 495 +++++++++++------- 2 files changed, 314 insertions(+), 182 deletions(-) diff --git a/src/sage/geometry/all.py b/src/sage/geometry/all.py index 3b7dcf756d8..263a530b989 100644 --- a/src/sage/geometry/all.py +++ b/src/sage/geometry/all.py @@ -16,4 +16,5 @@ lazy_import('sage.geometry.voronoi_diagram', 'VoronoiDiagram') lazy_import('sage.geometry.ribbon_graph', 'RibbonGraph') lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'HyperplaneArrangements') +lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'OrderedHyperplaneArrangements') lazy_import('sage.geometry.hyperplane_arrangement.library', 'hyperplane_arrangements') diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 093e456f055..fb117cd7426 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -805,7 +805,10 @@ def cone(self, variable='t', permutation=False): hyperplanes.append([0, 1] + [0] * self.dimension()) P = self.parent() names = (variable,) + P._names - H = HyperplaneArrangements(self.parent().base_ring(), names=names) + if 'Ordered' in str(type(self)): + H = OrderedHyperplaneArrangements(self.parent().base_ring(), names=names) + else: + H = HyperplaneArrangements(self.parent().base_ring(), names=names) result = H(*hyperplanes, backend=self._backend) if permutation: L1 = [_ for _ in result] @@ -1181,7 +1184,10 @@ def restriction(self, hyperplane, permutation=False): hyperplanes.append([A, b]) names = list(parent._names) names.pop(pivot) - H = HyperplaneArrangements(parent.base_ring(), names=tuple(names)) + if 'Ordered' in str(type(self)): + H = OrderedHyperplaneArrangements(parent.base_ring(), names=tuple(names)) + else: + H = HyperplaneArrangements(parent.base_ring(), names=tuple(names)) result = H(*hyperplanes, signed=False, backend=self._backend) if permutation: L1 = [_ for _ in result] @@ -3615,186 +3621,6 @@ def hyperplane_section(self, proj=True): 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): """ @@ -4116,3 +3942,308 @@ def _coerce_map_from_(self, P): if isinstance(P, HyperplaneArrangements): return self.base_ring().has_coerce_map_from(P.base_ring()) return super()._coerce_map_from_(P) + + +class OrderedHyperplaneArrangements(HyperplaneArrangements): + """ + Hyperplane arrangements. + + For more information on hyperplane arrangements, see + :mod:`sage.geometry.hyperplane_arrangement.arrangement`. + + INPUT: + + - ``base_ring`` -- ring; the base ring + + - ``names`` -- tuple of strings; the variable names + + EXAMPLES:: + + sage: H. = HyperplaneArrangements(QQ) + sage: x + Hyperplane x + 0*y + 0 + sage: x + y + Hyperplane x + y + 0 + sage: H(x, y, x-1, y-1) + Arrangement + """ + + def _element_constructor_(self, *args, **kwds): + """ + Construct an element of ``self``. + + INPUT: + + - ``*args`` -- positional arguments, each defining a + hyperplane; alternatively, a single polytope or a single + hyperplane arrangement + + - ``signed`` -- boolean (optional, default: ``True``); whether to + preserve signs of hyperplane equations + + - ``warn_duplicates`` -- boolean (optional, default: ``False``); + whether to issue a warning if duplicate hyperplanes were + passed -- note that duplicate hyperplanes are always removed, + whether or not there is a warning shown + + - ``check`` -- boolean (optional, default: ``True``); whether to + perform argument checking. + + EXAMPLES:: + + sage: L. = HyperplaneArrangements(QQ) + sage: L._element_constructor_(x, y) + Arrangement + sage: L._element_constructor_([x, y]) + Arrangement + sage: L._element_constructor_([0, 1, 0], [0, 0, 1]) + Arrangement + sage: L._element_constructor_([[0, 1, 0], [0, 0, 1]]) + Arrangement + + sage: L._element_constructor_(polytopes.hypercube(2)) + Arrangement <-x + 1 | -y + 1 | y + 1 | x + 1> + + sage: L(x, x, warn_duplicates=True) + doctest:...: UserWarning: Input contained 2 hyperplanes, but only 1 are distinct. + Arrangement + sage: L(-x, x + y - 1, signed=False) + Arrangement <-x - y + 1 | x> + + TESTS:: + + sage: L() + Empty hyperplane arrangement of dimension 2 + sage: L(0) # zero is equivalent to no argument, Issue #8648 + Empty hyperplane arrangement of dimension 2 + sage: L(0*x) # degenerate hyperplane is NOT allowed + Traceback (most recent call last): + ... + ValueError: linear expression must be non-constant to define a hyperplane + sage: L(0*x, y) # ditto + Traceback (most recent call last): + ... + ValueError: linear expression must be non-constant to define a hyperplane + """ + if len(args) == 1: + arg = args[0] + if isinstance(arg, HyperplaneArrangementElement) and args[0].parent() is self: + # optimization if argument is already a hyperplane arrangement + return arg + if arg == 0 and not isinstance(arg, Hyperplane): + # zero = neutral element under addition = the empty hyperplane arrangement + args = [] + # process keyword arguments + not_char2 = (self.base_ring().characteristic() != 2) + signed = kwds.pop('signed', not_char2) + warn_duplicates = kwds.pop('warn_duplicates', False) + check = kwds.pop('check', True) + backend = kwds.pop('backend', None) + if len(kwds) > 0: + raise ValueError('unknown keyword argument') + # process positional arguments + AA = self.ambient_space() + try: + hyperplanes = [AA(_) for _ in args] + except (TypeError, ValueError, AttributeError): + if len(args) > 1: + raise + arg = args[0] + if hasattr(arg, 'Hrepresentation'): + hyperplanes = [AA(h) for h in arg.Hrepresentation()] + else: + hyperplanes = [AA(_) for _ in arg] + hyperplanes = [h.primitive(signed) for h in hyperplanes] + if warn_duplicates: + from warnings import warn + warn('Input contained repeated hyperplanes.') + # argument checking (optional but recommended) + if check: + if signed and not not_char2: + raise ValueError('cannot be signed in characteristic 2') + for h in hyperplanes: + if h.A() == 0: + raise ValueError('linear expression must be non-constant to define a hyperplane') + if not_char2 and -h in hyperplanes: + raise ValueError('arrangement cannot simultaneously have h and -h as hyperplane') + return self.element_class(self, tuple(hyperplanes), backend=backend) + + 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) From 0d08854d072463a387e4670efa2bc8690103830c Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 22 Jul 2023 16:47:18 +0200 Subject: [PATCH 03/95] creating element for ordered hyperplane arrangements to have fundamenta_group as its method --- .../hyperplane_arrangement/arrangement.py | 632 ++++++++---------- .../hyperplane_arrangement/library.py | 7 +- 2 files changed, 285 insertions(+), 354 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index fb117cd7426..f37fc5008d0 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -634,7 +634,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,39 +643,26 @@ 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:: sage: H. = HyperplaneArrangements(QQ) + sage: H1. = OrderedHyperplaneArrangements(QQ) sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0]) sage: B = H([1,1,1], [1,-1,1], [1,0,-1]) - sage: A.union(B) - Arrangement of 8 hyperplanes of dimension 2 and rank 2 - sage: A | B # syntactic sugar + sage: C = A.union(B); C 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 + sage: C == A | B # syntactic sugar True - sage: p1 - [2, 4, 1, 3] + sage: A1 = H1(A) + sage: B1 = H1(B) + sage: C1 = A1.union(B1); C1 + Arrangement of 8 hyperplanes of dimension 2 and rank 2 + sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()] + [0, 5, 6, 1, 2, 3, 7, 4] A single hyperplane is coerced into a hyperplane arrangement if necessary:: @@ -692,25 +679,8 @@ def union(self, other, permutation=False): """ 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 add_hyperplane = union @@ -734,7 +704,7 @@ 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`. @@ -742,21 +712,12 @@ def cone(self, variable='t', permutation=False): - ``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`. + 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:: @@ -770,7 +731,10 @@ def cone(self, variable='t', permutation=False): EXAMPLES:: sage: a. = hyperplane_arrangements.semiorder(3) # optional - sage.combinat + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: a1 = H(a) # optional - sage.combinat sage: b = a.cone() # optional - sage.combinat + sage: b1 = a1.cone() # optional - sage.combinat sage: a.characteristic_polynomial().factor() # optional - sage.combinat x * (x^2 - 6*x + 12) sage: b.characteristic_polynomial().factor() # optional - sage.combinat @@ -790,13 +754,8 @@ 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 + sage: [b1.hyperplanes().index(h) for h in b.hyperplanes()] # optional - sage.combinat + [0, 2, 4, 6, 1, 3, 5] """ hyperplanes = [] for h in self.hyperplanes(): @@ -810,15 +769,6 @@ def cone(self, variable='t', permutation=False): else: 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 @cached_method @@ -1104,7 +1054,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, repetitions=False): r""" Return the restriction to a hyperplane. @@ -1112,35 +1062,36 @@ 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. + - ``repetitions`` -- (boolean, default: ``False``) eliminate + repetitions for ordered arrangements OUTPUT: - If ``permutation`` is ``False``, the restriction `\mathcal{A}_H` of the + 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:: sage: A. = hyperplane_arrangements.braid(4); A # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 4 and rank 3 + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: A1 = L(A) # optional - sage.graphs sage: H = A[0]; H # optional - sage.graphs Hyperplane 0*u + 0*x + y - z + 0 - sage: R = A.restriction(H); R # optional - sage.graphs + sage: R = A.restriction(H); R # optional - sage.graphs + Arrangement + sage: A1.restriction(H, repetitions=True).hyperplanes() # optional - sage.graphs + (Hyperplane 0*u + x - z + 0, + Hyperplane 0*u + x - z + 0, + Hyperplane u - x + 0*z + 0, + Hyperplane u + 0*x - z + 0, + Hyperplane u + 0*x - z + 0) + sage: A1.restriction(H) # 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: A.add_hyperplane(z).restriction(z) # optional - sage.graphs + Arrangement of 6 hyperplanes of dimension 3 and rank 3 + sage: A.add_hyperplane(u).restriction(u) # optional - sage.graphs + Arrangement of 6 hyperplanes of dimension 3 and rank 3 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 @@ -1186,23 +1137,16 @@ def restriction(self, hyperplane, permutation=False): names.pop(pivot) if 'Ordered' in str(type(self)): H = OrderedHyperplaneArrangements(parent.base_ring(), names=tuple(names)) + if not repetitions: + L = list(hyperplanes) + hyperplanes = () + for h in L: + if h not in hyperplanes: + hyperplanes += (h,) else: 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 result def change_ring(self, base_ring): """ @@ -1573,7 +1517,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. @@ -1581,20 +1525,10 @@ 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}`. + new hyperplane arrangement. EXAMPLES:: @@ -1678,17 +1612,7 @@ def echelon_col_iter(row_iter): # 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 result def sign_vector(self, p): r""" @@ -3509,6 +3433,18 @@ def derivation_module_basis(self, algorithm="singular"): else: raise ValueError("invalid algorithm") + +class OrderedHyperplaneArrangementElement(HyperplaneArrangementElement): + """ + A hyperplane arrangement. + + .. WARNING:: + + You should never create + :class:`HyperplaneArrangementElement` instances directly, + always use the parent. + """ + def hyperplane_section(self, proj=True): r""" It computes a generic hyperplane section of ``self``, an arrangement @@ -3521,94 +3457,74 @@ def hyperplane_section(self, proj=True): 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}`. + generic hyperplane. EXAMPLES:: - sage: A. = hyperplane_arrangements.braid(4); A # optional - sage.graphs + sage: A0. = hyperplane_arrangements.braid(4); A0 # optional - sage.graphs Arrangement of 6 hyperplanes of dimension 4 and rank 3 + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: A = L(A0) # optional - sage.graphs sage: M = A.matroid() # optional - sage.graphs - sage: A1, P = A.hyperplane_section() # optional - sage.graphs + sage: A1 = 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 + sage: A2 = 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 + sage: T1.is_isomorphic(M2) # optional - sage.graphs True - sage: a = hyperplane_arrangements.semiorder(3); a # optional - sage.combinat + sage: T1.isomorphism(M2) # optional - sage.graphs + {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5} + sage: a0 = hyperplane_arrangements.semiorder(3); a0 # optional - sage.combinat Arrangement of 6 hyperplanes of dimension 3 and rank 2 - sage: ca, p0 = a.cone(permutation=True) # optional - sage.combinat + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: a = L(a0) # optional - sage.combinat + sage: ca = a.cone() # optional - sage.combinat sage: m = ca.matroid() # optional - sage.combinat - sage: a1, p = a.hyperplane_section(proj=False) # optional - sage.combinat + sage: a1 = 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: ca1 = a1.cone() # 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 + sage: m.isomorphism(m1) # optional - sage.combinat + {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6} + sage: p0 = hyperplane_arrangements.Shi(4) # optional - sage.combinat + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: p = L(p0) # optional - sage.combinat + sage: a = p.hyperplane_section(proj=False); 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: ca = a.cone() # optional - sage.combinat sage: m = ca.matroid().truncation() # optional - sage.combinat - sage: a1, p = a.hyperplane_section(proj=False); a1 # optional - sage.combinat + sage: a1 = 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: ca1 = a1.cone() # 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 + sage: m1.is_isomorphism(m, {j: j for j in range(13)}) # 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) + H = self.cone() + H1 = H.hyperplane_section() 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)]) + m = mat.nrows() for j in range(mat.ncols()): - if mat[0, j] != 0: + if mat[m - 1, 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]) + mat.add_multiple_of_column(j, 0, -mat[m - 1, j] / mat[m - 1, 0]) vrs = H1.parent().variable_names()[1:] - A1 = HyperplaneArrangements(self.base_ring(), names=vrs) - mat_rows = mat.rows()[1:] + A1 = OrderedHyperplaneArrangements(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) + return H1b 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] @@ -3619,7 +3535,188 @@ def hyperplane_section(self, proj=True): break h0 = self.parent()((0,) + tuple(v1)) H1 = self.add_hyperplane(h0) - return H1.restriction(h0, permutation=True) + return H1.restriction(h0) + + 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. = OrderedHyperplaneArrangements(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 x + 0*y + 0, + Hyperplane 0*x + y + 0, + Hyperplane x + 0*y + 1, + Hyperplane 0*x + y + 1, + Hyperplane x - y + 0] + 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: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 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: [x0], 1: [x2], 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: A3. = OrderedHyperplaneArrangements(QQ) + sage: H = A3(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. = OrderedHyperplaneArrangements(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 x + 0*y + 0, + Hyperplane 0*x + y + 0, + Hyperplane x + 0*y + 1, + Hyperplane 0*x + y + 1, + Hyperplane x - y + 0] + 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: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 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: [x0], 1: [x2], 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: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(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: L. = OrderedHyperplaneArrangements(QQ) + sage: H = L(H) + 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 = self.hyperplane_section(proj=projective) + H2, dic = H1.fundamental_group(projective=projective) + return (H2, dic) class HyperplaneArrangements(Parent, UniqueRepresentation): @@ -3967,6 +4064,7 @@ class OrderedHyperplaneArrangements(HyperplaneArrangements): sage: H(x, y, x-1, y-1) Arrangement """ + Element = OrderedHyperplaneArrangementElement def _element_constructor_(self, *args, **kwds): """ @@ -4004,9 +4102,6 @@ def _element_constructor_(self, *args, **kwds): sage: L._element_constructor_(polytopes.hypercube(2)) Arrangement <-x + 1 | -y + 1 | y + 1 | x + 1> - sage: L(x, x, warn_duplicates=True) - doctest:...: UserWarning: Input contained 2 hyperplanes, but only 1 are distinct. - Arrangement sage: L(-x, x + y - 1, signed=False) Arrangement <-x - y + 1 | x> @@ -4068,182 +4163,17 @@ def _element_constructor_(self, *args, **kwds): raise ValueError('arrangement cannot simultaneously have h and -h as hyperplane') return self.element_class(self, tuple(hyperplanes), backend=backend) - 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. + def _repr_(self): """ - 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 + Return a string representation. OUTPUT: - A group finitely presented with the assignation of each hyperplane to - a member of a group (meridian). + A string. 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. + sage: L. = OrderedHyperplaneArrangements(QQ); L + Ordered hyperplane arrangements in 2-dimensional linear space over Rational Field with coordinates x, y """ - 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) + return 'Ordered hyperplane arrangements in {0}'.format(self.ambient_space()) diff --git a/src/sage/geometry/hyperplane_arrangement/library.py b/src/sage/geometry/hyperplane_arrangement/library.py index 5d0b4aae4d7..f74c9eaa640 100644 --- a/src/sage/geometry/hyperplane_arrangement/library.py +++ b/src/sage/geometry/hyperplane_arrangement/library.py @@ -155,7 +155,7 @@ def bigraphical(self, G, A=None, K=QQ, names=None): for u, v in G.edge_iterator(labels=False, sort_vertices=False): i = vertex_to_int[u] j = vertex_to_int[v] - hyperplanes.append( x[i] - x[j] - A[i][j]) + hyperplanes.append(x[i] - x[j] - A[i][j]) hyperplanes.append(-x[i] + x[j] - A[j][i]) return H(*hyperplanes) @@ -689,7 +689,7 @@ def Shi(self, data, K=QQ, names=None, m=1): x^3 - 54*x^2 + 972*x - 5832 """ if data in NN: - cartan_type = CartanType(["A",data-1]) + cartan_type = CartanType(["A", data - 1]) else: cartan_type = CartanType(data) if not cartan_type.is_crystallographic(): @@ -704,7 +704,7 @@ def Shi(self, data, K=QQ, names=None, m=1): hyperplanes = [] for a in PR: - for const in range(-m+1,m+1): + for const in range(-m + 1, m + 1): hyperplanes.append(sum(a[j]*x[j] for j in range(d))-const) A = H(*hyperplanes) x = polygen(QQ, 'x') @@ -712,4 +712,5 @@ def Shi(self, data, K=QQ, names=None, m=1): A.characteristic_polynomial.set_cache(charpoly) return A + hyperplane_arrangements = HyperplaneArrangementLibrary() From 6781a4b8f2b300830455e592319b7a8b7a8b0177 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 22 Jul 2023 16:58:23 +0200 Subject: [PATCH 04/95] correcting examples --- .../hyperplane_arrangement/arrangement.py | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index f37fc5008d0..b03204af95a 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3436,12 +3436,12 @@ def derivation_module_basis(self, algorithm="singular"): class OrderedHyperplaneArrangementElement(HyperplaneArrangementElement): """ - A hyperplane arrangement. + An ordered hyperplane arrangement. .. WARNING:: You should never create - :class:`HyperplaneArrangementElement` instances directly, + :class:`OrderedHyperplaneArrangementElement` instances directly, always use the parent. """ @@ -4068,7 +4068,7 @@ class OrderedHyperplaneArrangements(HyperplaneArrangements): def _element_constructor_(self, *args, **kwds): """ - Construct an element of ``self``. + Repetition of the corresponding function for hyperplane arrangements without reordering. INPUT: @@ -4079,31 +4079,26 @@ def _element_constructor_(self, *args, **kwds): - ``signed`` -- boolean (optional, default: ``True``); whether to preserve signs of hyperplane equations - - ``warn_duplicates`` -- boolean (optional, default: ``False``); - whether to issue a warning if duplicate hyperplanes were - passed -- note that duplicate hyperplanes are always removed, - whether or not there is a warning shown - - ``check`` -- boolean (optional, default: ``True``); whether to perform argument checking. EXAMPLES:: - sage: L. = HyperplaneArrangements(QQ) + sage: L. = OrderedHyperplaneArrangements(QQ) sage: L._element_constructor_(x, y) - Arrangement + Arrangement sage: L._element_constructor_([x, y]) - Arrangement + Arrangement sage: L._element_constructor_([0, 1, 0], [0, 0, 1]) - Arrangement - sage: L._element_constructor_([[0, 1, 0], [0, 0, 1]]) + Arrangement + sage: L._element_constructor_([[0, 0, 1], [0, 1, 0]]) Arrangement sage: L._element_constructor_(polytopes.hypercube(2)) - Arrangement <-x + 1 | -y + 1 | y + 1 | x + 1> + Arrangement <-x + 1 | -y + 1 | x + 1 | y + 1> sage: L(-x, x + y - 1, signed=False) - Arrangement <-x - y + 1 | x> + Arrangement TESTS:: From ae49054362de8d71ffb8e233ad1f8cfdf0eddb45 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 24 Jul 2023 16:35:58 +0200 Subject: [PATCH 05/95] typo --- .../hyperplane_arrangement/arrangement.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index b03204af95a..b4de142b5fb 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -130,6 +130,17 @@ sage: b == hyperplane_arrangements.coordinate(3) True +In some cases ordered arrangements are useful and the :class:`OrderedHyperplaneArrangements` for which the +hyperplanes are not sorted:: + + sage: H0. = HyperplaneArrangements(QQ) + sage: H0(t0 - t1, t1 - t2, t0 - t2) + Arrangement + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: H(t0 - t1, t1 - t2, t0 - t2) + Arrangement + + Properties of Arrangements -------------------------- @@ -237,7 +248,7 @@ \chi(x) := \sum_{w\in P} \mu(w) x^{dim(w)} -where the sum is `P` is the +where `P` is the :meth:`~HyperplaneArrangementElement.intersection_poset` of the arrangement and `\mu` is the Möbius function of `P`:: @@ -292,6 +303,8 @@ low dimensions. See :meth:`~HyperplaneArrangementElement.plot` for details. +For ordere hyperplane arrangements hyperplane sections and fundamental group are also defined. + TESTS:: sage: H. = HyperplaneArrangements(QQ) From 0c0a32079903ec458fd224c536ce05bf6bbb0f02 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 31 Jul 2023 16:33:59 +0200 Subject: [PATCH 06/95] merge --- .gitignore | 18 + CITATION.cff | 4 +- README.md | 2 +- VERSION.txt | 2 +- build/make/Makefile.in | 8 +- build/pkgs/bliss/distros/gentoo.txt | 1 + build/pkgs/configure/checksums.ini | 6 +- build/pkgs/configure/package-version.txt | 2 +- build/pkgs/sage_conf/install-requires.txt | 2 +- build/pkgs/sage_docbuild/install-requires.txt | 2 +- build/pkgs/sage_setup/install-requires.txt | 2 +- build/pkgs/sage_sws2rst/install-requires.txt | 2 +- build/pkgs/sagelib/install-requires.txt | 2 +- build/pkgs/sagelib/spkg-install | 3 + build/pkgs/sagemath_bliss/SPKG.rst | 1 + build/pkgs/sagemath_bliss/bootstrap | 1 + build/pkgs/sagemath_bliss/dependencies | 1 + build/pkgs/sagemath_bliss/distros/conda.txt | 1 + .../pkgs/sagemath_bliss/install-requires.txt | 2 + build/pkgs/sagemath_bliss/package-version.txt | 1 + build/pkgs/sagemath_bliss/spkg-install | 29 + build/pkgs/sagemath_bliss/spkg-src | 24 + build/pkgs/sagemath_bliss/src | 1 + build/pkgs/sagemath_bliss/type | 1 + .../sagemath_categories/install-requires.txt | 2 +- build/pkgs/sagemath_coxeter3/SPKG.rst | 1 + build/pkgs/sagemath_coxeter3/bootstrap | 1 + build/pkgs/sagemath_coxeter3/dependencies | 1 + .../pkgs/sagemath_coxeter3/distros/conda.txt | 1 + .../sagemath_coxeter3/install-requires.txt | 2 + .../sagemath_coxeter3/package-version.txt | 1 + build/pkgs/sagemath_coxeter3/spkg-install | 29 + build/pkgs/sagemath_coxeter3/spkg-src | 24 + build/pkgs/sagemath_coxeter3/src | 1 + build/pkgs/sagemath_coxeter3/type | 1 + .../sagemath_environment/install-requires.txt | 2 +- build/pkgs/sagemath_mcqd/SPKG.rst | 1 + build/pkgs/sagemath_mcqd/bootstrap | 1 + build/pkgs/sagemath_mcqd/dependencies | 1 + build/pkgs/sagemath_mcqd/distros/conda.txt | 1 + build/pkgs/sagemath_mcqd/install-requires.txt | 2 + build/pkgs/sagemath_mcqd/package-version.txt | 1 + build/pkgs/sagemath_mcqd/spkg-install | 29 + build/pkgs/sagemath_mcqd/spkg-src | 24 + build/pkgs/sagemath_mcqd/src | 1 + build/pkgs/sagemath_mcqd/type | 1 + build/pkgs/sagemath_meataxe/SPKG.rst | 1 + build/pkgs/sagemath_meataxe/bootstrap | 1 + build/pkgs/sagemath_meataxe/dependencies | 1 + build/pkgs/sagemath_meataxe/distros/conda.txt | 1 + .../sagemath_meataxe/install-requires.txt | 2 + .../pkgs/sagemath_meataxe/package-version.txt | 1 + build/pkgs/sagemath_meataxe/spkg-install | 29 + build/pkgs/sagemath_meataxe/spkg-src | 24 + build/pkgs/sagemath_meataxe/src | 1 + build/pkgs/sagemath_meataxe/type | 1 + .../sagemath_objects/install-requires.txt | 2 +- build/pkgs/sagemath_repl/install-requires.txt | 2 +- build/pkgs/sagemath_sirocco/SPKG.rst | 1 + build/pkgs/sagemath_sirocco/bootstrap | 1 + build/pkgs/sagemath_sirocco/dependencies | 1 + build/pkgs/sagemath_sirocco/distros/conda.txt | 1 + .../sagemath_sirocco/install-requires.txt | 2 + .../pkgs/sagemath_sirocco/package-version.txt | 1 + build/pkgs/sagemath_sirocco/spkg-install | 29 + build/pkgs/sagemath_sirocco/spkg-src | 24 + build/pkgs/sagemath_sirocco/src | 1 + build/pkgs/sagemath_sirocco/type | 1 + build/pkgs/sagemath_tdlib/SPKG.rst | 1 + build/pkgs/sagemath_tdlib/bootstrap | 1 + build/pkgs/sagemath_tdlib/dependencies | 1 + build/pkgs/sagemath_tdlib/distros/conda.txt | 1 + .../pkgs/sagemath_tdlib/install-requires.txt | 2 + build/pkgs/sagemath_tdlib/package-version.txt | 1 + build/pkgs/sagemath_tdlib/spkg-install | 29 + build/pkgs/sagemath_tdlib/spkg-src | 24 + build/pkgs/sagemath_tdlib/src | 1 + build/pkgs/sagemath_tdlib/type | 1 + pkgs/sage-conf/VERSION.txt | 2 +- pkgs/sage-conf_pypi/VERSION.txt | 2 +- pkgs/sage-docbuild/VERSION.txt | 2 +- pkgs/sage-setup/VERSION.txt | 2 +- pkgs/sage-sws2rst/VERSION.txt | 2 +- pkgs/sagemath-bliss/MANIFEST.in | 17 + pkgs/sagemath-bliss/README.rst | 32 + pkgs/sagemath-bliss/VERSION.txt | 1 + pkgs/sagemath-bliss/pyproject.toml.m4 | 12 + pkgs/sagemath-bliss/requirements.txt.m4 | 2 + pkgs/sagemath-bliss/sage | 1 + pkgs/sagemath-bliss/setup.cfg.m4 | 17 + pkgs/sagemath-bliss/setup.py | 63 + pkgs/sagemath-categories/README.rst | 2 +- pkgs/sagemath-categories/VERSION.txt | 2 +- pkgs/sagemath-coxeter3/MANIFEST.in | 19 + pkgs/sagemath-coxeter3/README.rst | 31 + pkgs/sagemath-coxeter3/VERSION.txt | 1 + pkgs/sagemath-coxeter3/pyproject.toml.m4 | 11 + pkgs/sagemath-coxeter3/requirements.txt.m4 | 2 + pkgs/sagemath-coxeter3/sage | 1 + pkgs/sagemath-coxeter3/setup.cfg.m4 | 20 + pkgs/sagemath-coxeter3/setup.py | 63 + pkgs/sagemath-environment/README.rst | 2 +- pkgs/sagemath-environment/VERSION.txt | 2 +- pkgs/sagemath-environment/setup.cfg.m4 | 1 + pkgs/sagemath-mcqd/MANIFEST.in | 19 + pkgs/sagemath-mcqd/README.rst | 33 + pkgs/sagemath-mcqd/VERSION.txt | 1 + pkgs/sagemath-mcqd/pyproject.toml.m4 | 12 + pkgs/sagemath-mcqd/requirements.txt.m4 | 2 + pkgs/sagemath-mcqd/sage | 1 + pkgs/sagemath-mcqd/setup.cfg.m4 | 21 + pkgs/sagemath-mcqd/setup.py | 63 + pkgs/sagemath-meataxe/MANIFEST.in | 20 + pkgs/sagemath-meataxe/README.rst | 36 + pkgs/sagemath-meataxe/VERSION.txt | 1 + pkgs/sagemath-meataxe/pyproject.toml.m4 | 11 + pkgs/sagemath-meataxe/requirements.txt.m4 | 2 + pkgs/sagemath-meataxe/sage | 1 + pkgs/sagemath-meataxe/setup.cfg.m4 | 22 + pkgs/sagemath-meataxe/setup.py | 63 + pkgs/sagemath-objects/README.rst | 2 +- pkgs/sagemath-objects/VERSION.txt | 2 +- pkgs/sagemath-repl/README.rst | 2 +- pkgs/sagemath-repl/VERSION.txt | 2 +- pkgs/sagemath-sirocco/MANIFEST.in | 15 + pkgs/sagemath-sirocco/README.rst | 32 + pkgs/sagemath-sirocco/VERSION.txt | 1 + pkgs/sagemath-sirocco/pyproject.toml.m4 | 12 + pkgs/sagemath-sirocco/requirements.txt.m4 | 2 + pkgs/sagemath-sirocco/sage | 1 + pkgs/sagemath-sirocco/setup.cfg.m4 | 14 + pkgs/sagemath-sirocco/setup.py | 63 + pkgs/sagemath-standard/setup.py | 7 +- pkgs/sagemath-tdlib/MANIFEST.in | 17 + pkgs/sagemath-tdlib/README.rst | 32 + pkgs/sagemath-tdlib/VERSION.txt | 1 + pkgs/sagemath-tdlib/pyproject.toml.m4 | 11 + pkgs/sagemath-tdlib/requirements.txt.m4 | 2 + pkgs/sagemath-tdlib/sage | 1 + pkgs/sagemath-tdlib/setup.cfg.m4 | 12 + pkgs/sagemath-tdlib/setup.py | 63 + src/MANIFEST.in | 3 + src/VERSION.txt | 2 +- src/bin/sage-fixdoctests | 407 ++++-- src/bin/sage-runtests | 4 + src/bin/sage-update-version | 6 +- src/bin/sage-version.sh | 6 +- src/doc/de/tutorial/latex.rst | 17 +- src/doc/en/developer/coding_basics.rst | 152 ++- src/doc/en/developer/doctesting.rst | 445 ++++++- .../en/developer/packaging_sage_library.rst | 54 +- src/doc/en/reference/references/index.rst | 14 +- src/doc/fr/tutorial/latex.rst | 17 +- src/doc/ja/tutorial/latex.rst | 17 +- src/doc/pt/tutorial/latex.rst | 17 +- src/sage/algebras/fusion_rings/f_matrix.py | 6 +- src/sage/all__sagemath_bliss.py | 0 src/sage/all__sagemath_coxeter3.py | 0 src/sage/all__sagemath_mcqd.py | 0 src/sage/all__sagemath_meataxe.py | 0 src/sage/all__sagemath_sirocco.py | 0 src/sage/all__sagemath_tdlib.py | 0 src/sage/calculus/ode.pyx | 15 +- src/sage/categories/category_singleton.pyx | 9 +- ...eflection_or_generalized_coxeter_groups.py | 2 +- src/sage/categories/lie_algebras.py | 318 ++--- src/sage/categories/loop_crystals.py | 2 +- src/sage/categories/posets.py | 9 +- src/sage/coding/guruswami_sudan/gs_decoder.py | 6 +- src/sage/coding/linear_rank_metric.py | 2 +- .../cluster_algebra_quiver/mutation_type.py | 8 +- .../combinat/cluster_algebra_quiver/quiver.py | 4 +- src/sage/combinat/constellation.py | 2 +- src/sage/combinat/crystals/littelmann_path.py | 12 +- src/sage/combinat/designs/bibd.py | 120 +- src/sage/combinat/designs/block_design.py | 43 +- src/sage/combinat/designs/database.py | 321 +++-- src/sage/combinat/designs/designs_pyx.pyx | 98 +- .../combinat/designs/difference_family.py | 330 ++--- .../combinat/designs/difference_matrices.py | 9 +- .../designs/evenly_distributed_sets.pyx | 16 +- .../designs/gen_quadrangles_with_spread.pyx | 1 + .../designs/group_divisible_designs.py | 1 + .../combinat/designs/incidence_structures.py | 227 ++-- src/sage/combinat/designs/latin_squares.py | 27 +- .../combinat/designs/orthogonal_arrays.py | 13 +- .../orthogonal_arrays_build_recursive.py | 1 + .../orthogonal_arrays_find_recursive.pyx | 1 + src/sage/combinat/designs/resolvable_bibd.py | 1 + .../combinat/designs/subhypergraph_search.pyx | 4 +- src/sage/combinat/designs/twographs.py | 45 +- src/sage/combinat/diagram.py | 50 +- src/sage/combinat/finite_state_machine.py | 3 +- src/sage/combinat/k_regular_sequence.py | 485 ++++++- src/sage/combinat/partition_algebra.py | 2 +- src/sage/combinat/posets/mobile.py | 2 +- src/sage/combinat/posets/posets.py | 8 +- src/sage/combinat/recognizable_series.py | 51 +- .../combinat/root_system/ambient_space.py | 2 +- src/sage/combinat/root_system/cartan_type.py | 2 +- src/sage/combinat/tutorial.py | 131 +- src/sage/doctest/control.py | 134 +- src/sage/doctest/external.py | 7 +- src/sage/doctest/fixtures.py | 2 +- src/sage/doctest/forker.py | 90 +- src/sage/doctest/parsing.py | 670 +++++++++- src/sage/doctest/reporting.py | 14 +- src/sage/doctest/sources.py | 86 +- src/sage/doctest/test.py | 20 +- src/sage/doctest/util.py | 7 +- .../arithmetic_dynamics/projective_ds.py | 4 +- .../complex_dynamics/mandel_julia_helper.pyx | 11 +- src/sage/features/__init__.py | 25 +- src/sage/features/all.py | 91 +- src/sage/features/gap.py | 41 +- src/sage/features/kenzo.py | 5 +- src/sage/features/sagemath.py | 652 ++++++++-- src/sage/features/singular.py | 37 +- src/sage/geometry/lattice_polytope.py | 91 +- src/sage/geometry/polyhedral_complex.py | 4 +- src/sage/geometry/polyhedron/base4.py | 68 +- src/sage/geometry/polyhedron/base_QQ.py | 4 +- src/sage/graphs/all__sagemath_bliss.py | 0 src/sage/graphs/all__sagemath_mcqd.py | 0 src/sage/graphs/all__sagemath_tdlib.py | 0 src/sage/graphs/base/c_graph.pyx | 2 +- src/sage/graphs/base/static_sparse_graph.pyx | 2 +- src/sage/graphs/bipartite_graph.py | 2 +- .../bliss_cpp/bliss_find_automorphisms.h | 2 + src/sage/graphs/comparability.pyx | 4 +- src/sage/graphs/connectivity.pyx | 154 ++- src/sage/graphs/distances_all_pairs.pyx | 2 +- src/sage/graphs/generators/world_map.py | 6 +- src/sage/graphs/generic_graph.py | 1117 +++++++++-------- src/sage/graphs/generic_graph_pyx.pyx | 189 ++- src/sage/graphs/graph.py | 8 +- src/sage/graphs/graph_coloring.pyx | 4 +- .../all__sagemath_tdlib.py | 0 .../clique_separators.pyx | 2 +- .../graph_decompositions/graph_products.pyx | 4 +- .../modular_decomposition.py | 6 +- .../tree_decomposition.pyx | 2 +- src/sage/graphs/mcqd.pxd | 3 +- src/sage/graphs/partial_cube.py | 2 +- src/sage/graphs/spanning_tree.pyx | 4 +- src/sage/graphs/traversals.pyx | 2 +- src/sage/graphs/tutte_polynomial.py | 2 +- .../perm_gps/partn_ref/data_structures.pxd | 4 +- .../perm_gps/partn_ref/data_structures.pyx | 20 +- .../partn_ref2/refinement_generic.pxd | 5 + .../partn_ref2/refinement_generic.pyx | 42 +- src/sage/interfaces/gnuplot.py | 2 +- src/sage/interfaces/r.py | 274 ++-- src/sage/knots/knotinfo.py | 9 +- src/sage/libs/all__sagemath_coxeter3.py | 0 src/sage/libs/all__sagemath_meataxe.py | 0 src/sage/libs/all__sagemath_sirocco.py | 0 .../libs/coxeter3/all__sagemath_coxeter3.py | 0 src/sage/libs/coxeter3/coxeter.pxd | 2 + src/sage/libs/coxeter3/coxeter.pyx | 1 - src/sage/libs/coxeter3/coxeter_group.py | 3 +- src/sage/libs/coxeter3/decl.pxd | 2 + src/sage/libs/meataxe.pxd | 2 + src/sage/matrix/all__sagemath_meataxe.py | 0 src/sage/matrix/matrix_gfpn_dense.pxd | 2 + src/sage/matroids/linear_matroid.pyx | 2 +- src/sage/matroids/matroid.pyx | 4 +- src/sage/matroids/utilities.py | 14 +- src/sage/misc/latex_standalone.py | 229 ++-- src/sage/misc/profiler.py | 11 +- src/sage/misc/sagedoc.py | 34 + src/sage/quivers/algebra_elements.pyx | 11 +- src/sage/repl/user_globals.py | 1 + src/sage/rings/big_oh.py | 33 +- src/sage/rings/cfinite_sequence.py | 9 +- src/sage/rings/function_field/constructor.py | 36 +- src/sage/rings/function_field/derivations.py | 8 +- .../function_field/derivations_polymod.py | 204 +-- src/sage/rings/function_field/differential.py | 403 +++--- .../drinfeld_modules/drinfeld_module.py | 531 +++++++- src/sage/rings/function_field/element.pyx | 256 ++-- .../rings/function_field/element_polymod.pyx | 39 +- .../rings/function_field/element_rational.pyx | 61 +- src/sage/rings/function_field/extensions.py | 96 +- .../rings/function_field/function_field.py | 510 ++++---- .../function_field/function_field_polymod.py | 466 +++---- .../function_field/function_field_rational.py | 194 +-- src/sage/rings/function_field/ideal.py | 698 +++++----- .../rings/function_field/ideal_polymod.py | 997 ++++++++------- .../rings/function_field/ideal_rational.py | 174 +-- src/sage/rings/function_field/maps.py | 331 ++--- src/sage/rings/function_field/order.py | 52 +- src/sage/rings/function_field/order_basis.py | 198 +-- .../rings/function_field/order_polymod.py | 416 +++--- .../rings/function_field/order_rational.py | 142 ++- src/sage/rings/function_field/place.py | 197 +-- .../rings/function_field/place_polymod.py | 185 +-- .../rings/function_field/place_rational.py | 4 +- src/sage/rings/function_field/valuation.py | 316 ++--- src/sage/rings/homset.py | 57 +- src/sage/rings/polynomial/hilbert.pyx | 8 - .../polynomial/multi_polynomial_ideal.py | 927 +++++++------- src/sage/rings/polynomial/polynomial_ring.py | 594 ++++----- .../cyclic_covers/cycliccover_finite_field.py | 2 +- src/sage/schemes/elliptic_curves/heegner.py | 28 +- .../schemes/elliptic_curves/hom_frobenius.py | 12 +- .../schemes/projective/proj_bdd_height.py | 6 +- .../lp_doctest.py | 2 +- src/sage/topology/simplicial_complex.py | 2 +- .../topology/simplicial_set_constructions.py | 2 +- src/sage/version.py | 6 +- 311 files changed, 11191 insertions(+), 6167 deletions(-) create mode 100644 build/pkgs/bliss/distros/gentoo.txt create mode 120000 build/pkgs/sagemath_bliss/SPKG.rst create mode 120000 build/pkgs/sagemath_bliss/bootstrap create mode 100644 build/pkgs/sagemath_bliss/dependencies create mode 100644 build/pkgs/sagemath_bliss/distros/conda.txt create mode 100644 build/pkgs/sagemath_bliss/install-requires.txt create mode 120000 build/pkgs/sagemath_bliss/package-version.txt create mode 100755 build/pkgs/sagemath_bliss/spkg-install create mode 100755 build/pkgs/sagemath_bliss/spkg-src create mode 120000 build/pkgs/sagemath_bliss/src create mode 100644 build/pkgs/sagemath_bliss/type create mode 120000 build/pkgs/sagemath_coxeter3/SPKG.rst create mode 120000 build/pkgs/sagemath_coxeter3/bootstrap create mode 100644 build/pkgs/sagemath_coxeter3/dependencies create mode 100644 build/pkgs/sagemath_coxeter3/distros/conda.txt create mode 100644 build/pkgs/sagemath_coxeter3/install-requires.txt create mode 120000 build/pkgs/sagemath_coxeter3/package-version.txt create mode 100755 build/pkgs/sagemath_coxeter3/spkg-install create mode 100755 build/pkgs/sagemath_coxeter3/spkg-src create mode 120000 build/pkgs/sagemath_coxeter3/src create mode 100644 build/pkgs/sagemath_coxeter3/type create mode 120000 build/pkgs/sagemath_mcqd/SPKG.rst create mode 120000 build/pkgs/sagemath_mcqd/bootstrap create mode 100644 build/pkgs/sagemath_mcqd/dependencies create mode 100644 build/pkgs/sagemath_mcqd/distros/conda.txt create mode 100644 build/pkgs/sagemath_mcqd/install-requires.txt create mode 120000 build/pkgs/sagemath_mcqd/package-version.txt create mode 100755 build/pkgs/sagemath_mcqd/spkg-install create mode 100755 build/pkgs/sagemath_mcqd/spkg-src create mode 120000 build/pkgs/sagemath_mcqd/src create mode 100644 build/pkgs/sagemath_mcqd/type create mode 120000 build/pkgs/sagemath_meataxe/SPKG.rst create mode 120000 build/pkgs/sagemath_meataxe/bootstrap create mode 100644 build/pkgs/sagemath_meataxe/dependencies create mode 100644 build/pkgs/sagemath_meataxe/distros/conda.txt create mode 100644 build/pkgs/sagemath_meataxe/install-requires.txt create mode 120000 build/pkgs/sagemath_meataxe/package-version.txt create mode 100755 build/pkgs/sagemath_meataxe/spkg-install create mode 100755 build/pkgs/sagemath_meataxe/spkg-src create mode 120000 build/pkgs/sagemath_meataxe/src create mode 100644 build/pkgs/sagemath_meataxe/type create mode 120000 build/pkgs/sagemath_sirocco/SPKG.rst create mode 120000 build/pkgs/sagemath_sirocco/bootstrap create mode 100644 build/pkgs/sagemath_sirocco/dependencies create mode 100644 build/pkgs/sagemath_sirocco/distros/conda.txt create mode 100644 build/pkgs/sagemath_sirocco/install-requires.txt create mode 120000 build/pkgs/sagemath_sirocco/package-version.txt create mode 100755 build/pkgs/sagemath_sirocco/spkg-install create mode 100755 build/pkgs/sagemath_sirocco/spkg-src create mode 120000 build/pkgs/sagemath_sirocco/src create mode 100644 build/pkgs/sagemath_sirocco/type create mode 120000 build/pkgs/sagemath_tdlib/SPKG.rst create mode 120000 build/pkgs/sagemath_tdlib/bootstrap create mode 100644 build/pkgs/sagemath_tdlib/dependencies create mode 100644 build/pkgs/sagemath_tdlib/distros/conda.txt create mode 100644 build/pkgs/sagemath_tdlib/install-requires.txt create mode 120000 build/pkgs/sagemath_tdlib/package-version.txt create mode 100755 build/pkgs/sagemath_tdlib/spkg-install create mode 100755 build/pkgs/sagemath_tdlib/spkg-src create mode 120000 build/pkgs/sagemath_tdlib/src create mode 100644 build/pkgs/sagemath_tdlib/type create mode 100644 pkgs/sagemath-bliss/MANIFEST.in create mode 100644 pkgs/sagemath-bliss/README.rst create mode 100644 pkgs/sagemath-bliss/VERSION.txt create mode 100644 pkgs/sagemath-bliss/pyproject.toml.m4 create mode 100644 pkgs/sagemath-bliss/requirements.txt.m4 create mode 120000 pkgs/sagemath-bliss/sage create mode 100644 pkgs/sagemath-bliss/setup.cfg.m4 create mode 100644 pkgs/sagemath-bliss/setup.py create mode 100644 pkgs/sagemath-coxeter3/MANIFEST.in create mode 100644 pkgs/sagemath-coxeter3/README.rst create mode 100644 pkgs/sagemath-coxeter3/VERSION.txt create mode 100644 pkgs/sagemath-coxeter3/pyproject.toml.m4 create mode 100644 pkgs/sagemath-coxeter3/requirements.txt.m4 create mode 120000 pkgs/sagemath-coxeter3/sage create mode 100644 pkgs/sagemath-coxeter3/setup.cfg.m4 create mode 100644 pkgs/sagemath-coxeter3/setup.py create mode 100644 pkgs/sagemath-mcqd/MANIFEST.in create mode 100644 pkgs/sagemath-mcqd/README.rst create mode 100644 pkgs/sagemath-mcqd/VERSION.txt create mode 100644 pkgs/sagemath-mcqd/pyproject.toml.m4 create mode 100644 pkgs/sagemath-mcqd/requirements.txt.m4 create mode 120000 pkgs/sagemath-mcqd/sage create mode 100644 pkgs/sagemath-mcqd/setup.cfg.m4 create mode 100644 pkgs/sagemath-mcqd/setup.py create mode 100644 pkgs/sagemath-meataxe/MANIFEST.in create mode 100644 pkgs/sagemath-meataxe/README.rst create mode 100644 pkgs/sagemath-meataxe/VERSION.txt create mode 100644 pkgs/sagemath-meataxe/pyproject.toml.m4 create mode 100644 pkgs/sagemath-meataxe/requirements.txt.m4 create mode 120000 pkgs/sagemath-meataxe/sage create mode 100644 pkgs/sagemath-meataxe/setup.cfg.m4 create mode 100644 pkgs/sagemath-meataxe/setup.py create mode 100644 pkgs/sagemath-sirocco/MANIFEST.in create mode 100644 pkgs/sagemath-sirocco/README.rst create mode 100644 pkgs/sagemath-sirocco/VERSION.txt create mode 100644 pkgs/sagemath-sirocco/pyproject.toml.m4 create mode 100644 pkgs/sagemath-sirocco/requirements.txt.m4 create mode 120000 pkgs/sagemath-sirocco/sage create mode 100644 pkgs/sagemath-sirocco/setup.cfg.m4 create mode 100644 pkgs/sagemath-sirocco/setup.py create mode 100644 pkgs/sagemath-tdlib/MANIFEST.in create mode 100644 pkgs/sagemath-tdlib/README.rst create mode 100644 pkgs/sagemath-tdlib/VERSION.txt create mode 100644 pkgs/sagemath-tdlib/pyproject.toml.m4 create mode 100644 pkgs/sagemath-tdlib/requirements.txt.m4 create mode 120000 pkgs/sagemath-tdlib/sage create mode 100644 pkgs/sagemath-tdlib/setup.cfg.m4 create mode 100644 pkgs/sagemath-tdlib/setup.py create mode 100644 src/sage/all__sagemath_bliss.py create mode 100644 src/sage/all__sagemath_coxeter3.py create mode 100644 src/sage/all__sagemath_mcqd.py create mode 100644 src/sage/all__sagemath_meataxe.py create mode 100644 src/sage/all__sagemath_sirocco.py create mode 100644 src/sage/all__sagemath_tdlib.py create mode 100644 src/sage/graphs/all__sagemath_bliss.py create mode 100644 src/sage/graphs/all__sagemath_mcqd.py create mode 100644 src/sage/graphs/all__sagemath_tdlib.py create mode 100644 src/sage/graphs/graph_decompositions/all__sagemath_tdlib.py create mode 100644 src/sage/libs/all__sagemath_coxeter3.py create mode 100644 src/sage/libs/all__sagemath_meataxe.py create mode 100644 src/sage/libs/all__sagemath_sirocco.py create mode 100644 src/sage/libs/coxeter3/all__sagemath_coxeter3.py create mode 100644 src/sage/matrix/all__sagemath_meataxe.py diff --git a/.gitignore b/.gitignore index 0c1c9086cf7..efdbfa8f616 100644 --- a/.gitignore +++ b/.gitignore @@ -176,14 +176,32 @@ build/bin/sage-build-env-config /pkgs/*/.tox /pkgs/sagemath-objects/setup.cfg +/pkgs/sagemath-bliss/setup.cfg +/pkgs/sagemath-coxeter3/setup.cfg +/pkgs/sagemath-mcqd/setup.cfg +/pkgs/sagemath-meataxe/setup.cfg +/pkgs/sagemath-sirocco/setup.cfg +/pkgs/sagemath-tdlib/setup.cfg /pkgs/sagemath-categories/setup.cfg /pkgs/sagemath-environment/setup.cfg /pkgs/sagemath-repl/setup.cfg /pkgs/sagemath-objects/pyproject.toml +/pkgs/sagemath-bliss/pyproject.toml +/pkgs/sagemath-coxeter3/pyproject.toml +/pkgs/sagemath-mcqd/pyproject.toml +/pkgs/sagemath-meataxe/pyproject.toml +/pkgs/sagemath-sirocco/pyproject.toml +/pkgs/sagemath-tdlib/pyproject.toml /pkgs/sagemath-categories/pyproject.toml /pkgs/sagemath-environment/pyproject.toml /pkgs/sagemath-repl/pyproject.toml /pkgs/sagemath-objects/requirements.txt +/pkgs/sagemath-bliss/requirements.txt +/pkgs/sagemath-coxeter3/requirements.txt +/pkgs/sagemath-mcqd/requirements.txt +/pkgs/sagemath-meataxe/requirements.txt +/pkgs/sagemath-sirocco/requirements.txt +/pkgs/sagemath-tdlib/requirements.txt /pkgs/sagemath-categories/requirements.txt /pkgs/sagemath-environment/requirements.txt /pkgs/sagemath-repl/requirements.txt diff --git a/CITATION.cff b/CITATION.cff index 79c0fb602cf..6f4f46a6d8a 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -4,8 +4,8 @@ title: SageMath abstract: SageMath is a free open-source mathematics software system. authors: - name: "The SageMath Developers" -version: 10.1.beta7 +version: 10.1.beta8 doi: 10.5281/zenodo.593563 -date-released: 2023-07-20 +date-released: 2023-07-30 repository-code: "https://github.com/sagemath/sage" url: "https://www.sagemath.org/" diff --git a/README.md b/README.md index f086349a015..9982460cd78 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ > "Creating a Viable Open Source Alternative to > Magma, Maple, Mathematica, and MATLAB" -> Copyright (C) 2005-2022 The Sage Development Team +> Copyright (C) 2005-2023 The Sage Development Team https://www.sagemath.org diff --git a/VERSION.txt b/VERSION.txt index 5c4804748f8..d9562f0204a 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -SageMath version 10.1.beta7, Release Date: 2023-07-20 +SageMath version 10.1.beta8, Release Date: 2023-07-30 diff --git a/build/make/Makefile.in b/build/make/Makefile.in index 370d436bb2c..b6858173362 100644 --- a/build/make/Makefile.in +++ b/build/make/Makefile.in @@ -135,7 +135,13 @@ PYPI_WHEEL_PACKAGES = \ sagemath_environment \ sagemath_objects \ sagemath_repl \ - sagemath_categories + sagemath_categories \ + sagemath_bliss \ + sagemath_mcqd \ + sagemath_tdlib \ + sagemath_coxeter3 \ + sagemath_sirocco \ + sagemath_meataxe # sage_docbuild is here, not in PYPI_WHEEL_PACKAGES, because it depends on sagelib WHEEL_PACKAGES = $(PYPI_WHEEL_PACKAGES) \ diff --git a/build/pkgs/bliss/distros/gentoo.txt b/build/pkgs/bliss/distros/gentoo.txt new file mode 100644 index 00000000000..73add6de49d --- /dev/null +++ b/build/pkgs/bliss/distros/gentoo.txt @@ -0,0 +1 @@ +sci-libs/bliss diff --git a/build/pkgs/configure/checksums.ini b/build/pkgs/configure/checksums.ini index 5b8d24c0c9d..e489dd26d2e 100644 --- a/build/pkgs/configure/checksums.ini +++ b/build/pkgs/configure/checksums.ini @@ -1,4 +1,4 @@ tarball=configure-VERSION.tar.gz -sha1=017780512407f8da87a091c485cf8c7162b5adaf -md5=4e3e25464ee850c2593f16364bd6b206 -cksum=531215747 +sha1=5930a5bb8c0164176122d6ffb1143df61b990213 +md5=b70ab1eaa247dce59a59910ec8e89051 +cksum=3869087595 diff --git a/build/pkgs/configure/package-version.txt b/build/pkgs/configure/package-version.txt index 23da78a991e..33340576bb6 100644 --- a/build/pkgs/configure/package-version.txt +++ b/build/pkgs/configure/package-version.txt @@ -1 +1 @@ -98520945e069c603400cf2ae902a843f7a1bda13 +79dc3510abb05344ac7ef23d42d1228f186b7493 diff --git a/build/pkgs/sage_conf/install-requires.txt b/build/pkgs/sage_conf/install-requires.txt index a3aad3a1bd8..308956090b6 100644 --- a/build/pkgs/sage_conf/install-requires.txt +++ b/build/pkgs/sage_conf/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-conf ~= 10.1b7 +sage-conf ~= 10.1b8 diff --git a/build/pkgs/sage_docbuild/install-requires.txt b/build/pkgs/sage_docbuild/install-requires.txt index 17837780a33..f2408da3bef 100644 --- a/build/pkgs/sage_docbuild/install-requires.txt +++ b/build/pkgs/sage_docbuild/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-docbuild ~= 10.1b7 +sage-docbuild ~= 10.1b8 diff --git a/build/pkgs/sage_setup/install-requires.txt b/build/pkgs/sage_setup/install-requires.txt index 1e1663ab1bf..3fb4f9bd897 100644 --- a/build/pkgs/sage_setup/install-requires.txt +++ b/build/pkgs/sage_setup/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-setup ~= 10.1b7 +sage-setup ~= 10.1b8 diff --git a/build/pkgs/sage_sws2rst/install-requires.txt b/build/pkgs/sage_sws2rst/install-requires.txt index b90c1d38b9f..5705cd5cef9 100644 --- a/build/pkgs/sage_sws2rst/install-requires.txt +++ b/build/pkgs/sage_sws2rst/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sage-sws2rst ~= 10.1b7 +sage-sws2rst ~= 10.1b8 diff --git a/build/pkgs/sagelib/install-requires.txt b/build/pkgs/sagelib/install-requires.txt index 6e35e566194..09ddf8e9f0e 100644 --- a/build/pkgs/sagelib/install-requires.txt +++ b/build/pkgs/sagelib/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagelib ~= 10.1b7 +sagemath-standard ~= 10.1b8 diff --git a/build/pkgs/sagelib/spkg-install b/build/pkgs/sagelib/spkg-install index ed6bb969f31..730829b3101 100755 --- a/build/pkgs/sagelib/spkg-install +++ b/build/pkgs/sagelib/spkg-install @@ -55,6 +55,8 @@ unset SAGE_PKG_CONFIG_PATH SITEPACKAGESDIR=$(python3 -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])') +export SAGE_OPTIONAL_PACKAGES_WITH_EXTENSIONS="" + if [ "$SAGE_EDITABLE" = yes ]; then # In an incremental build, we may need to uninstall old versions installed by distutils # under the old distribution name "sage" (before #30912, which switched to setuptools @@ -79,6 +81,7 @@ else # Compiling sage/interfaces/sagespawn.pyx because it depends on /private/var/folders/38/wnh4gf1552g_crsjnv2vmmww0000gp/T/pip-build-env-609n5985/overlay/lib/python3.10/site-packages/Cython/Includes/posix/unistd.pxd time sdh_pip_install --no-build-isolation . else + SAGE_OPTIONAL_PACKAGES_WITH_EXTENSIONS+="mcqd,tdlib,coxeter3,sirocco,meataxe,bliss" time python3 -u setup.py --no-user-cfg build install || exit 1 fi fi diff --git a/build/pkgs/sagemath_bliss/SPKG.rst b/build/pkgs/sagemath_bliss/SPKG.rst new file mode 120000 index 00000000000..b4545b4bda6 --- /dev/null +++ b/build/pkgs/sagemath_bliss/SPKG.rst @@ -0,0 +1 @@ +src/README.rst \ No newline at end of file diff --git a/build/pkgs/sagemath_bliss/bootstrap b/build/pkgs/sagemath_bliss/bootstrap new file mode 120000 index 00000000000..40542346a4e --- /dev/null +++ b/build/pkgs/sagemath_bliss/bootstrap @@ -0,0 +1 @@ +../sagelib/bootstrap \ No newline at end of file diff --git a/build/pkgs/sagemath_bliss/dependencies b/build/pkgs/sagemath_bliss/dependencies new file mode 100644 index 00000000000..279e79ace91 --- /dev/null +++ b/build/pkgs/sagemath_bliss/dependencies @@ -0,0 +1 @@ +$(PYTHON) bliss cysignals | $(PYTHON_TOOLCHAIN) sage_setup sage_conf sagemath_environment cython pkgconfig diff --git a/build/pkgs/sagemath_bliss/distros/conda.txt b/build/pkgs/sagemath_bliss/distros/conda.txt new file mode 100644 index 00000000000..d6139d966ec --- /dev/null +++ b/build/pkgs/sagemath_bliss/distros/conda.txt @@ -0,0 +1 @@ +sagemath-bliss diff --git a/build/pkgs/sagemath_bliss/install-requires.txt b/build/pkgs/sagemath_bliss/install-requires.txt new file mode 100644 index 00000000000..175856b245f --- /dev/null +++ b/build/pkgs/sagemath_bliss/install-requires.txt @@ -0,0 +1,2 @@ +# This file is updated on every release by the sage-update-version script +sagemath-bliss ~= 10.1b8 diff --git a/build/pkgs/sagemath_bliss/package-version.txt b/build/pkgs/sagemath_bliss/package-version.txt new file mode 120000 index 00000000000..c4540217bba --- /dev/null +++ b/build/pkgs/sagemath_bliss/package-version.txt @@ -0,0 +1 @@ +src/VERSION.txt \ No newline at end of file diff --git a/build/pkgs/sagemath_bliss/spkg-install b/build/pkgs/sagemath_bliss/spkg-install new file mode 100755 index 00000000000..7ce202f09ae --- /dev/null +++ b/build/pkgs/sagemath_bliss/spkg-install @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# From sage-spkg. +# For type=script packages, the build rule in build/make/Makefile sources +# sage-env but not sage-dist-helpers. +lib="$SAGE_ROOT/build/bin/sage-dist-helpers" +source "$lib" +if [ $? -ne 0 ]; then + echo >&2 "Error: failed to source $lib" + echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" + exit 1 +fi +cd src + +export PIP_NO_INDEX=true +export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS" + +if [ "$SAGE_EDITABLE" = yes ]; then + # SAGE_ROOT/src/setup.py installs everything, nothing to do... + if [ "$SAGE_WHEELS" = yes ]; then + # ... except we build the wheel if requested + sdh_setup_bdist_wheel && sdh_store_wheel . + fi +else + if [ "$SAGE_WHEELS" = yes ]; then + # Modularized install via wheels + sdh_pip_install . + # else nothing to do in legacy direct installation. + fi +fi diff --git a/build/pkgs/sagemath_bliss/spkg-src b/build/pkgs/sagemath_bliss/spkg-src new file mode 100755 index 00000000000..483b2f349e3 --- /dev/null +++ b/build/pkgs/sagemath_bliss/spkg-src @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# Script to prepare an sdist tarball for sagemath-bliss +# This script is not used during build. +# +# HOW TO MAKE THE TARBALL: +# ./sage --sh build/pkgs/sagemath_bliss/spkg-src + +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +# Exit on failure +set -e + +cd build/pkgs/sagemath_bliss + +cd src +# Get rid of old *.egg-info/SOURCES.txt +rm -Rf *.egg-info + +python3 -m build --sdist --no-isolation --skip-dependency-check --outdir "$SAGE_DISTFILES" diff --git a/build/pkgs/sagemath_bliss/src b/build/pkgs/sagemath_bliss/src new file mode 120000 index 00000000000..51c70b82b95 --- /dev/null +++ b/build/pkgs/sagemath_bliss/src @@ -0,0 +1 @@ +../../../pkgs/sagemath-bliss \ No newline at end of file diff --git a/build/pkgs/sagemath_bliss/type b/build/pkgs/sagemath_bliss/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/sagemath_bliss/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/sagemath_categories/install-requires.txt b/build/pkgs/sagemath_categories/install-requires.txt index 73e94ac27f3..2bc854cfebc 100644 --- a/build/pkgs/sagemath_categories/install-requires.txt +++ b/build/pkgs/sagemath_categories/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-categories ~= 10.1b7 +sagemath-categories ~= 10.1b8 diff --git a/build/pkgs/sagemath_coxeter3/SPKG.rst b/build/pkgs/sagemath_coxeter3/SPKG.rst new file mode 120000 index 00000000000..b4545b4bda6 --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/SPKG.rst @@ -0,0 +1 @@ +src/README.rst \ No newline at end of file diff --git a/build/pkgs/sagemath_coxeter3/bootstrap b/build/pkgs/sagemath_coxeter3/bootstrap new file mode 120000 index 00000000000..40542346a4e --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/bootstrap @@ -0,0 +1 @@ +../sagelib/bootstrap \ No newline at end of file diff --git a/build/pkgs/sagemath_coxeter3/dependencies b/build/pkgs/sagemath_coxeter3/dependencies new file mode 100644 index 00000000000..615392ca427 --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/dependencies @@ -0,0 +1 @@ +$(PYTHON) coxeter3 | $(PYTHON_TOOLCHAIN) sage_setup sagemath_environment cython pkgconfig diff --git a/build/pkgs/sagemath_coxeter3/distros/conda.txt b/build/pkgs/sagemath_coxeter3/distros/conda.txt new file mode 100644 index 00000000000..3ffe2eb8bfc --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/distros/conda.txt @@ -0,0 +1 @@ +sagemath-coxeter3 diff --git a/build/pkgs/sagemath_coxeter3/install-requires.txt b/build/pkgs/sagemath_coxeter3/install-requires.txt new file mode 100644 index 00000000000..7576be637d3 --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/install-requires.txt @@ -0,0 +1,2 @@ +# This file is updated on every release by the sage-update-version script +sagemath-coxeter3 ~= 10.1b8 diff --git a/build/pkgs/sagemath_coxeter3/package-version.txt b/build/pkgs/sagemath_coxeter3/package-version.txt new file mode 120000 index 00000000000..c4540217bba --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/package-version.txt @@ -0,0 +1 @@ +src/VERSION.txt \ No newline at end of file diff --git a/build/pkgs/sagemath_coxeter3/spkg-install b/build/pkgs/sagemath_coxeter3/spkg-install new file mode 100755 index 00000000000..7ce202f09ae --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/spkg-install @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# From sage-spkg. +# For type=script packages, the build rule in build/make/Makefile sources +# sage-env but not sage-dist-helpers. +lib="$SAGE_ROOT/build/bin/sage-dist-helpers" +source "$lib" +if [ $? -ne 0 ]; then + echo >&2 "Error: failed to source $lib" + echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" + exit 1 +fi +cd src + +export PIP_NO_INDEX=true +export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS" + +if [ "$SAGE_EDITABLE" = yes ]; then + # SAGE_ROOT/src/setup.py installs everything, nothing to do... + if [ "$SAGE_WHEELS" = yes ]; then + # ... except we build the wheel if requested + sdh_setup_bdist_wheel && sdh_store_wheel . + fi +else + if [ "$SAGE_WHEELS" = yes ]; then + # Modularized install via wheels + sdh_pip_install . + # else nothing to do in legacy direct installation. + fi +fi diff --git a/build/pkgs/sagemath_coxeter3/spkg-src b/build/pkgs/sagemath_coxeter3/spkg-src new file mode 100755 index 00000000000..df635f450cf --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/spkg-src @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# Script to prepare an sdist tarball for sagemath-coxeter3 +# This script is not used during build. +# +# HOW TO MAKE THE TARBALL: +# ./sage --sh build/pkgs/sagemath_coxeter3/spkg-src + +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +# Exit on failure +set -e + +cd build/pkgs/sagemath_coxeter3 + +cd src +# Get rid of old *.egg-info/SOURCES.txt +rm -Rf *.egg-info + +python3 -m build --sdist --no-isolation --skip-dependency-check --outdir "$SAGE_DISTFILES" diff --git a/build/pkgs/sagemath_coxeter3/src b/build/pkgs/sagemath_coxeter3/src new file mode 120000 index 00000000000..a9a1c8ae443 --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/src @@ -0,0 +1 @@ +../../../pkgs/sagemath-coxeter3 \ No newline at end of file diff --git a/build/pkgs/sagemath_coxeter3/type b/build/pkgs/sagemath_coxeter3/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/sagemath_coxeter3/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/sagemath_environment/install-requires.txt b/build/pkgs/sagemath_environment/install-requires.txt index 8062cdfb73d..ac5083c4105 100644 --- a/build/pkgs/sagemath_environment/install-requires.txt +++ b/build/pkgs/sagemath_environment/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-environment ~= 10.1b7 +sagemath-environment ~= 10.1b8 diff --git a/build/pkgs/sagemath_mcqd/SPKG.rst b/build/pkgs/sagemath_mcqd/SPKG.rst new file mode 120000 index 00000000000..b4545b4bda6 --- /dev/null +++ b/build/pkgs/sagemath_mcqd/SPKG.rst @@ -0,0 +1 @@ +src/README.rst \ No newline at end of file diff --git a/build/pkgs/sagemath_mcqd/bootstrap b/build/pkgs/sagemath_mcqd/bootstrap new file mode 120000 index 00000000000..40542346a4e --- /dev/null +++ b/build/pkgs/sagemath_mcqd/bootstrap @@ -0,0 +1 @@ +../sagelib/bootstrap \ No newline at end of file diff --git a/build/pkgs/sagemath_mcqd/dependencies b/build/pkgs/sagemath_mcqd/dependencies new file mode 100644 index 00000000000..e383df7dafa --- /dev/null +++ b/build/pkgs/sagemath_mcqd/dependencies @@ -0,0 +1 @@ +$(PYTHON) mcqd memory_allocator cysignals | $(PYTHON_TOOLCHAIN) sage_setup cython pkgconfig diff --git a/build/pkgs/sagemath_mcqd/distros/conda.txt b/build/pkgs/sagemath_mcqd/distros/conda.txt new file mode 100644 index 00000000000..9504f7f4c76 --- /dev/null +++ b/build/pkgs/sagemath_mcqd/distros/conda.txt @@ -0,0 +1 @@ +sagemath-mcqd diff --git a/build/pkgs/sagemath_mcqd/install-requires.txt b/build/pkgs/sagemath_mcqd/install-requires.txt new file mode 100644 index 00000000000..211e612c18e --- /dev/null +++ b/build/pkgs/sagemath_mcqd/install-requires.txt @@ -0,0 +1,2 @@ +# This file is updated on every release by the sage-update-version script +sagemath-mcqd ~= 10.1b8 diff --git a/build/pkgs/sagemath_mcqd/package-version.txt b/build/pkgs/sagemath_mcqd/package-version.txt new file mode 120000 index 00000000000..c4540217bba --- /dev/null +++ b/build/pkgs/sagemath_mcqd/package-version.txt @@ -0,0 +1 @@ +src/VERSION.txt \ No newline at end of file diff --git a/build/pkgs/sagemath_mcqd/spkg-install b/build/pkgs/sagemath_mcqd/spkg-install new file mode 100755 index 00000000000..7ce202f09ae --- /dev/null +++ b/build/pkgs/sagemath_mcqd/spkg-install @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# From sage-spkg. +# For type=script packages, the build rule in build/make/Makefile sources +# sage-env but not sage-dist-helpers. +lib="$SAGE_ROOT/build/bin/sage-dist-helpers" +source "$lib" +if [ $? -ne 0 ]; then + echo >&2 "Error: failed to source $lib" + echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" + exit 1 +fi +cd src + +export PIP_NO_INDEX=true +export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS" + +if [ "$SAGE_EDITABLE" = yes ]; then + # SAGE_ROOT/src/setup.py installs everything, nothing to do... + if [ "$SAGE_WHEELS" = yes ]; then + # ... except we build the wheel if requested + sdh_setup_bdist_wheel && sdh_store_wheel . + fi +else + if [ "$SAGE_WHEELS" = yes ]; then + # Modularized install via wheels + sdh_pip_install . + # else nothing to do in legacy direct installation. + fi +fi diff --git a/build/pkgs/sagemath_mcqd/spkg-src b/build/pkgs/sagemath_mcqd/spkg-src new file mode 100755 index 00000000000..c1602f0953e --- /dev/null +++ b/build/pkgs/sagemath_mcqd/spkg-src @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# Script to prepare an sdist tarball for sagemath-mcqd +# This script is not used during build. +# +# HOW TO MAKE THE TARBALL: +# ./sage --sh build/pkgs/sagemath_mcqd/spkg-src + +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +# Exit on failure +set -e + +cd build/pkgs/sagemath_mcqd + +cd src +# Get rid of old *.egg-info/SOURCES.txt +rm -Rf *.egg-info + +python3 -m build --sdist --no-isolation --skip-dependency-check --outdir "$SAGE_DISTFILES" diff --git a/build/pkgs/sagemath_mcqd/src b/build/pkgs/sagemath_mcqd/src new file mode 120000 index 00000000000..03d68b8cbe3 --- /dev/null +++ b/build/pkgs/sagemath_mcqd/src @@ -0,0 +1 @@ +../../../pkgs/sagemath-mcqd \ No newline at end of file diff --git a/build/pkgs/sagemath_mcqd/type b/build/pkgs/sagemath_mcqd/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/sagemath_mcqd/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/sagemath_meataxe/SPKG.rst b/build/pkgs/sagemath_meataxe/SPKG.rst new file mode 120000 index 00000000000..b4545b4bda6 --- /dev/null +++ b/build/pkgs/sagemath_meataxe/SPKG.rst @@ -0,0 +1 @@ +src/README.rst \ No newline at end of file diff --git a/build/pkgs/sagemath_meataxe/bootstrap b/build/pkgs/sagemath_meataxe/bootstrap new file mode 120000 index 00000000000..40542346a4e --- /dev/null +++ b/build/pkgs/sagemath_meataxe/bootstrap @@ -0,0 +1 @@ +../sagelib/bootstrap \ No newline at end of file diff --git a/build/pkgs/sagemath_meataxe/dependencies b/build/pkgs/sagemath_meataxe/dependencies new file mode 100644 index 00000000000..f100932802a --- /dev/null +++ b/build/pkgs/sagemath_meataxe/dependencies @@ -0,0 +1 @@ +$(PYTHON) meataxe | $(PYTHON_TOOLCHAIN) sage_setup sagemath_environment cython pkgconfig diff --git a/build/pkgs/sagemath_meataxe/distros/conda.txt b/build/pkgs/sagemath_meataxe/distros/conda.txt new file mode 100644 index 00000000000..b3abc7692fe --- /dev/null +++ b/build/pkgs/sagemath_meataxe/distros/conda.txt @@ -0,0 +1 @@ +sagemath-meataxe diff --git a/build/pkgs/sagemath_meataxe/install-requires.txt b/build/pkgs/sagemath_meataxe/install-requires.txt new file mode 100644 index 00000000000..1ca467ef1b7 --- /dev/null +++ b/build/pkgs/sagemath_meataxe/install-requires.txt @@ -0,0 +1,2 @@ +# This file is updated on every release by the sage-update-version script +sagemath-meataxe ~= 10.1b8 diff --git a/build/pkgs/sagemath_meataxe/package-version.txt b/build/pkgs/sagemath_meataxe/package-version.txt new file mode 120000 index 00000000000..c4540217bba --- /dev/null +++ b/build/pkgs/sagemath_meataxe/package-version.txt @@ -0,0 +1 @@ +src/VERSION.txt \ No newline at end of file diff --git a/build/pkgs/sagemath_meataxe/spkg-install b/build/pkgs/sagemath_meataxe/spkg-install new file mode 100755 index 00000000000..7ce202f09ae --- /dev/null +++ b/build/pkgs/sagemath_meataxe/spkg-install @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# From sage-spkg. +# For type=script packages, the build rule in build/make/Makefile sources +# sage-env but not sage-dist-helpers. +lib="$SAGE_ROOT/build/bin/sage-dist-helpers" +source "$lib" +if [ $? -ne 0 ]; then + echo >&2 "Error: failed to source $lib" + echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" + exit 1 +fi +cd src + +export PIP_NO_INDEX=true +export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS" + +if [ "$SAGE_EDITABLE" = yes ]; then + # SAGE_ROOT/src/setup.py installs everything, nothing to do... + if [ "$SAGE_WHEELS" = yes ]; then + # ... except we build the wheel if requested + sdh_setup_bdist_wheel && sdh_store_wheel . + fi +else + if [ "$SAGE_WHEELS" = yes ]; then + # Modularized install via wheels + sdh_pip_install . + # else nothing to do in legacy direct installation. + fi +fi diff --git a/build/pkgs/sagemath_meataxe/spkg-src b/build/pkgs/sagemath_meataxe/spkg-src new file mode 100755 index 00000000000..a0e05c1fc98 --- /dev/null +++ b/build/pkgs/sagemath_meataxe/spkg-src @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# Script to prepare an sdist tarball for sagemath-meataxe +# This script is not used during build. +# +# HOW TO MAKE THE TARBALL: +# ./sage --sh build/pkgs/sagemath_meataxe/spkg-src + +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +# Exit on failure +set -e + +cd build/pkgs/sagemath_meataxe + +cd src +# Get rid of old *.egg-info/SOURCES.txt +rm -Rf *.egg-info + +python3 -m build --sdist --no-isolation --skip-dependency-check --outdir "$SAGE_DISTFILES" diff --git a/build/pkgs/sagemath_meataxe/src b/build/pkgs/sagemath_meataxe/src new file mode 120000 index 00000000000..1164dd787fd --- /dev/null +++ b/build/pkgs/sagemath_meataxe/src @@ -0,0 +1 @@ +../../../pkgs/sagemath-meataxe \ No newline at end of file diff --git a/build/pkgs/sagemath_meataxe/type b/build/pkgs/sagemath_meataxe/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/sagemath_meataxe/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/sagemath_objects/install-requires.txt b/build/pkgs/sagemath_objects/install-requires.txt index 68d2166b52b..c8da8347647 100644 --- a/build/pkgs/sagemath_objects/install-requires.txt +++ b/build/pkgs/sagemath_objects/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-objects ~= 10.1b7 +sagemath-objects ~= 10.1b8 diff --git a/build/pkgs/sagemath_repl/install-requires.txt b/build/pkgs/sagemath_repl/install-requires.txt index 3c157046090..da36117bd6e 100644 --- a/build/pkgs/sagemath_repl/install-requires.txt +++ b/build/pkgs/sagemath_repl/install-requires.txt @@ -1,2 +1,2 @@ # This file is updated on every release by the sage-update-version script -sagemath-repl ~= 10.1b7 +sagemath-repl ~= 10.1b8 diff --git a/build/pkgs/sagemath_sirocco/SPKG.rst b/build/pkgs/sagemath_sirocco/SPKG.rst new file mode 120000 index 00000000000..b4545b4bda6 --- /dev/null +++ b/build/pkgs/sagemath_sirocco/SPKG.rst @@ -0,0 +1 @@ +src/README.rst \ No newline at end of file diff --git a/build/pkgs/sagemath_sirocco/bootstrap b/build/pkgs/sagemath_sirocco/bootstrap new file mode 120000 index 00000000000..40542346a4e --- /dev/null +++ b/build/pkgs/sagemath_sirocco/bootstrap @@ -0,0 +1 @@ +../sagelib/bootstrap \ No newline at end of file diff --git a/build/pkgs/sagemath_sirocco/dependencies b/build/pkgs/sagemath_sirocco/dependencies new file mode 100644 index 00000000000..b923f34d7eb --- /dev/null +++ b/build/pkgs/sagemath_sirocco/dependencies @@ -0,0 +1 @@ +$(PYTHON) sirocco cypari cysignals mpfr | $(PYTHON_TOOLCHAIN) sage_setup sagemath_environment cython pkgconfig diff --git a/build/pkgs/sagemath_sirocco/distros/conda.txt b/build/pkgs/sagemath_sirocco/distros/conda.txt new file mode 100644 index 00000000000..fc9cfb79706 --- /dev/null +++ b/build/pkgs/sagemath_sirocco/distros/conda.txt @@ -0,0 +1 @@ +sagemath-sirocco diff --git a/build/pkgs/sagemath_sirocco/install-requires.txt b/build/pkgs/sagemath_sirocco/install-requires.txt new file mode 100644 index 00000000000..417fc0e71cf --- /dev/null +++ b/build/pkgs/sagemath_sirocco/install-requires.txt @@ -0,0 +1,2 @@ +# This file is updated on every release by the sage-update-version script +sagemath-sirocco ~= 10.1b8 diff --git a/build/pkgs/sagemath_sirocco/package-version.txt b/build/pkgs/sagemath_sirocco/package-version.txt new file mode 120000 index 00000000000..c4540217bba --- /dev/null +++ b/build/pkgs/sagemath_sirocco/package-version.txt @@ -0,0 +1 @@ +src/VERSION.txt \ No newline at end of file diff --git a/build/pkgs/sagemath_sirocco/spkg-install b/build/pkgs/sagemath_sirocco/spkg-install new file mode 100755 index 00000000000..7ce202f09ae --- /dev/null +++ b/build/pkgs/sagemath_sirocco/spkg-install @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# From sage-spkg. +# For type=script packages, the build rule in build/make/Makefile sources +# sage-env but not sage-dist-helpers. +lib="$SAGE_ROOT/build/bin/sage-dist-helpers" +source "$lib" +if [ $? -ne 0 ]; then + echo >&2 "Error: failed to source $lib" + echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" + exit 1 +fi +cd src + +export PIP_NO_INDEX=true +export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS" + +if [ "$SAGE_EDITABLE" = yes ]; then + # SAGE_ROOT/src/setup.py installs everything, nothing to do... + if [ "$SAGE_WHEELS" = yes ]; then + # ... except we build the wheel if requested + sdh_setup_bdist_wheel && sdh_store_wheel . + fi +else + if [ "$SAGE_WHEELS" = yes ]; then + # Modularized install via wheels + sdh_pip_install . + # else nothing to do in legacy direct installation. + fi +fi diff --git a/build/pkgs/sagemath_sirocco/spkg-src b/build/pkgs/sagemath_sirocco/spkg-src new file mode 100755 index 00000000000..4082abf43b2 --- /dev/null +++ b/build/pkgs/sagemath_sirocco/spkg-src @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# Script to prepare an sdist tarball for sagemath-sirocco +# This script is not used during build. +# +# HOW TO MAKE THE TARBALL: +# ./sage --sh build/pkgs/sagemath_sirocco/spkg-src + +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +# Exit on failure +set -e + +cd build/pkgs/sagemath_sirocco + +cd src +# Get rid of old *.egg-info/SOURCES.txt +rm -Rf *.egg-info + +python3 -m build --sdist --no-isolation --skip-dependency-check --outdir "$SAGE_DISTFILES" diff --git a/build/pkgs/sagemath_sirocco/src b/build/pkgs/sagemath_sirocco/src new file mode 120000 index 00000000000..03d0d674991 --- /dev/null +++ b/build/pkgs/sagemath_sirocco/src @@ -0,0 +1 @@ +../../../pkgs/sagemath-sirocco \ No newline at end of file diff --git a/build/pkgs/sagemath_sirocco/type b/build/pkgs/sagemath_sirocco/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/sagemath_sirocco/type @@ -0,0 +1 @@ +optional diff --git a/build/pkgs/sagemath_tdlib/SPKG.rst b/build/pkgs/sagemath_tdlib/SPKG.rst new file mode 120000 index 00000000000..b4545b4bda6 --- /dev/null +++ b/build/pkgs/sagemath_tdlib/SPKG.rst @@ -0,0 +1 @@ +src/README.rst \ No newline at end of file diff --git a/build/pkgs/sagemath_tdlib/bootstrap b/build/pkgs/sagemath_tdlib/bootstrap new file mode 120000 index 00000000000..40542346a4e --- /dev/null +++ b/build/pkgs/sagemath_tdlib/bootstrap @@ -0,0 +1 @@ +../sagelib/bootstrap \ No newline at end of file diff --git a/build/pkgs/sagemath_tdlib/dependencies b/build/pkgs/sagemath_tdlib/dependencies new file mode 100644 index 00000000000..5c7ecd31f2f --- /dev/null +++ b/build/pkgs/sagemath_tdlib/dependencies @@ -0,0 +1 @@ +$(PYTHON) tdlib cysignals | $(PYTHON_TOOLCHAIN) sage_setup sagemath_environment cython pkgconfig diff --git a/build/pkgs/sagemath_tdlib/distros/conda.txt b/build/pkgs/sagemath_tdlib/distros/conda.txt new file mode 100644 index 00000000000..f890d1c9084 --- /dev/null +++ b/build/pkgs/sagemath_tdlib/distros/conda.txt @@ -0,0 +1 @@ +sagemath-tdlib diff --git a/build/pkgs/sagemath_tdlib/install-requires.txt b/build/pkgs/sagemath_tdlib/install-requires.txt new file mode 100644 index 00000000000..949a775737d --- /dev/null +++ b/build/pkgs/sagemath_tdlib/install-requires.txt @@ -0,0 +1,2 @@ +# This file is updated on every release by the sage-update-version script +sagemath-tdlib ~= 10.1b8 diff --git a/build/pkgs/sagemath_tdlib/package-version.txt b/build/pkgs/sagemath_tdlib/package-version.txt new file mode 120000 index 00000000000..c4540217bba --- /dev/null +++ b/build/pkgs/sagemath_tdlib/package-version.txt @@ -0,0 +1 @@ +src/VERSION.txt \ No newline at end of file diff --git a/build/pkgs/sagemath_tdlib/spkg-install b/build/pkgs/sagemath_tdlib/spkg-install new file mode 100755 index 00000000000..7ce202f09ae --- /dev/null +++ b/build/pkgs/sagemath_tdlib/spkg-install @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# From sage-spkg. +# For type=script packages, the build rule in build/make/Makefile sources +# sage-env but not sage-dist-helpers. +lib="$SAGE_ROOT/build/bin/sage-dist-helpers" +source "$lib" +if [ $? -ne 0 ]; then + echo >&2 "Error: failed to source $lib" + echo >&2 "Is $SAGE_ROOT the correct SAGE_ROOT?" + exit 1 +fi +cd src + +export PIP_NO_INDEX=true +export PIP_FIND_LINKS="file://$SAGE_SPKG_WHEELS" + +if [ "$SAGE_EDITABLE" = yes ]; then + # SAGE_ROOT/src/setup.py installs everything, nothing to do... + if [ "$SAGE_WHEELS" = yes ]; then + # ... except we build the wheel if requested + sdh_setup_bdist_wheel && sdh_store_wheel . + fi +else + if [ "$SAGE_WHEELS" = yes ]; then + # Modularized install via wheels + sdh_pip_install . + # else nothing to do in legacy direct installation. + fi +fi diff --git a/build/pkgs/sagemath_tdlib/spkg-src b/build/pkgs/sagemath_tdlib/spkg-src new file mode 100755 index 00000000000..88e67414c05 --- /dev/null +++ b/build/pkgs/sagemath_tdlib/spkg-src @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# +# Script to prepare an sdist tarball for sagemath-tdlib +# This script is not used during build. +# +# HOW TO MAKE THE TARBALL: +# ./sage --sh build/pkgs/sagemath_tdlib/spkg-src + +if [ -z "$SAGE_ROOT" ] ; then + echo >&2 "Error - SAGE_ROOT undefined ... exiting" + echo >&2 "Maybe run 'sage -sh'?" + exit 1 +fi + +# Exit on failure +set -e + +cd build/pkgs/sagemath_tdlib + +cd src +# Get rid of old *.egg-info/SOURCES.txt +rm -Rf *.egg-info + +python3 -m build --sdist --no-isolation --skip-dependency-check --outdir "$SAGE_DISTFILES" diff --git a/build/pkgs/sagemath_tdlib/src b/build/pkgs/sagemath_tdlib/src new file mode 120000 index 00000000000..0d238df793e --- /dev/null +++ b/build/pkgs/sagemath_tdlib/src @@ -0,0 +1 @@ +../../../pkgs/sagemath-tdlib \ No newline at end of file diff --git a/build/pkgs/sagemath_tdlib/type b/build/pkgs/sagemath_tdlib/type new file mode 100644 index 00000000000..134d9bc32d5 --- /dev/null +++ b/build/pkgs/sagemath_tdlib/type @@ -0,0 +1 @@ +optional diff --git a/pkgs/sage-conf/VERSION.txt b/pkgs/sage-conf/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sage-conf/VERSION.txt +++ b/pkgs/sage-conf/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sage-conf_pypi/VERSION.txt b/pkgs/sage-conf_pypi/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sage-conf_pypi/VERSION.txt +++ b/pkgs/sage-conf_pypi/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sage-docbuild/VERSION.txt b/pkgs/sage-docbuild/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sage-docbuild/VERSION.txt +++ b/pkgs/sage-docbuild/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sage-setup/VERSION.txt b/pkgs/sage-setup/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sage-setup/VERSION.txt +++ b/pkgs/sage-setup/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sage-sws2rst/VERSION.txt b/pkgs/sage-sws2rst/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sage-sws2rst/VERSION.txt +++ b/pkgs/sage-sws2rst/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sagemath-bliss/MANIFEST.in b/pkgs/sagemath-bliss/MANIFEST.in new file mode 100644 index 00000000000..689b87560e0 --- /dev/null +++ b/pkgs/sagemath-bliss/MANIFEST.in @@ -0,0 +1,17 @@ +global-include all__sagemath_bliss.py + +include VERSION.txt + +graft sage/graphs/bliss_cpp + +global-exclude *.c +global-exclude *.cpp + +global-exclude __pycache__ +global-exclude *.py[co] +global-exclude *.bak +global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-bliss/README.rst b/pkgs/sagemath-bliss/README.rst new file mode 100644 index 00000000000..9e1cb2e826f --- /dev/null +++ b/pkgs/sagemath-bliss/README.rst @@ -0,0 +1,32 @@ +============================================================================== + Sage: Open Source Mathematics Software: Graph (iso/auto)morphisms with bliss +============================================================================== + +About SageMath +-------------- + + "Creating a Viable Open Source Alternative to + Magma, Maple, Mathematica, and MATLAB" + + Copyright (C) 2005-2023 The Sage Development Team + + https://www.sagemath.org + +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or Windows Subsystem for Linux). + +The traditional and recommended way to install SageMath is from source via +Sage-the-distribution (https://www.sagemath.org/download-source.html). +Sage-the-distribution first builds a large number of open source packages from +source (unless it finds suitable versions installed in the system) and then +installs the Sage Library (sagelib, implemented in Python and Cython). + + +About this pip-installable source distribution +---------------------------------------------- + +This pip-installable source distribution ``sagemath-bliss`` is a small +optional distribution for use with ``sagemath-standard``. + +It provides a Cython interface to the ``bliss`` library for the purpose +of computing graph (iso/auto)morphisms. diff --git a/pkgs/sagemath-bliss/VERSION.txt b/pkgs/sagemath-bliss/VERSION.txt new file mode 100644 index 00000000000..d06eca61e5a --- /dev/null +++ b/pkgs/sagemath-bliss/VERSION.txt @@ -0,0 +1 @@ +10.1.beta8 diff --git a/pkgs/sagemath-bliss/pyproject.toml.m4 b/pkgs/sagemath-bliss/pyproject.toml.m4 new file mode 100644 index 00000000000..439482ad26b --- /dev/null +++ b/pkgs/sagemath-bliss/pyproject.toml.m4 @@ -0,0 +1,12 @@ +include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*- +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + SPKG_INSTALL_REQUIRES_setuptools + SPKG_INSTALL_REQUIRES_sage_conf + SPKG_INSTALL_REQUIRES_sage_setup + SPKG_INSTALL_REQUIRES_sagemath_environment + SPKG_INSTALL_REQUIRES_cython + SPKG_INSTALL_REQUIRES_cysignals +] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sagemath-bliss/requirements.txt.m4 b/pkgs/sagemath-bliss/requirements.txt.m4 new file mode 100644 index 00000000000..8b6ca03b0b9 --- /dev/null +++ b/pkgs/sagemath-bliss/requirements.txt.m4 @@ -0,0 +1,2 @@ +Cython==esyscmd(`printf $(sed "s/[.]p.*//;" ../cython/package-version.txt)') +sagemath-standard==esyscmd(`printf $(sed "s/[.]p.*//;" ../sagelib/package-version.txt)') diff --git a/pkgs/sagemath-bliss/sage b/pkgs/sagemath-bliss/sage new file mode 120000 index 00000000000..e0da5daa6f2 --- /dev/null +++ b/pkgs/sagemath-bliss/sage @@ -0,0 +1 @@ +../../src/sage \ No newline at end of file diff --git a/pkgs/sagemath-bliss/setup.cfg.m4 b/pkgs/sagemath-bliss/setup.cfg.m4 new file mode 100644 index 00000000000..d1faa96a563 --- /dev/null +++ b/pkgs/sagemath-bliss/setup.cfg.m4 @@ -0,0 +1,17 @@ +include(`sage_spkg_versions.m4')dnl' -*- conf-unix -*- +[metadata] +name = sagemath-bliss +version = file: VERSION.txt +description = Sage: Open Source Mathematics Software: Graph (iso/auto)morphisms with bliss +long_description = file: README.rst +long_description_content_type = text/x-rst +include(`setup_cfg_metadata.m4')dnl' + +[options] +python_requires = >=3.8, <3.12 +install_requires = + SPKG_INSTALL_REQUIRES_cysignals + +[options.extras_require] +test = + SPKG_INSTALL_REQUIRES_sagemath_repl diff --git a/pkgs/sagemath-bliss/setup.py b/pkgs/sagemath-bliss/setup.py new file mode 100644 index 00000000000..a78c51347d6 --- /dev/null +++ b/pkgs/sagemath-bliss/setup.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +from distutils import log +from setuptools import setup + +# Work around a Cython problem in Python 3.8.x on macOS +# https://github.com/cython/cython/issues/3262 +import os +if os.uname().sysname == 'Darwin': + import multiprocessing + multiprocessing.set_start_method('fork', force=True) + +# If build isolation is not in use and setuptools_scm is installed, +# then its file_finders entry point is invoked, which we don't need. +# Workaround from ​https://github.com/pypa/setuptools_scm/issues/190#issuecomment-351181286 +try: + import setuptools_scm.integration + setuptools_scm.integration.find_files = lambda _: [] +except ImportError: + pass + +# PEP 517 builds do not have . in sys.path +import sys +sys.path.insert(0, os.path.dirname(__file__)) + +if len(sys.argv) > 1 and (sys.argv[1] == "sdist" or sys.argv[1] == "egg_info"): + sdist = True +else: + sdist = False + +if sdist: + cmdclass = {} +else: + from sage_setup.excepthook import excepthook + sys.excepthook = excepthook + + from sage_setup.setenv import setenv + setenv() + + import sage.env + sage.env.default_required_modules = sage.env.default_optional_modules = () + + from sage_setup.command.sage_build_cython import sage_build_cython + from sage_setup.command.sage_build_ext import sage_build_ext + sage_build_cython.built_distributions = ['sagemath-bliss'] + + cmdclass = dict(build_cython=sage_build_cython, + build_ext=sage_build_ext) + +from sage_setup.find import find_python_sources +python_packages, python_modules, cython_modules = find_python_sources( + '.', ['sage'], distributions=['sagemath-bliss']) + +log.warn('python_packages = {0}'.format(python_packages)) +log.warn('python_modules = {0}'.format(python_modules)) +log.warn('cython_modules = {0}'.format(cython_modules)) + +setup( + cmdclass = cmdclass, + packages = python_packages, + py_modules = python_modules, + ext_modules = cython_modules, +) diff --git a/pkgs/sagemath-categories/README.rst b/pkgs/sagemath-categories/README.rst index d1f90fea966..55cddc3c95c 100644 --- a/pkgs/sagemath-categories/README.rst +++ b/pkgs/sagemath-categories/README.rst @@ -8,7 +8,7 @@ About SageMath "Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, and MATLAB" - Copyright (C) 2005-2022 The Sage Development Team + Copyright (C) 2005-2023 The Sage Development Team https://www.sagemath.org diff --git a/pkgs/sagemath-categories/VERSION.txt b/pkgs/sagemath-categories/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sagemath-categories/VERSION.txt +++ b/pkgs/sagemath-categories/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sagemath-coxeter3/MANIFEST.in b/pkgs/sagemath-coxeter3/MANIFEST.in new file mode 100644 index 00000000000..003ab8d5180 --- /dev/null +++ b/pkgs/sagemath-coxeter3/MANIFEST.in @@ -0,0 +1,19 @@ +prune sage + +global-include all__sagemath_coxeter3.py + +include VERSION.txt + +graft sage/libs/coxeter3 + +global-exclude *.c +global-exclude *.cpp + +global-exclude __pycache__ +global-exclude *.py[co] +global-exclude *.bak +global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-coxeter3/README.rst b/pkgs/sagemath-coxeter3/README.rst new file mode 100644 index 00000000000..8b605bda499 --- /dev/null +++ b/pkgs/sagemath-coxeter3/README.rst @@ -0,0 +1,31 @@ +==================================================================================================================== + Sage: Open Source Mathematics Software: Coxeter groups, Bruhat ordering, Kazhdan-Lusztig polynomials with coxeter3 +==================================================================================================================== + +About SageMath +-------------- + + "Creating a Viable Open Source Alternative to + Magma, Maple, Mathematica, and MATLAB" + + Copyright (C) 2005-2023 The Sage Development Team + + https://www.sagemath.org + +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or Windows Subsystem for Linux). + +The traditional and recommended way to install SageMath is from source via +Sage-the-distribution (https://www.sagemath.org/download-source.html). +Sage-the-distribution first builds a large number of open source packages from +source (unless it finds suitable versions installed in the system) and then +installs the Sage Library (sagelib, implemented in Python and Cython). + + +About this pip-installable source distribution +---------------------------------------------- + +This pip-installable source distribution ``sagemath-coxeter3`` is a small +optional distribution for use with ``sagemath-standard``. + +It provides a Cython interface to the ``coxeter3`` library. diff --git a/pkgs/sagemath-coxeter3/VERSION.txt b/pkgs/sagemath-coxeter3/VERSION.txt new file mode 100644 index 00000000000..d06eca61e5a --- /dev/null +++ b/pkgs/sagemath-coxeter3/VERSION.txt @@ -0,0 +1 @@ +10.1.beta8 diff --git a/pkgs/sagemath-coxeter3/pyproject.toml.m4 b/pkgs/sagemath-coxeter3/pyproject.toml.m4 new file mode 100644 index 00000000000..e5b939e414d --- /dev/null +++ b/pkgs/sagemath-coxeter3/pyproject.toml.m4 @@ -0,0 +1,11 @@ +include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*- +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + SPKG_INSTALL_REQUIRES_setuptools + SPKG_INSTALL_REQUIRES_sage_setup + SPKG_INSTALL_REQUIRES_sagemath_environment + SPKG_INSTALL_REQUIRES_cython + SPKG_INSTALL_REQUIRES_cysignals +] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sagemath-coxeter3/requirements.txt.m4 b/pkgs/sagemath-coxeter3/requirements.txt.m4 new file mode 100644 index 00000000000..8b6ca03b0b9 --- /dev/null +++ b/pkgs/sagemath-coxeter3/requirements.txt.m4 @@ -0,0 +1,2 @@ +Cython==esyscmd(`printf $(sed "s/[.]p.*//;" ../cython/package-version.txt)') +sagemath-standard==esyscmd(`printf $(sed "s/[.]p.*//;" ../sagelib/package-version.txt)') diff --git a/pkgs/sagemath-coxeter3/sage b/pkgs/sagemath-coxeter3/sage new file mode 120000 index 00000000000..e0da5daa6f2 --- /dev/null +++ b/pkgs/sagemath-coxeter3/sage @@ -0,0 +1 @@ +../../src/sage \ No newline at end of file diff --git a/pkgs/sagemath-coxeter3/setup.cfg.m4 b/pkgs/sagemath-coxeter3/setup.cfg.m4 new file mode 100644 index 00000000000..ab3288d89ab --- /dev/null +++ b/pkgs/sagemath-coxeter3/setup.cfg.m4 @@ -0,0 +1,20 @@ +include(`sage_spkg_versions.m4')dnl' -*- conf-unix -*- +[metadata] +name = sagemath-coxeter3 +version = file: VERSION.txt +description = Sage: Open Source Mathematics Software: Coxeter groups, Bruhat ordering, Kazhdan-Lusztig polynomials with coxeter3 +long_description = file: README.rst +long_description_content_type = text/x-rst +include(`setup_cfg_metadata.m4')dnl' + +[options] +python_requires = >=3.8, <3.12 +install_requires = + +packages = + sage.libs.coxeter3 + +[options.package_data] +sage.libs.coxeter3 = + coxeter.pxd + decl.pxd diff --git a/pkgs/sagemath-coxeter3/setup.py b/pkgs/sagemath-coxeter3/setup.py new file mode 100644 index 00000000000..50d81893558 --- /dev/null +++ b/pkgs/sagemath-coxeter3/setup.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +from distutils import log +from setuptools import setup + +# Work around a Cython problem in Python 3.8.x on macOS +# https://github.com/cython/cython/issues/3262 +import os +if os.uname().sysname == 'Darwin': + import multiprocessing + multiprocessing.set_start_method('fork', force=True) + +# If build isolation is not in use and setuptools_scm is installed, +# then its file_finders entry point is invoked, which we don't need. +# Workaround from ​https://github.com/pypa/setuptools_scm/issues/190#issuecomment-351181286 +try: + import setuptools_scm.integration + setuptools_scm.integration.find_files = lambda _: [] +except ImportError: + pass + +# PEP 517 builds do not have . in sys.path +import sys +sys.path.insert(0, os.path.dirname(__file__)) + +if len(sys.argv) > 1 and (sys.argv[1] == "sdist" or sys.argv[1] == "egg_info"): + sdist = True +else: + sdist = False + +if sdist: + cmdclass = {} +else: + from sage_setup.excepthook import excepthook + sys.excepthook = excepthook + + from sage_setup.setenv import setenv + setenv() + + import sage.env + sage.env.default_required_modules = sage.env.default_optional_modules = () + + from sage_setup.command.sage_build_cython import sage_build_cython + from sage_setup.command.sage_build_ext import sage_build_ext + sage_build_cython.built_distributions = ['sagemath-coxeter3'] + + cmdclass = dict(build_cython=sage_build_cython, + build_ext=sage_build_ext) + +from sage_setup.find import find_python_sources +python_packages, python_modules, cython_modules = find_python_sources( + '.', ['sage'], distributions=['sagemath-coxeter3']) + +log.warn('python_packages = {0}'.format(python_packages)) +log.warn('python_modules = {0}'.format(python_modules)) +log.warn('cython_modules = {0}'.format(cython_modules)) + +setup( + cmdclass = cmdclass, + packages = python_packages, + py_modules = python_modules, + ext_modules = cython_modules, +) diff --git a/pkgs/sagemath-environment/README.rst b/pkgs/sagemath-environment/README.rst index ba5905777c0..eaeb4078fed 100644 --- a/pkgs/sagemath-environment/README.rst +++ b/pkgs/sagemath-environment/README.rst @@ -8,7 +8,7 @@ About SageMath "Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, and MATLAB" - Copyright (C) 2005-2022 The Sage Development Team + Copyright (C) 2005-2023 The Sage Development Team https://www.sagemath.org diff --git a/pkgs/sagemath-environment/VERSION.txt b/pkgs/sagemath-environment/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sagemath-environment/VERSION.txt +++ b/pkgs/sagemath-environment/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sagemath-environment/setup.cfg.m4 b/pkgs/sagemath-environment/setup.cfg.m4 index deb74565b3b..1ede94ee283 100644 --- a/pkgs/sagemath-environment/setup.cfg.m4 +++ b/pkgs/sagemath-environment/setup.cfg.m4 @@ -17,6 +17,7 @@ py_modules = sage.version sage.misc.all__sagemath_environment sage.misc.package + sage.misc.package_dir sage.misc.temporary_file sage.misc.viewer diff --git a/pkgs/sagemath-mcqd/MANIFEST.in b/pkgs/sagemath-mcqd/MANIFEST.in new file mode 100644 index 00000000000..392d97b78e0 --- /dev/null +++ b/pkgs/sagemath-mcqd/MANIFEST.in @@ -0,0 +1,19 @@ +prune sage + +global-include all__sagemath_mcqd.py + +include VERSION.txt + +include sage/graphs/mcqd.p* + +global-exclude *.c +global-exclude *.cpp + +global-exclude __pycache__ +global-exclude *.py[co] +global-exclude *.bak +global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-mcqd/README.rst b/pkgs/sagemath-mcqd/README.rst new file mode 100644 index 00000000000..00ef5f8ea3e --- /dev/null +++ b/pkgs/sagemath-mcqd/README.rst @@ -0,0 +1,33 @@ +=========================================================================== + Sage: Open Source Mathematics Software: Finding maximum cliques with mcqd +=========================================================================== + +About SageMath +-------------- + + "Creating a Viable Open Source Alternative to + Magma, Maple, Mathematica, and MATLAB" + + Copyright (C) 2005-2023 The Sage Development Team + + https://www.sagemath.org + +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or Windows Subsystem for Linux). + +The traditional and recommended way to install SageMath is from source via +Sage-the-distribution (https://www.sagemath.org/download-source.html). +Sage-the-distribution first builds a large number of open source packages from +source (unless it finds suitable versions installed in the system) and then +installs the Sage Library (sagelib, implemented in Python and Cython). + + +About this pip-installable source distribution +---------------------------------------------- + +This pip-installable source distribution ``sagemath-mcqd`` is a small +optional distribution for use with ``sagemath-standard``. + +It provides a Cython interface to the ``mcqd`` library, +providing a fast exact algorithm for finding a maximum clique in +an undirected graph. diff --git a/pkgs/sagemath-mcqd/VERSION.txt b/pkgs/sagemath-mcqd/VERSION.txt new file mode 100644 index 00000000000..d06eca61e5a --- /dev/null +++ b/pkgs/sagemath-mcqd/VERSION.txt @@ -0,0 +1 @@ +10.1.beta8 diff --git a/pkgs/sagemath-mcqd/pyproject.toml.m4 b/pkgs/sagemath-mcqd/pyproject.toml.m4 new file mode 100644 index 00000000000..d28ff179a00 --- /dev/null +++ b/pkgs/sagemath-mcqd/pyproject.toml.m4 @@ -0,0 +1,12 @@ +include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*- +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + SPKG_INSTALL_REQUIRES_setuptools + SPKG_INSTALL_REQUIRES_sage_setup + SPKG_INSTALL_REQUIRES_sagemath_environment + SPKG_INSTALL_REQUIRES_cython + SPKG_INSTALL_REQUIRES_memory_allocator + SPKG_INSTALL_REQUIRES_cysignals +] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sagemath-mcqd/requirements.txt.m4 b/pkgs/sagemath-mcqd/requirements.txt.m4 new file mode 100644 index 00000000000..8b6ca03b0b9 --- /dev/null +++ b/pkgs/sagemath-mcqd/requirements.txt.m4 @@ -0,0 +1,2 @@ +Cython==esyscmd(`printf $(sed "s/[.]p.*//;" ../cython/package-version.txt)') +sagemath-standard==esyscmd(`printf $(sed "s/[.]p.*//;" ../sagelib/package-version.txt)') diff --git a/pkgs/sagemath-mcqd/sage b/pkgs/sagemath-mcqd/sage new file mode 120000 index 00000000000..e0da5daa6f2 --- /dev/null +++ b/pkgs/sagemath-mcqd/sage @@ -0,0 +1 @@ +../../src/sage \ No newline at end of file diff --git a/pkgs/sagemath-mcqd/setup.cfg.m4 b/pkgs/sagemath-mcqd/setup.cfg.m4 new file mode 100644 index 00000000000..fff8f2805ef --- /dev/null +++ b/pkgs/sagemath-mcqd/setup.cfg.m4 @@ -0,0 +1,21 @@ +include(`sage_spkg_versions.m4')dnl' -*- conf-unix -*- +[metadata] +name = sagemath-mcqd +version = file: VERSION.txt +description = Sage: Open Source Mathematics Software: Finding maximum cliques with mcqd +long_description = file: README.rst +long_description_content_type = text/x-rst +include(`setup_cfg_metadata.m4')dnl' + +[options] +python_requires = >=3.8, <3.12 +install_requires = + SPKG_INSTALL_REQUIRES_memory_allocator + SPKG_INSTALL_REQUIRES_cysignals + +packages = + sage.graphs + +[options.package_data] +sage.graphs = + mcqd.pxd diff --git a/pkgs/sagemath-mcqd/setup.py b/pkgs/sagemath-mcqd/setup.py new file mode 100644 index 00000000000..c7d90663bfc --- /dev/null +++ b/pkgs/sagemath-mcqd/setup.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +from distutils import log +from setuptools import setup + +# Work around a Cython problem in Python 3.8.x on macOS +# https://github.com/cython/cython/issues/3262 +import os +if os.uname().sysname == 'Darwin': + import multiprocessing + multiprocessing.set_start_method('fork', force=True) + +# If build isolation is not in use and setuptools_scm is installed, +# then its file_finders entry point is invoked, which we don't need. +# Workaround from ​https://github.com/pypa/setuptools_scm/issues/190#issuecomment-351181286 +try: + import setuptools_scm.integration + setuptools_scm.integration.find_files = lambda _: [] +except ImportError: + pass + +# PEP 517 builds do not have . in sys.path +import sys +sys.path.insert(0, os.path.dirname(__file__)) + +if len(sys.argv) > 1 and (sys.argv[1] == "sdist" or sys.argv[1] == "egg_info"): + sdist = True +else: + sdist = False + +if sdist: + cmdclass = {} +else: + from sage_setup.excepthook import excepthook + sys.excepthook = excepthook + + from sage_setup.setenv import setenv + setenv() + + import sage.env + sage.env.default_required_modules = sage.env.default_optional_modules = () + + from sage_setup.command.sage_build_cython import sage_build_cython + from sage_setup.command.sage_build_ext import sage_build_ext + sage_build_cython.built_distributions = ['sagemath-mcqd'] + + cmdclass = dict(build_cython=sage_build_cython, + build_ext=sage_build_ext) + +from sage_setup.find import find_python_sources +python_packages, python_modules, cython_modules = find_python_sources( + '.', ['sage'], distributions=['sagemath-mcqd']) + +log.warn('python_packages = {0}'.format(python_packages)) +log.warn('python_modules = {0}'.format(python_modules)) +log.warn('cython_modules = {0}'.format(cython_modules)) + +setup( + cmdclass = cmdclass, + packages = python_packages, + py_modules = python_modules, + ext_modules = cython_modules, +) diff --git a/pkgs/sagemath-meataxe/MANIFEST.in b/pkgs/sagemath-meataxe/MANIFEST.in new file mode 100644 index 00000000000..4cf78dd5d02 --- /dev/null +++ b/pkgs/sagemath-meataxe/MANIFEST.in @@ -0,0 +1,20 @@ +prune sage + +global-include all__sagemath_meataxe.py + +include VERSION.txt + +include sage/libs/meataxe.p* +include sage/matrix/matrix_gfpn_dense.p* + +global-exclude *.c +global-exclude *.cpp + +global-exclude __pycache__ +global-exclude *.py[co] +global-exclude *.bak +global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-meataxe/README.rst b/pkgs/sagemath-meataxe/README.rst new file mode 100644 index 00000000000..088ce86478b --- /dev/null +++ b/pkgs/sagemath-meataxe/README.rst @@ -0,0 +1,36 @@ +======================================================================================== + Sage: Open Source Mathematics Software: Matrices over small finite fields with meataxe +======================================================================================== + +About SageMath +-------------- + + "Creating a Viable Open Source Alternative to + Magma, Maple, Mathematica, and MATLAB" + + Copyright (C) 2005-2023 The Sage Development Team + + https://www.sagemath.org + +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or Windows Subsystem for Linux). + +The traditional and recommended way to install SageMath is from source via +Sage-the-distribution (https://www.sagemath.org/download-source.html). +Sage-the-distribution first builds a large number of open source packages from +source (unless it finds suitable versions installed in the system) and then +installs the Sage Library (sagelib, implemented in Python and Cython). + + +About this pip-installable source distribution +---------------------------------------------- + +This pip-installable source distribution ``sagemath-meataxe`` is a small +optional distribution for use with ``sagemath-standard``. + +This distribution provides the SageMath modules :mod:`sage.libs.meataxe` +and :mod:`sage.matrix.matrix_gfpn_dense`. + +It provides a specialized implementation of matrices over the finite field F_q, where +q <= 255, using the `SharedMeatAxe ` +library. diff --git a/pkgs/sagemath-meataxe/VERSION.txt b/pkgs/sagemath-meataxe/VERSION.txt new file mode 100644 index 00000000000..d06eca61e5a --- /dev/null +++ b/pkgs/sagemath-meataxe/VERSION.txt @@ -0,0 +1 @@ +10.1.beta8 diff --git a/pkgs/sagemath-meataxe/pyproject.toml.m4 b/pkgs/sagemath-meataxe/pyproject.toml.m4 new file mode 100644 index 00000000000..e5b939e414d --- /dev/null +++ b/pkgs/sagemath-meataxe/pyproject.toml.m4 @@ -0,0 +1,11 @@ +include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*- +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + SPKG_INSTALL_REQUIRES_setuptools + SPKG_INSTALL_REQUIRES_sage_setup + SPKG_INSTALL_REQUIRES_sagemath_environment + SPKG_INSTALL_REQUIRES_cython + SPKG_INSTALL_REQUIRES_cysignals +] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sagemath-meataxe/requirements.txt.m4 b/pkgs/sagemath-meataxe/requirements.txt.m4 new file mode 100644 index 00000000000..8b6ca03b0b9 --- /dev/null +++ b/pkgs/sagemath-meataxe/requirements.txt.m4 @@ -0,0 +1,2 @@ +Cython==esyscmd(`printf $(sed "s/[.]p.*//;" ../cython/package-version.txt)') +sagemath-standard==esyscmd(`printf $(sed "s/[.]p.*//;" ../sagelib/package-version.txt)') diff --git a/pkgs/sagemath-meataxe/sage b/pkgs/sagemath-meataxe/sage new file mode 120000 index 00000000000..e0da5daa6f2 --- /dev/null +++ b/pkgs/sagemath-meataxe/sage @@ -0,0 +1 @@ +../../src/sage \ No newline at end of file diff --git a/pkgs/sagemath-meataxe/setup.cfg.m4 b/pkgs/sagemath-meataxe/setup.cfg.m4 new file mode 100644 index 00000000000..a558825e120 --- /dev/null +++ b/pkgs/sagemath-meataxe/setup.cfg.m4 @@ -0,0 +1,22 @@ +include(`sage_spkg_versions.m4')dnl' -*- conf-unix -*- +[metadata] +name = sagemath-meataxe +version = file: VERSION.txt +description = Sage: Open Source Mathematics Software: Matrices over small finite fields with meataxe +long_description = file: README.rst +long_description_content_type = text/x-rst +include(`setup_cfg_metadata.m4')dnl' + +[options] +python_requires = >=3.8, <3.12 + +packages = + sage.libs + sage.matrix + +[options.package_data] +sage.libs = + meataxe.pxd + +sage.matrix = + matrix_gfpn_dense.pxd diff --git a/pkgs/sagemath-meataxe/setup.py b/pkgs/sagemath-meataxe/setup.py new file mode 100644 index 00000000000..638c921a0d3 --- /dev/null +++ b/pkgs/sagemath-meataxe/setup.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +from distutils import log +from setuptools import setup + +# Work around a Cython problem in Python 3.8.x on macOS +# https://github.com/cython/cython/issues/3262 +import os +if os.uname().sysname == 'Darwin': + import multiprocessing + multiprocessing.set_start_method('fork', force=True) + +# If build isolation is not in use and setuptools_scm is installed, +# then its file_finders entry point is invoked, which we don't need. +# Workaround from ​https://github.com/pypa/setuptools_scm/issues/190#issuecomment-351181286 +try: + import setuptools_scm.integration + setuptools_scm.integration.find_files = lambda _: [] +except ImportError: + pass + +# PEP 517 builds do not have . in sys.path +import sys +sys.path.insert(0, os.path.dirname(__file__)) + +if len(sys.argv) > 1 and (sys.argv[1] == "sdist" or sys.argv[1] == "egg_info"): + sdist = True +else: + sdist = False + +if sdist: + cmdclass = {} +else: + from sage_setup.excepthook import excepthook + sys.excepthook = excepthook + + from sage_setup.setenv import setenv + setenv() + + import sage.env + sage.env.default_required_modules = sage.env.default_optional_modules = () + + from sage_setup.command.sage_build_cython import sage_build_cython + from sage_setup.command.sage_build_ext import sage_build_ext + sage_build_cython.built_distributions = ['sagemath-meataxe'] + + cmdclass = dict(build_cython=sage_build_cython, + build_ext=sage_build_ext) + +from sage_setup.find import find_python_sources +python_packages, python_modules, cython_modules = find_python_sources( + '.', ['sage'], distributions=['sagemath-meataxe']) + +log.warn('python_packages = {0}'.format(python_packages)) +log.warn('python_modules = {0}'.format(python_modules)) +log.warn('cython_modules = {0}'.format(cython_modules)) + +setup( + cmdclass = cmdclass, + packages = python_packages, + py_modules = python_modules, + ext_modules = cython_modules, +) diff --git a/pkgs/sagemath-objects/README.rst b/pkgs/sagemath-objects/README.rst index 9dc9cfd888f..4426d8683a3 100644 --- a/pkgs/sagemath-objects/README.rst +++ b/pkgs/sagemath-objects/README.rst @@ -8,7 +8,7 @@ About SageMath "Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, and MATLAB" - Copyright (C) 2005-2022 The Sage Development Team + Copyright (C) 2005-2023 The Sage Development Team https://www.sagemath.org diff --git a/pkgs/sagemath-objects/VERSION.txt b/pkgs/sagemath-objects/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sagemath-objects/VERSION.txt +++ b/pkgs/sagemath-objects/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sagemath-repl/README.rst b/pkgs/sagemath-repl/README.rst index 3dde4aae5e5..90b73be3cf5 100644 --- a/pkgs/sagemath-repl/README.rst +++ b/pkgs/sagemath-repl/README.rst @@ -8,7 +8,7 @@ About SageMath "Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, and MATLAB" - Copyright (C) 2005-2022 The Sage Development Team + Copyright (C) 2005-2023 The Sage Development Team https://www.sagemath.org diff --git a/pkgs/sagemath-repl/VERSION.txt b/pkgs/sagemath-repl/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/pkgs/sagemath-repl/VERSION.txt +++ b/pkgs/sagemath-repl/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/pkgs/sagemath-sirocco/MANIFEST.in b/pkgs/sagemath-sirocco/MANIFEST.in new file mode 100644 index 00000000000..7fab4dffc5d --- /dev/null +++ b/pkgs/sagemath-sirocco/MANIFEST.in @@ -0,0 +1,15 @@ +global-include all__sagemath_sirocco.py + +include VERSION.txt + +global-exclude *.c +global-exclude *.cpp + +global-exclude __pycache__ +global-exclude *.py[co] +global-exclude *.bak +global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-sirocco/README.rst b/pkgs/sagemath-sirocco/README.rst new file mode 100644 index 00000000000..0207d63b9a5 --- /dev/null +++ b/pkgs/sagemath-sirocco/README.rst @@ -0,0 +1,32 @@ +================================================================================== + Sage: Open Source Mathematics Software: Certified root continuation with sirocco +================================================================================== + +About SageMath +-------------- + + "Creating a Viable Open Source Alternative to + Magma, Maple, Mathematica, and MATLAB" + + Copyright (C) 2005-2023 The Sage Development Team + + https://www.sagemath.org + +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or Windows Subsystem for Linux). + +The traditional and recommended way to install SageMath is from source via +Sage-the-distribution (https://www.sagemath.org/download-source.html). +Sage-the-distribution first builds a large number of open source packages from +source (unless it finds suitable versions installed in the system) and then +installs the Sage Library (sagelib, implemented in Python and Cython). + + +About this pip-installable source distribution +---------------------------------------------- + +This pip-installable source distribution ``sagemath-sirocco`` is a small +optional distribution for use with ``sagemath-standard``. + +It provides a Cython interface to the ``sirocco`` library for the purpose +of compute topologically certified root continuation of bivariate polynomials. diff --git a/pkgs/sagemath-sirocco/VERSION.txt b/pkgs/sagemath-sirocco/VERSION.txt new file mode 100644 index 00000000000..d06eca61e5a --- /dev/null +++ b/pkgs/sagemath-sirocco/VERSION.txt @@ -0,0 +1 @@ +10.1.beta8 diff --git a/pkgs/sagemath-sirocco/pyproject.toml.m4 b/pkgs/sagemath-sirocco/pyproject.toml.m4 new file mode 100644 index 00000000000..1b000bd5c3b --- /dev/null +++ b/pkgs/sagemath-sirocco/pyproject.toml.m4 @@ -0,0 +1,12 @@ +include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*- +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + SPKG_INSTALL_REQUIRES_setuptools + SPKG_INSTALL_REQUIRES_sage_setup + SPKG_INSTALL_REQUIRES_sagemath_environment + SPKG_INSTALL_REQUIRES_cython + SPKG_INSTALL_REQUIRES_cypari + SPKG_INSTALL_REQUIRES_cysignals +] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sagemath-sirocco/requirements.txt.m4 b/pkgs/sagemath-sirocco/requirements.txt.m4 new file mode 100644 index 00000000000..8b6ca03b0b9 --- /dev/null +++ b/pkgs/sagemath-sirocco/requirements.txt.m4 @@ -0,0 +1,2 @@ +Cython==esyscmd(`printf $(sed "s/[.]p.*//;" ../cython/package-version.txt)') +sagemath-standard==esyscmd(`printf $(sed "s/[.]p.*//;" ../sagelib/package-version.txt)') diff --git a/pkgs/sagemath-sirocco/sage b/pkgs/sagemath-sirocco/sage new file mode 120000 index 00000000000..e0da5daa6f2 --- /dev/null +++ b/pkgs/sagemath-sirocco/sage @@ -0,0 +1 @@ +../../src/sage \ No newline at end of file diff --git a/pkgs/sagemath-sirocco/setup.cfg.m4 b/pkgs/sagemath-sirocco/setup.cfg.m4 new file mode 100644 index 00000000000..4f1e0f03d95 --- /dev/null +++ b/pkgs/sagemath-sirocco/setup.cfg.m4 @@ -0,0 +1,14 @@ +include(`sage_spkg_versions.m4')dnl' -*- conf-unix -*- +[metadata] +name = sagemath-sirocco +version = file: VERSION.txt +description = Sage: Open Source Mathematics Software: Certified root continuation with sirocco +long_description = file: README.rst +long_description_content_type = text/x-rst +include(`setup_cfg_metadata.m4')dnl' + +[options] +python_requires = >=3.8, <3.12 +install_requires = + SPKG_INSTALL_REQUIRES_cypari + SPKG_INSTALL_REQUIRES_cysignals diff --git a/pkgs/sagemath-sirocco/setup.py b/pkgs/sagemath-sirocco/setup.py new file mode 100644 index 00000000000..bffe9189bb5 --- /dev/null +++ b/pkgs/sagemath-sirocco/setup.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +from distutils import log +from setuptools import setup + +# Work around a Cython problem in Python 3.8.x on macOS +# https://github.com/cython/cython/issues/3262 +import os +if os.uname().sysname == 'Darwin': + import multiprocessing + multiprocessing.set_start_method('fork', force=True) + +# If build isolation is not in use and setuptools_scm is installed, +# then its file_finders entry point is invoked, which we don't need. +# Workaround from ​https://github.com/pypa/setuptools_scm/issues/190#issuecomment-351181286 +try: + import setuptools_scm.integration + setuptools_scm.integration.find_files = lambda _: [] +except ImportError: + pass + +# PEP 517 builds do not have . in sys.path +import sys +sys.path.insert(0, os.path.dirname(__file__)) + +if len(sys.argv) > 1 and (sys.argv[1] == "sdist" or sys.argv[1] == "egg_info"): + sdist = True +else: + sdist = False + +if sdist: + cmdclass = {} +else: + from sage_setup.excepthook import excepthook + sys.excepthook = excepthook + + from sage_setup.setenv import setenv + setenv() + + import sage.env + sage.env.default_required_modules = sage.env.default_optional_modules = () + + from sage_setup.command.sage_build_cython import sage_build_cython + from sage_setup.command.sage_build_ext import sage_build_ext + sage_build_cython.built_distributions = ['sagemath-sirocco'] + + cmdclass = dict(build_cython=sage_build_cython, + build_ext=sage_build_ext) + +from sage_setup.find import find_python_sources +python_packages, python_modules, cython_modules = find_python_sources( + '.', ['sage'], distributions=['sagemath-sirocco']) + +log.warn('python_packages = {0}'.format(python_packages)) +log.warn('python_modules = {0}'.format(python_modules)) +log.warn('cython_modules = {0}'.format(cython_modules)) + +setup( + cmdclass = cmdclass, + packages = python_packages, + py_modules = python_modules, + ext_modules = cython_modules, +) diff --git a/pkgs/sagemath-standard/setup.py b/pkgs/sagemath-standard/setup.py index 975f89b5905..7f6318a0fa0 100755 --- a/pkgs/sagemath-standard/setup.py +++ b/pkgs/sagemath-standard/setup.py @@ -76,15 +76,14 @@ # TODO: This should be quiet by default print("Discovering Python/Cython source code....") t = time.time() -distributions = [''] from sage.misc.package import is_package_installed_and_updated -optional_packages_with_extensions = ['mcqd', 'bliss', 'tdlib', - 'coxeter3', 'sirocco', 'meataxe'] +distributions = [''] +optional_packages_with_extensions = os.environ.get('SAGE_OPTIONAL_PACKAGES_WITH_EXTENSIONS', '').split(',') distributions += ['sagemath-{}'.format(pkg) for pkg in optional_packages_with_extensions if is_package_installed_and_updated(pkg)] log.warn('distributions = {0}'.format(distributions)) -from sage_setup.find import find_python_sources, find_extra_files +from sage_setup.find import find_python_sources python_packages, python_modules, cython_modules = find_python_sources( SAGE_SRC, ['sage'], distributions=distributions) diff --git a/pkgs/sagemath-tdlib/MANIFEST.in b/pkgs/sagemath-tdlib/MANIFEST.in new file mode 100644 index 00000000000..f3fbc97a588 --- /dev/null +++ b/pkgs/sagemath-tdlib/MANIFEST.in @@ -0,0 +1,17 @@ +global-include all__sagemath_tdlib.py + +include VERSION.txt + +global-exclude *.c +global-exclude *.cpp + +include sage/graphs/graph_decompositions/sage_tdlib.cpp + +global-exclude __pycache__ +global-exclude *.py[co] +global-exclude *.bak +global-exclude *.so +global-exclude *~ +prune .tox +prune build +prune dist diff --git a/pkgs/sagemath-tdlib/README.rst b/pkgs/sagemath-tdlib/README.rst new file mode 100644 index 00000000000..0c91ddcf1c6 --- /dev/null +++ b/pkgs/sagemath-tdlib/README.rst @@ -0,0 +1,32 @@ +======================================================================== + Sage: Open Source Mathematics Software: Tree decompositions with tdlib +======================================================================== + +About SageMath +-------------- + + "Creating a Viable Open Source Alternative to + Magma, Maple, Mathematica, and MATLAB" + + Copyright (C) 2005-2023 The Sage Development Team + + https://www.sagemath.org + +SageMath fully supports all major Linux distributions, recent versions of +macOS, and Windows (using Cygwin or Windows Subsystem for Linux). + +The traditional and recommended way to install SageMath is from source via +Sage-the-distribution (https://www.sagemath.org/download-source.html). +Sage-the-distribution first builds a large number of open source packages from +source (unless it finds suitable versions installed in the system) and then +installs the Sage Library (sagelib, implemented in Python and Cython). + + +About this pip-installable source distribution +---------------------------------------------- + +This pip-installable source distribution ``sagemath-tdlib`` is a small +optional distribution for use with ``sagemath-standard``. + +It provides a Cython interface to the ``tdlib`` library, providing +algorithms concerning tree decompositions. diff --git a/pkgs/sagemath-tdlib/VERSION.txt b/pkgs/sagemath-tdlib/VERSION.txt new file mode 100644 index 00000000000..d06eca61e5a --- /dev/null +++ b/pkgs/sagemath-tdlib/VERSION.txt @@ -0,0 +1 @@ +10.1.beta8 diff --git a/pkgs/sagemath-tdlib/pyproject.toml.m4 b/pkgs/sagemath-tdlib/pyproject.toml.m4 new file mode 100644 index 00000000000..e5b939e414d --- /dev/null +++ b/pkgs/sagemath-tdlib/pyproject.toml.m4 @@ -0,0 +1,11 @@ +include(`sage_spkg_versions_toml.m4')dnl' -*- conf-toml -*- +[build-system] +# Minimum requirements for the build system to execute. +requires = [ + SPKG_INSTALL_REQUIRES_setuptools + SPKG_INSTALL_REQUIRES_sage_setup + SPKG_INSTALL_REQUIRES_sagemath_environment + SPKG_INSTALL_REQUIRES_cython + SPKG_INSTALL_REQUIRES_cysignals +] +build-backend = "setuptools.build_meta" diff --git a/pkgs/sagemath-tdlib/requirements.txt.m4 b/pkgs/sagemath-tdlib/requirements.txt.m4 new file mode 100644 index 00000000000..8b6ca03b0b9 --- /dev/null +++ b/pkgs/sagemath-tdlib/requirements.txt.m4 @@ -0,0 +1,2 @@ +Cython==esyscmd(`printf $(sed "s/[.]p.*//;" ../cython/package-version.txt)') +sagemath-standard==esyscmd(`printf $(sed "s/[.]p.*//;" ../sagelib/package-version.txt)') diff --git a/pkgs/sagemath-tdlib/sage b/pkgs/sagemath-tdlib/sage new file mode 120000 index 00000000000..e0da5daa6f2 --- /dev/null +++ b/pkgs/sagemath-tdlib/sage @@ -0,0 +1 @@ +../../src/sage \ No newline at end of file diff --git a/pkgs/sagemath-tdlib/setup.cfg.m4 b/pkgs/sagemath-tdlib/setup.cfg.m4 new file mode 100644 index 00000000000..62833bbe6f6 --- /dev/null +++ b/pkgs/sagemath-tdlib/setup.cfg.m4 @@ -0,0 +1,12 @@ +include(`sage_spkg_versions.m4')dnl' -*- conf-unix -*- +[metadata] +name = sagemath-tdlib +version = file: VERSION.txt +description = Sage: Open Source Mathematics Software: Tree decompositions with tdlib +long_description = file: README.rst +long_description_content_type = text/x-rst +include(`setup_cfg_metadata.m4')dnl' + +[options] +python_requires = >=3.8, <3.12 +install_requires = SPKG_INSTALL_REQUIRES_cysignals diff --git a/pkgs/sagemath-tdlib/setup.py b/pkgs/sagemath-tdlib/setup.py new file mode 100644 index 00000000000..12547533309 --- /dev/null +++ b/pkgs/sagemath-tdlib/setup.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python + +from distutils import log +from setuptools import setup + +# Work around a Cython problem in Python 3.8.x on macOS +# https://github.com/cython/cython/issues/3262 +import os +if os.uname().sysname == 'Darwin': + import multiprocessing + multiprocessing.set_start_method('fork', force=True) + +# If build isolation is not in use and setuptools_scm is installed, +# then its file_finders entry point is invoked, which we don't need. +# Workaround from ​https://github.com/pypa/setuptools_scm/issues/190#issuecomment-351181286 +try: + import setuptools_scm.integration + setuptools_scm.integration.find_files = lambda _: [] +except ImportError: + pass + +# PEP 517 builds do not have . in sys.path +import sys +sys.path.insert(0, os.path.dirname(__file__)) + +if len(sys.argv) > 1 and (sys.argv[1] == "sdist" or sys.argv[1] == "egg_info"): + sdist = True +else: + sdist = False + +if sdist: + cmdclass = {} +else: + from sage_setup.excepthook import excepthook + sys.excepthook = excepthook + + from sage_setup.setenv import setenv + setenv() + + import sage.env + sage.env.default_required_modules = sage.env.default_optional_modules = () + + from sage_setup.command.sage_build_cython import sage_build_cython + from sage_setup.command.sage_build_ext import sage_build_ext + sage_build_cython.built_distributions = ['sagemath-tdlib'] + + cmdclass = dict(build_cython=sage_build_cython, + build_ext=sage_build_ext) + +from sage_setup.find import find_python_sources +python_packages, python_modules, cython_modules = find_python_sources( + '.', ['sage'], distributions=['sagemath-tdlib']) + +log.warn('python_packages = {0}'.format(python_packages)) +log.warn('python_modules = {0}'.format(python_modules)) +log.warn('cython_modules = {0}'.format(cython_modules)) + +setup( + cmdclass = cmdclass, + packages = python_packages, + py_modules = python_modules, + ext_modules = cython_modules, +) diff --git a/src/MANIFEST.in b/src/MANIFEST.in index 1e7df6e529b..0f6e2a37890 100644 --- a/src/MANIFEST.in +++ b/src/MANIFEST.in @@ -44,6 +44,9 @@ include sage/geometry/triangulation/triangulations.cc include sage/geometry/triangulation/data.cc include sage/geometry/triangulation/functions.cc +# Exclude extension modules shipped by optional packages +exclude sage/graphs/bliss.pyx + global-exclude __pycache__ global-exclude *.py[co] global-exclude *.bak diff --git a/src/VERSION.txt b/src/VERSION.txt index 9a62741f586..d06eca61e5a 100644 --- a/src/VERSION.txt +++ b/src/VERSION.txt @@ -1 +1 @@ -10.1.beta7 +10.1.beta8 diff --git a/src/bin/sage-fixdoctests b/src/bin/sage-fixdoctests index 3cba3464a3e..0c06d448887 100755 --- a/src/bin/sage-fixdoctests +++ b/src/bin/sage-fixdoctests @@ -14,71 +14,233 @@ AUTHORS:: situations when either the expected output or computed output are empty. Added doctest to sage.tests.cmdline """ + +# **************************************************************************** +# Copyright (C) 2006 William Stein +# 2009 Nicolas M. Thiery +# 2013 Andrew Mathas +# 2014 Volker Braun +# 2020 Jonathan Kliem +# 2021 Frédéric Chapoton +# 2023 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +import itertools import os +import re +import shlex import subprocess +import sys + from argparse import ArgumentParser, FileType +from pathlib import Path + +from sage.doctest.control import skipfile +from sage.doctest.parsing import parse_file_optional_tags, parse_optional_tags, unparse_optional_tags, update_optional_tags +from sage.env import SAGE_ROOT +from sage.features import PythonModule +from sage.features.all import all_features, module_feature, name_feature from sage.misc.temporary_file import tmp_filename parser = ArgumentParser(description="Given an input file with doctests, this creates a modified file that passes the doctests (modulo any raised exceptions). By default, the input file is modified. You can also name an output file.") -parser.add_argument('-l', '--long', - dest='long', action="store_true", default=False) -parser.add_argument("input", help="input filename", - type=FileType('r')) -parser.add_argument('output', nargs='?', help="output filename", - type=FileType('w')) +parser.add_argument('-l', '--long', dest='long', action="store_true", default=False, + help="include tests tagged '# long time'") +parser.add_argument("--distribution", type=str, default='', + help="distribution package to test, e.g., 'sagemath-graphs', 'sagemath-combinat[modules]'; sets defaults for --venv and --environment") +parser.add_argument("--venv", type=str, default='', + help="directory name of a venv where 'sage -t' is to be run") +parser.add_argument("--environment", type=str, default='', + help="name of a module that provides the global environment for tests, e.g., 'sage.all__sagemath_modules'; implies --keep-both and --full-tracebacks") +parser.add_argument("--no-test", default=False, action="store_true", + help="do not run the doctester, only rewrite '# optional/needs' tags; implies --only-tags") +parser.add_argument("--full-tracebacks", default=False, action="store_true", + help="include full tracebacks rather than '...'") +parser.add_argument("--only-tags", default=False, action="store_true", + help="only add '# optional/needs' tags where needed, ignore other failures") +parser.add_argument("--probe", metavar="FEATURES", type=str, default='', + help="check whether '# optional/needs' tags are still needed, remove these") +parser.add_argument("--keep-both", default=False, action="store_true", + help="do not replace test results; duplicate the test instead, showing both results, and mark both copies '# optional'") +parser.add_argument("--overwrite", default=False, action="store_true", + help="never interpret a second filename as OUTPUT; overwrite the source files") +parser.add_argument("--no-overwrite", default=False, action="store_true", + help="never interpret a second filename as OUTPUT; output goes to files named INPUT.fixed") +parser.add_argument("filename", nargs='*', help="input filenames; or (deprecated) INPUT_FILENAME OUTPUT_FILENAME if exactly two filenames are given and neither --overwrite nor --no-overwrite is present", + type=str) args = parser.parse_args() -# set input and output files -test_file = args.input -src_in = test_file.read() -test_file.close() -# put the output of the test into sage's temporary directory -doc_file = tmp_filename() -os.system('sage -t %s %s > %s' % ('--long' if args.long else '', - test_file.name, doc_file)) -with open(doc_file, 'r') as doc: - doc_out = doc.read() -sep = "**********************************************************************\n" +runtest_default_environment = "sage.repl.ipython_kernel.all_jupyter" + +def default_venv_environment_from_distribution(): + if args.distribution: + # shortcuts / variants + args.distribution = args.distribution.replace('_', '-') + if not (args.distribution.startswith('sagemath-') + or args.distribution.startswith('sage-')): + args.distribution = f'sagemath-{args.distribution}' + # extras + m = re.fullmatch(r'([^[]*)(\[([^]]*)\])?', args.distribution) + plain_distribution, extras = m.group(1), m.group(3) + tox_env_name = 'sagepython-sagewheels-nopypi-norequirements' + if extras: + tox_env_name += '-' + extras.replace(',', '-') + default_venv = os.path.join(SAGE_ROOT, 'pkgs', plain_distribution, '.tox', tox_env_name) + default_environment = 'sage.all__' + plain_distribution.replace('-', '_') + else: + default_venv = '' + default_environment = runtest_default_environment + return default_venv, default_environment -doctests = doc_out.split(sep) -src_in_lines = src_in.splitlines() +default_venv, default_environment = default_venv_environment_from_distribution() -for block in doctests: - if 'Failed example:' not in block: - continue # sanity checking, but shouldn't happen +if not args.venv: + args.venv = default_venv +if not args.environment: + args.environment = default_environment +if args.distribution or args.venv != default_venv or args.environment != default_environment: + args.keep_both = args.full_tracebacks = True + +venv_explainers = [] + +if args.venv: + if m := re.search(f'pkgs/(sage[^/]*)/[.]tox/((sagepython|sagewheels|nopypi|norequirements)-*)*([^/]*)$', + args.venv): + args.distribution, extras = m.group(1), m.group(4) + if extras: + args.distribution += '[' + extras.replace('-', ',') + ']' + default_venv_given_distribution, default_environment_given_distribution = default_venv_environment_from_distribution() + + if (Path(args.venv).resolve() == Path(default_venv_given_distribution).resolve() + or args.environment == default_environment_given_distribution): + venv_explainers.append(f'--distribution {shlex.quote(args.distribution)}') + default_venv, default_environment = default_venv_given_distribution, default_environment_given_distribution + +if Path(args.venv).resolve() != Path(default_venv).resolve(): + venv_explainers.append(f'--venv {shlex.quote(args.venv)}') +if args.environment != default_environment: + venv_explainers.append(f'--environment {args.environment}') + +if venv_explainers: + venv_explainer = ' (with ' + ' '.join(venv_explainers) + ')' +else: + venv_explainer = '' + + +def process_block(block, src_in_lines, file_optional_tags): # Extract the line, what was expected, and was got. - line = block.find('line ') # block should contain: 'line ##, in ...', where ## is an integer - comma = block.find(', in ') # we try to extract the line number which gives the test failure - if line == -1 or comma == -1: - continue # but if something goes wrong we give up - line_num = eval(block[line + 5:comma]) - - # Take care of multiline examples. - if 'Expected' in block: - i1 = block.index('Failed example') - i2 = block.index('Expected') - example_len = block[i1:i2].count('\n') - 1 - line_num += example_len - 1 - - if 'Expected nothing' in block: - exp = block.find('Expected nothing') - block = '\n' + block[exp + 17:] # so that split('\nGot:\n') does not fail below - elif 'Expected:' in block: - exp = block.find('Expected:\n') - block = block[exp + 10:] - elif 'Exception raised' in block: - exp = block.find('Exception raised') - block = '\nGot:\n' + block[exp + 17:] + if not (m := re.match('File "([^"]*)", line ([0-9]+), in ', block)): + return + filename = m.group(1) + first_line_num = line_num = int(m.group(2)) # 1-based line number of the first line of the example + + if m := re.search(r"using.*block-scoped tag.*'(sage: .*)'.*to avoid repeating the tag", block): + indent = (len(src_in_lines[first_line_num - 1]) - len(src_in_lines[first_line_num - 1].lstrip())) + src_in_lines[line_num - 2] += '\n' + ' ' * indent + m.group(1) + + if m := re.search(r"updating.*block-scoped tag.*'sage: (.*)'.*to avoid repeating the tag", block): + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + tags=parse_optional_tags('# ' + m.group(1))) + + if m := re.search(r"referenced here was set only in doctest marked '# (optional|needs)[-: ]*([^;']*)", block): + optional = m.group(2).split() + if src_in_lines[first_line_num - 1].strip() in ['"""', "'''"]: + # This happens due to a virtual doctest in src/sage/repl/user_globals.py + return + optional = set(optional) - set(file_optional_tags) + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + add_tags=optional) + + if m := re.search(r"tag '# (optional|needs)[-: ]([^;']*)' may no longer be needed", block): + optional = m.group(2).split() + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + remove_tags=optional) + + if m2 := re.search('(Expected:|Expected nothing|Exception raised:)\n', block): + m1 = re.search('Failed example:\n', block) + line_num += block[m1.end() : m2.start()].count('\n') - 1 + # Now line_num is the 1-based line number of the last line of the example + + if m2.group(1) == 'Expected nothing': + expected = '' + block = '\n' + block[m2.end():] # so that split('\nGot:\n') does not fail below + elif m2.group(1) == 'Exception raised:': + # In this case, the doctester does not show the expected output, + # so we do not know how many lines it spans; so we check for the next prompt or + # docstring end. + expected = [] + indentation = ' ' * (len(src_in_lines[line_num - 1]) - len(src_in_lines[line_num - 1].lstrip())) + i = line_num + while ((not src_in_lines[i].rstrip() or src_in_lines[i].startswith(indentation)) + and not re.match(' *(sage:|""")', src_in_lines[i])): + expected.append(src_in_lines[i]) + i += 1 + block = '\n'.join(expected) + '\nGot:\n' + block[m2.end():] + else: + block = block[m2.end():] else: - continue + return + # Error testing. + if m := re.search(r"ModuleNotFoundError: No module named '([^']*)'", block): + module = m.group(1) + asked_why = re.search('#.*(why|explain)', src_in_lines[first_line_num - 1]) + optional = module_feature(module) + if optional and optional.name not in file_optional_tags: + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + add_tags=[optional.name]) + if not asked_why: + # When no explanation has been demanded, + # we just mark the doctest with the feature + return + # Otherwise, continue and show the backtrace as 'GOT' + if 'Traceback (most recent call last):' in block: + expected, got = block.split('\nGot:\n') - got = got.splitlines() - got = [' Traceback (most recent call last):', ' ...', got[-1]] + if args.full_tracebacks: + if re.fullmatch(' *\n', got): + got = got[re.end(0):] + # don't show doctester internals (anything before first "" frame + if m := re.search('( *Traceback.*\n *)(?s:.*?)(^ *File "]*)>', got, re.MULTILINE): + got = m.group(1) + '...\n' + m.group(2) + '...' + got[m.end(3):] + while m := re.search(' *File "]*)>', got): + got = got[:m.start(1)] + '...' + got[m.end(1):] + # simplify filenames shown in backtrace + while m := re.search('"([-a-zA-Z0-9._/]*/site-packages)/sage/', got): + got = got[:m.start(1)] + '...' + got[m.end(1):] + + last_frame = got.rfind('File "') + if (last_frame >= 0 + and (index_NameError := got.rfind("NameError:")) >= 0 + and got[last_frame:].startswith('File "\n': expected = block[:-22] got = [''] @@ -86,21 +248,37 @@ for block in doctests: expected, got = block.split('\nGot:\n') got = got.splitlines() # got can't be the empty string + if args.only_tags: + return + + expected = expected.splitlines() + + if args.keep_both: + test_lines = ([update_optional_tags(src_in_lines[first_line_num - 1], + add_tags=[f'GOT{venv_explainer}'])] + + src_in_lines[first_line_num : line_num]) + src_in_lines[first_line_num - 1] = update_optional_tags(src_in_lines[first_line_num - 1], + add_tags=['EXPECTED']) + indent = (len(src_in_lines[line_num - 1]) - len(src_in_lines[line_num - 1].lstrip())) + line_num += len(expected) # skip to the last line of the expected output + src_in_lines[line_num - 1] += '\n'.join([''] + test_lines) # 2nd copy of the test + # now line_num is the last line of the 2nd copy of the test + expected = [] + # If we expected nothing, and got something, then we need to insert the line before line_num # and match indentation with line number line_num-1 - if expected == '': - indent = (len(src_in_lines[line_num - 1]) - len(src_in_lines[line_num - 1].lstrip())) + if not expected: + indent = (len(src_in_lines[first_line_num - 1]) - len(src_in_lines[first_line_num - 1].lstrip())) src_in_lines[line_num - 1] += '\n' + '\n'.join('%s%s' % (' ' * indent, line.lstrip()) for line in got) - continue + return - # Guess how much extra indenting got needs to match with the indentation + # Guess how much extra indenting ``got`` needs to match with the indentation # of src_in_lines - we match the indentation with the line in ``got`` which # has the smallest indentation after lstrip(). Note that the amount of indentation # required could be negative if the ``got`` block is indented. In this case # ``indent`` is set to zero. - indent = max(0, (len(src_in_lines[line_num]) - len(src_in_lines[line_num].lstrip()) - min(len(got[j]) - len(got[j].lstrip()) for j in range(len(got))))) - - expected = expected.splitlines() + indent = max(0, (len(src_in_lines[line_num]) - len(src_in_lines[line_num].lstrip()) + - min(len(got[j]) - len(got[j].lstrip()) for j in range(len(got))))) # Double check that what was expected was indeed in the source file and if # it is not then then print a warning for the user which contains the @@ -111,7 +289,7 @@ for block in doctests: txt = "Did not manage to replace\n%s\n%s\n%s\nwith\n%s\n%s\n%s" warnings.warn(txt % ('>' * 40, '\n'.join(expected), '>' * 40, '<' * 40, '\n'.join(got), '<' * 40)) - continue + return # If we got something when we expected nothing then we delete the line from the # output, otherwise, add all of what we `got` onto the end of src_in_lines[line_num] @@ -125,28 +303,109 @@ for block in doctests: for i in range(1, len(expected)): src_in_lines[line_num + i] = None -# Overwrite the source (or output file specified on the command line) -if args.output: - test_output = args.output + +# set input and output files +if len(args.filename) == 2 and not args.overwrite and not args.no_overwrite: + inputs, outputs = [args.filename[0]], [args.filename[1]] + print("sage-fixdoctests: When passing two filenames, the second one is taken as an output filename; " + "this is deprecated. To pass two input filenames, use the option --overwrite.") +elif args.no_overwrite: + inputs, outputs = args.filename, [input + ".fixed" for input in args.filename] else: - test_output = open(test_file.name, 'w') + inputs = outputs = args.filename + +# Test the doctester, putting the output of the test into sage's temporary directory +if not args.no_test: + executable = f'{os.path.relpath(args.venv)}/bin/sage' if args.venv else 'sage' + environment_args = f'--environment {args.environment} ' if args.environment != runtest_default_environment else '' + long_args = f'--long ' if args.long else '' + probe_args = f'--probe {shlex.quote(args.probe)} ' if args.probe else '' + lib_args = f'--if-installed ' if args.venv else '' + doc_file = tmp_filename() + if args.venv or environment_args: + input = os.path.join(os.path.relpath(SAGE_ROOT), 'src', 'sage', 'version.py') + cmdline = f'{shlex.quote(executable)} -t {environment_args}{long_args}{probe_args}{lib_args}{shlex.quote(input)}' + print(f'Running "{cmdline}"') + if status := os.waitstatus_to_exitcode(os.system(f'{cmdline} > {shlex.quote(doc_file)}')): + print(f'Doctester exited with error status {status}') + sys.exit(status) -for line in src_in_lines: - if line is None: +for input, output in zip(inputs, outputs): + if (skipfile_result := skipfile(input, True, log=print)) is True: continue - test_output.write(line) - test_output.write('\n') -test_output.close() + if args.no_test: + doc_out = '' + else: + # Run the doctester, putting the output of the test into sage's temporary directory + cmdline = f'{shlex.quote(executable)} -t {environment_args}{long_args}{probe_args}{lib_args}{shlex.quote(input)}' + print(f'Running "{cmdline}"') + os.system(f'{cmdline} > {shlex.quote(doc_file)}') + + with open(doc_file, 'r') as doc: + doc_out = doc.read() -# Show summary of changes -if args.output: - print('The fixed doctests have been saved as {0}.'.format(test_output.name)) -else: - from sage.env import SAGE_ROOT - relative = os.path.relpath(test_output.name, SAGE_ROOT) - print(relative) - if relative.startswith('..'): - print('Fixed source file is not part of Sage.') + # echo control messages + for m in re.finditer('^Skipping .*', doc_out, re.MULTILINE): + print('sage-runtests: ' + m.group(0)) + break else: - subprocess.call(['git', 'diff', relative], cwd=SAGE_ROOT) + sep = "**********************************************************************\n" + doctests = doc_out.split(sep) + + with open(input, 'r') as test_file: + src_in = test_file.read() + src_in_lines = src_in.splitlines() + shallow_copy_of_src_in_lines = list(src_in_lines) + + file_optional_tags = set(parse_file_optional_tags(enumerate(src_in_lines))) + + for block in doctests: + process_block(block, src_in_lines, file_optional_tags) + + # Now source line numbers do not matter any more, and lines can be real lines again + src_in_lines = list(itertools.chain.from_iterable( + [] if line is None else [''] if not line else line.splitlines() + for line in src_in_lines)) + + # Remove duplicate optional tags and rewrite all '# optional' that should be '# needs' + persistent_optional_tags = {} + for i, line in enumerate(src_in_lines): + if m := re.match(' *sage: *(.*)#', line): + tags, line_sans_tags, is_persistent = parse_optional_tags(line, return_string_sans_tags=True) + if is_persistent: + persistent_optional_tags = {tag: explanation + for tag, explanation in tags.items() + if explanation or tag not in file_optional_tags} + line = update_optional_tags(line, tags=persistent_optional_tags, force_rewrite='standard') + if not line.rstrip(): + # persistent (block-scoped or file-scoped) tag was removed, so remove the whole line + line = None + else: + tags = {tag: explanation + for tag, explanation in tags.items() + if explanation or (tag not in file_optional_tags + and tag not in persistent_optional_tags)} + line = update_optional_tags(line, tags=tags, force_rewrite='standard') + src_in_lines[i] = line + elif line.strip() in ['', '"""', "'''"]: # Blank line or end of docstring + persistent_optional_tags = {} + + if src_in_lines != shallow_copy_of_src_in_lines: + with open(output, 'w') as test_output: + for line in src_in_lines: + if line is None: + continue + test_output.write(line) + test_output.write('\n') + + # Show summary of changes + if input != output: + print("The fixed doctests have been saved as '{0}'.".format(output)) + else: + relative = os.path.relpath(output, SAGE_ROOT) + print(f"The input file '{output}' has been overwritten.") + if not relative.startswith('..'): + subprocess.call(['git', '--no-pager', 'diff', relative], cwd=SAGE_ROOT) + else: + print(f"No fixes made in '{input}'") diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 4131161647d..4d606c5a3a8 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -56,6 +56,9 @@ if __name__ == "__main__": help='run tests pretending that the software listed in FEATURES (separated by commas) is not installed; ' 'if "all" is listed, will also hide features corresponding to all optional or experimental packages; ' 'if "optional" is listed, will also hide features corresponding to optional packages.') + parser.add_argument("--probe", metavar="FEATURES", default="", + help='run tests that would not be run because one of the given FEATURES (separated by commas) is not installed; ' + 'report the tests that pass nevertheless') parser.add_argument("--randorder", type=int, metavar="SEED", help="randomize order of tests") parser.add_argument("--random-seed", dest="random_seed", type=int, metavar="SEED", help="random seed (integer) for fuzzing doctests", default=os.environ.get("SAGE_DOCTEST_RANDOM_SEED")) @@ -66,6 +69,7 @@ if __name__ == "__main__": parser.add_argument("-i", "--initial", action="store_true", default=False, help="only show the first failure in each file") parser.add_argument("--exitfirst", action="store_true", default=False, help="end the test run immediately after the first failure or unexpected exception") parser.add_argument("--force_lib", "--force-lib", action="store_true", default=False, help="do not import anything from the tested file(s)") + parser.add_argument("--if-installed", action="store_true", default=False, help="skip Python/Cython files that are not installed as modules") parser.add_argument("--abspath", action="store_true", default=False, help="print absolute paths rather than relative paths") parser.add_argument("--verbose", action="store_true", default=False, help="print debugging output during the test") parser.add_argument("-d", "--debug", action="store_true", default=False, help="drop into a python debugger when an unexpected error is raised") diff --git a/src/bin/sage-update-version b/src/bin/sage-update-version index fd6d2a8dcf3..29d8c794375 100755 --- a/src/bin/sage-update-version +++ b/src/bin/sage-update-version @@ -48,7 +48,11 @@ done if [ -f "$spkg"/install-requires.txt -a -d "$spkg"/src ]; then ( echo "# This file is updated on every release by the sage-update-version script" # Normalize the package name to PyPI convention (dashes, not underscores) - pkg=${spkg//_/-} + if [ "$spkg" = sagelib ]; then + pkg=sagemath-standard + else + pkg=${spkg//_/-} + fi # Normalize the version (updated above as VERSION.txt) according to PEP440. version=$(cat "$spkg"/package-version.txt) version=${version//.beta/b} diff --git a/src/bin/sage-version.sh b/src/bin/sage-version.sh index 71b69e96b77..1800ec2771f 100644 --- a/src/bin/sage-version.sh +++ b/src/bin/sage-version.sh @@ -4,6 +4,6 @@ # which stops "setup.py develop" from rewriting it as a Python file. : # This file is auto-generated by the sage-update-version script, do not edit! -SAGE_VERSION='10.1.beta7' -SAGE_RELEASE_DATE='2023-07-20' -SAGE_VERSION_BANNER='SageMath version 10.1.beta7, Release Date: 2023-07-20' +SAGE_VERSION='10.1.beta8' +SAGE_RELEASE_DATE='2023-07-30' +SAGE_VERSION_BANNER='SageMath version 10.1.beta8, Release Date: 2023-07-30' diff --git a/src/doc/de/tutorial/latex.rst b/src/doc/de/tutorial/latex.rst index c664bb970cd..77c0834ab77 100644 --- a/src/doc/de/tutorial/latex.rst +++ b/src/doc/de/tutorial/latex.rst @@ -319,17 +319,18 @@ lässt. Diese Liste wird verwaltet durch die Befehle ``latex.add_to_mathjax_avoid_list`` und ``latex.mathjax_avoid_list``. :: - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] Nehmen wir an ein LaTeX-Ausdruck wurde im Notebook durch ``view()`` diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index b323a7eaee4..091cb47283b 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -935,6 +935,15 @@ written. Sage does not know about the function ``AA()`` by default, so it needs to be imported before it is tested. Hence the first line in the example. + All blocks within the same docstring are linked: Variables set + in a doctest keep their values for the remaining doctests within the + same docstring. It is good practice to use different variable names for different + values, as it makes the data flow in the examples easier to understand + for human readers. (It also makes the data flow analysis in the + Sage doctester more precise.) In particular, when unrelated examples + appear in the same docstring, do not use the same variable name + for both examples. + - **Preparsing:** As in Sage's console, `4/3` returns `4/3` and not `1.3333333333333333` as in Python 3.8. Testing occurs with full Sage preparsing of input within the standard Sage shell environment, as @@ -958,6 +967,78 @@ written. 5 7 +- **Wrap long doctest lines:** Note that all doctests in EXAMPLES blocks + get formatted as part of our HTML and PDF reference manuals. Our HTML manuals + are formatted using the responsive design provided by the + :ref:`Furo theme `. Even when the browser window is expanded to + make use of the full width of a wide desktop screen, the style will not + allow code boxes to grow arbitrarily wide. + + It is best to wrap long lines when possible so that readers do not have to + scroll horizontally (back and forth) to follow an example. + + - Try to wrap long lines somewhere around columns 80 to 88 + and try to never exceed column 95 in the source file. + (Columns numbers are from the left margin in the source file; + these rules work no matter how deep the docstring may be nested + because also the formatted output will be nested.) + + - If you have to break an expression at a place that is not already + nested in parentheses, wrap it in parentheses:: + + sage: (len(list(Permutations(['a', 'b', 'c', 'd', 'e', 'f', 'g']))) + ....: == len(list(Permutations(7)))) + True + + - If the output in your only example is very wide and cannot be reasonably + reformatted to fit (for example, large symbolic matrices or numbers with many digits), + consider showing a smaller example first. + + - No need to wrap long ``import`` statements. Typically, the ``import`` statements + are not the interesting parts of the doctests. Users only need to be able to + copy-paste them into a Sage session or source file:: + + sage: from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_polydict, MPolynomialRing_polydict_domain # this is fine + + - Wrap and indent long output to maximize readability in the source code + and in the HTML output. But do not wrap strings:: + + sage: from sage.schemes.generic.algebraic_scheme import AlgebraicScheme_quasi + sage: P. = ProjectiveSpace(2, ZZ) + sage: S = P.subscheme([]) + sage: T = P.subscheme([x - y]) + sage: U = AlgebraicScheme_quasi(S, T); U + Quasi-projective subscheme X - Y of Projective Space of dimension 2 + over Integer Ring, + where X is defined by: (no polynomials) + and Y is defined by: x - y + sage: U._repr_() # this is fine + 'Quasi-projective subscheme X - Y of Projective Space of dimension 2 over Integer Ring, where X is defined by:\n (no polynomials)\nand Y is defined by:\n x - y' + + Also, if there is no whitespace in the doctest output where you could wrap the line, + do not add such whitespace. Just don't wrap the line:: + + sage: B47 = RibbonGraph(4,7, bipartite=True); B47 + Ribbon graph of genus 9 and 1 boundary components + sage: B47.sigma() # this is fine + (1,2,3,4,5,6,7)(8,9,10,11,12,13,14)(15,16,17,18,19,20,21)(22,23,24,25,26,27,28)(29,30,31,32)(33,34,35,36)(37,38,39,40)(41,42,43,44)(45,46,47,48)(49,50,51,52)(53,54,55,56) + + - Doctest tags for modularization purposes such as ``# needs sage.modules`` + (see :ref:`section-further_conventions`) should be aligned at column 88. + Clean lines from consistent alignment help reduce visual clutter. + Moreover, at the maximum window width, only the word ``# needs`` will be + visible in the HTML output without horizontal scrolling, striking a + thoughtfully chosen balance between presenting + the information and reducing visual clutter. (How much can be seen may be + browser-dependent, of course.) In visually dense doctests, you can try to sculpt out visual space to separate + the test commands from the annotation. + + - Doctest tags such as ``# optional - pynormaliz`` that make the doctest + conditional on the presence of optional packages, on the other hand, + should be aligned so that they are visible without having to scroll horizontally. + The :ref:`doctest fixer ` uses + tab stops at columns 48, 56, 64, ... for these tags. + - **Python3 print:** Python3 syntax for print must be used in Sage code and doctests. If you use an old-style print in doctests, it will raise a SyntaxError:: @@ -1131,8 +1212,9 @@ framework. Here is a comprehensive list: Neither of this applies to files or directories which are explicitly given as command line arguments: those are always tested. -- **optional:** A line flagged with ``optional - keyword`` is not tested unless - the ``--optional=keyword`` flag is passed to ``sage -t`` (see +- **optional/needs:** A line tagged with ``optional - FEATURE`` + or ``needs FEATURE`` is not tested unless the ``--optional=KEYWORD`` flag + is passed to ``sage -t`` (see :ref:`section-optional-doctest-flag`). The main applications are: - **optional packages:** When a line requires an optional package to be @@ -1140,26 +1222,6 @@ framework. Here is a comprehensive list: sage: SloaneEncyclopedia[60843] # optional - sloane_database - .. NOTE:: - - If one of the first 10 lines of a file starts with any of - ``r""" sage.doctest: optional - keyword`` - (or ``""" sage.doctest: optional - keyword`` - or ``# sage.doctest: optional - keyword`` - or ``% sage.doctest: optional - keyword`` - or ``.. sage.doctest: optional - keyword``, - or any of these with different spacing), - then that file will be skipped unless - the ``--optional=keyword`` flag is passed to ``sage -t``. - - This does not apply to files which are explicitly given - as command line arguments: those are always tested. - - If you add such a line to a file, you are strongly encouraged - to add a note to the module-level documentation, saying that - the doctests in this file will be skipped unless the - appropriate conditions are met. - - **internet:** For lines that require an internet connection:: sage: oeis(60843) # optional - internet @@ -1167,8 +1229,8 @@ framework. Here is a comprehensive list: n-state Turing machine can make on an initially blank tape before eventually halting. - - **bug:** For lines that describe bugs. Alternatively, use ``# known bug`` - instead: it is an alias for ``optional bug``. + - **known bugs:** For lines that describe known bugs, you can use ``# optional - bug``, + although ``# known bug`` is preferred. .. CODE-BLOCK:: rest @@ -1179,21 +1241,55 @@ framework. Here is a comprehensive list: sage: 2+2 # known bug 5 + - **modularization:** To enable + :ref:`separate testing of the distribution packages ` + of the modularized Sage library, doctests that depend on features provided + by other distribution packages can be tagged ``# needs FEATURE``. + For example: + + .. CODE-BLOCK:: rest + + Consider the following calculation:: + + sage: a = AA(2).sqrt() # needs sage.rings.number_field + sage: b = sqrt(3) # needs sage.symbolic + sage: a + AA(b) # needs sage.rings.number_field sage.symbolic + 3.146264369941973? + .. NOTE:: - - Any words after ``# optional`` are interpreted as a list of + - Any words after ``# optional`` and ``# needs`` are interpreted as a list of package (spkg) names or other feature tags, separated by spaces. - Any punctuation other than underscores (``_``) and periods (``.``), that is, commas, hyphens, semicolons, ..., after the first word ends the list of packages. Hyphens or colons between the word ``optional`` and the first package name are allowed. Therefore, - you should not write ``optional: needs package CHomP`` but simply - ``optional: CHomP``. + you should not write ``# optional - depends on package CHomP`` but simply + ``# optional - CHomP``. - - Optional tags are case-insensitive, so you could also write ``optional: + - Optional tags are case-insensitive, so you could also write ``# optional - chOMP``. + If ``# optional`` or ``# needs`` is placed right after the ``sage:`` prompt, + it is a block-scoped tag, which applies to all doctest lines until + a blank line is encountered. + + These tags can also be applied to an entire file. If one of the first 10 lines + of a file starts with any of ``r""" sage.doctest: optional - FEATURE``, + ``# sage.doctest: needs FEATURE``, or ``.. sage.doctest: optional - FEATURE`` + (in ``.rst`` files), etc., then this applies to all doctests in this file. + + When a file is skipped that was explicitly given as a command line argument, + a warning is displayed. + + .. NOTE:: + + If you add such a line to a file, you are strongly encouraged + to add a note to the module-level documentation, saying that + the doctests in this file will be skipped unless the + appropriate conditions are met. + - **indirect doctest:** in the docstring of a function ``A(...)``, a line calling ``A`` and in which the name ``A`` does not appear should have this flag. This prevents ``sage --coverage `` from reporting the docstring as diff --git a/src/doc/en/developer/doctesting.rst b/src/doc/en/developer/doctesting.rst index 2bc8f4765bf..db4fc7f9a83 100644 --- a/src/doc/en/developer/doctesting.rst +++ b/src/doc/en/developer/doctesting.rst @@ -13,11 +13,11 @@ its documentation. Testing can be performed using one thread or multiple threads. After compiling a source version of Sage, doctesting can be run on the whole Sage library, on all modules under a given directory, or on a specified module only. For the purposes of this -chapter, suppose we have compiled Sage 6.0 from source and the top -level Sage directory is:: +chapter, suppose we have compiled Sage from source and the top +level directory is:: - [jdemeyer@sage sage-6.0]$ pwd - /scratch/jdemeyer/build/sage-6.0 + [jdemeyer@localhost sage]$ pwd + /home/jdemeyer/sage See the section :ref:`chapter-testing` for information on Sage's automated testing process. The general syntax for doctesting is as @@ -43,7 +43,7 @@ Say we want to run all tests in the sudoku module top level Sage directory of our local Sage installation. Now we can start doctesting as demonstrated in the following terminal session:: - [jdemeyer@sage sage-6.0]$ ./sage -t src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -t src/sage/games/sudoku.py Running doctests with ID 2012-07-03-03-36-49-d82849c6. Doctesting 1 file. sage -t src/sage/games/sudoku.py @@ -63,7 +63,7 @@ one module so it is not surprising that the total testing time is approximately the same as the time required to test only that one module. Notice that the syntax is:: - [jdemeyer@sage sage-6.0]$ ./sage -t src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -t src/sage/games/sudoku.py Running doctests with ID 2012-07-03-03-39-02-da6accbb. Doctesting 1 file. sage -t src/sage/games/sudoku.py @@ -77,7 +77,7 @@ module. Notice that the syntax is:: but not:: - [jdemeyer@sage sage-6.0]$ ./sage -t sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -t sage/games/sudoku.py Running doctests with ID 2012-07-03-03-40-53-6cc4f29f. No files matching sage/games/sudoku.py No files to doctest @@ -85,11 +85,11 @@ but not:: We can also first ``cd`` to the directory containing the module ``sudoku.py`` and doctest that module as follows:: - [jdemeyer@sage sage-6.0]$ cd src/sage/games/ - [jdemeyer@sage games]$ ls + [jdemeyer@localhost sage]$ cd src/sage/games/ + [jdemeyer@localhost games]$ ls __init__.py hexad.py sudoku.py sudoku_backtrack.pyx all.py quantumino.py sudoku_backtrack.c - [jdemeyer@sage games]$ ../../../../sage -t sudoku.py + [jdemeyer@localhost games]$ ../../../../sage -t sudoku.py Running doctests with ID 2012-07-03-03-41-39-95ebd2ff. Doctesting 1 file. sage -t sudoku.py @@ -108,7 +108,7 @@ installation is a recipe for confusion. You can also run the Sage doctester as follows:: - [jdemeyer@sage sage-6.0]$ ./sage -tox -e doctest -- src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -tox -e doctest -- src/sage/games/sudoku.py See :ref:`chapter-tools` for more information about tox. @@ -126,7 +126,7 @@ our system has multiple Sage installations. For example, the following syntax is acceptable because we explicitly specify the Sage installation in the current ``SAGE_ROOT``:: - [jdemeyer@sage sage-6.0]$ ./sage -t src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ ./sage -t src/sage/games/sudoku.py Running doctests with ID 2012-07-03-03-43-24-a3449f54. Doctesting 1 file. sage -t src/sage/games/sudoku.py @@ -137,7 +137,7 @@ installation in the current ``SAGE_ROOT``:: Total time for all tests: 4.9 seconds cpu time: 3.6 seconds cumulative wall time: 3.6 seconds - [jdemeyer@sage sage-6.0]$ ./sage -t "src/sage/games/sudoku.py" + [jdemeyer@localhost sage]$ ./sage -t "src/sage/games/sudoku.py" Running doctests with ID 2012-07-03-03-43-54-ac8ca007. Doctesting 1 file. sage -t src/sage/games/sudoku.py @@ -156,10 +156,10 @@ Sage installation (if it exists): :: - [jdemeyer@sage sage-6.0]$ sage -t src/sage/games/sudoku.py + [jdemeyer@localhost sage]$ sage -t src/sage/games/sudoku.py sage -t "src/sage/games/sudoku.py" ********************************************************************** - File "/home/jdemeyer/sage/sage-6.0/src/sage/games/sudoku.py", line 515: + File "/home/jdemeyer/sage/src/sage/games/sudoku.py", line 515: sage: next(h.solve(algorithm='backtrack')) Exception raised: Traceback (most recent call last): @@ -215,7 +215,7 @@ and then using four threads. For this example, suppose we want to test all the modules under ``sage/crypto/``. We can use a syntax similar to that shown above to achieve this:: - [jdemeyer@sage sage-6.0]$ ./sage -t src/sage/crypto + [jdemeyer@localhost sage]$ ./sage -t src/sage/crypto Running doctests with ID 2012-07-03-03-45-40-7f837dcf. Doctesting 24 files. sage -t src/sage/crypto/__init__.py @@ -276,7 +276,7 @@ that shown above to achieve this:: Now we do the same thing, but this time we also use the optional argument ``--long``:: - [jdemeyer@sage sage-6.0]$ ./sage -t --long src/sage/crypto/ + [jdemeyer@localhost sage]$ ./sage -t --long src/sage/crypto/ Running doctests with ID 2012-07-03-03-48-11-c16721e6. Doctesting 24 files. sage -t --long src/sage/crypto/__init__.py @@ -372,7 +372,7 @@ as taking a long time: Now we doctest the same directory in parallel using 4 threads:: - [jdemeyer@sage sage-6.0]$ ./sage -tp 4 src/sage/crypto/ + [jdemeyer@localhost sage]$ ./sage -tp 4 src/sage/crypto/ Running doctests with ID 2012-07-07-00-11-55-9b17765e. Sorting sources by runtime so that slower doctests are run first.... Doctesting 24 files using 4 threads. @@ -430,7 +430,7 @@ Now we doctest the same directory in parallel using 4 threads:: Total time for all tests: 12.9 seconds cpu time: 30.5 seconds cumulative wall time: 31.7 seconds - [jdemeyer@sage sage-6.0]$ ./sage -tp 4 --long src/sage/crypto/ + [jdemeyer@localhost sage]$ ./sage -tp 4 --long src/sage/crypto/ Running doctests with ID 2012-07-07-00-13-04-d71f3cd4. Sorting sources by runtime so that slower doctests are run first.... Doctesting 24 files using 4 threads. @@ -504,7 +504,7 @@ to doctest the main library using multiple threads. When doing release management or patching the main Sage library, a release manager would parallel test the library using 10 threads with the following command:: - [jdemeyer@sage sage-6.0]$ ./sage -tp 10 --long src/ + [jdemeyer@localhost sage]$ ./sage -tp 10 --long src/ Another way is run ``make ptestlong``, which builds Sage (if necessary), builds the Sage documentation (if necessary), and then runs parallel @@ -516,7 +516,7 @@ the number of CPU cores (as determined by the Python function In any case, this will test the Sage library with multiple threads:: - [jdemeyer@sage sage-6.0]$ make ptestlong + [jdemeyer@localhost sage]$ make ptestlong Any of the following commands would also doctest the Sage library or one of its clones: @@ -577,7 +577,7 @@ Doctesting also works fine for files not in the Sage library. For example, suppose we have a Python script called ``my_python_script.py``:: - [mvngu@sage build]$ cat my_python_script.py + [mvngu@localhost sage]$ cat my_python_script.py from sage.all_cmdline import * # import sage library def square(n): @@ -593,7 +593,7 @@ example, suppose we have a Python script called Then we can doctest it just as with Sage library files:: - [mvngu@sage sage-6.0]$ ./sage -t my_python_script.py + [mvngu@localhost sage]$ ./sage -t my_python_script.py Running doctests with ID 2012-07-07-00-17-56-d056f7c0. Doctesting 1 file. sage -t my_python_script.py @@ -608,7 +608,7 @@ Then we can doctest it just as with Sage library files:: Doctesting can also be performed on Sage scripts. Say we have a Sage script called ``my_sage_script.sage`` with the following content:: - [mvngu@sage sage-6.0]$ cat my_sage_script.sage + [mvngu@localhost sage]$ cat my_sage_script.sage def cube(n): r""" Return the cube of n. @@ -622,7 +622,7 @@ script called ``my_sage_script.sage`` with the following content:: Then we can doctest it just as for Python files:: - [mvngu@sage build]$ sage-6.0/sage -t my_sage_script.sage + [mvngu@localhost sage]$ ./sage -t my_sage_script.sage Running doctests with ID 2012-07-07-00-20-06-82ee728c. Doctesting 1 file. sage -t my_sage_script.sage @@ -637,8 +637,8 @@ Then we can doctest it just as for Python files:: Alternatively, we can preparse it to convert it to a Python script, and then doctest that:: - [mvngu@sage build]$ sage-6.0/sage --preparse my_sage_script.sage - [mvngu@sage build]$ cat my_sage_script.sage.py + [mvngu@localhost sage]$ ./sage --preparse my_sage_script.sage + [mvngu@localhost sage]$ cat my_sage_script.sage.py # This file was *autogenerated* from the file my_sage_script.sage. from sage.all_cmdline import * # import sage library _sage_const_3 = Integer(3) @@ -652,7 +652,7 @@ and then doctest that:: 8 """ return n**_sage_const_3 - [mvngu@sage build]$ sage-6.0/sage -t my_sage_script.sage.py + [mvngu@localhost sage]$ ./sage -t my_sage_script.sage.py Running doctests with ID 2012-07-07-00-26-46-2bb00911. Doctesting 1 file. sage -t my_sage_script.sage.py @@ -716,7 +716,7 @@ Use the ``--long`` flag to run doctests that have been marked with the comment ``# long time``. These tests are normally skipped in order to reduce the time spent running tests:: - [roed@sage sage-6.0]$ sage -t src/sage/rings/tests.py + [roed@localhost sage]$ ./sage -t src/sage/rings/tests.py Running doctests with ID 2012-06-21-16-00-13-40835825. Doctesting 1 file. sage -t tests.py @@ -730,7 +730,7 @@ reduce the time spent running tests:: In order to run the long tests as well, do the following:: - [roed@sage sage-6.0]$ sage -t --long src/sage/rings/tests.py + [roed@localhost sage]$ ./sage -t --long src/sage/rings/tests.py Running doctests with ID 2012-06-21-16-02-05-d13a9a24. Doctesting 1 file. sage -t tests.py @@ -747,7 +747,7 @@ To find tests that take longer than the allowed time use the print a warning if they take longer than 1.0 second. Note that this is a warning, not an error:: - [roed@sage sage-6.0]$ sage -t --warn-long src/sage/rings/factorint.pyx + [roed@localhost sage]$ ./sage -t --warn-long src/sage/rings/factorint.pyx Running doctests with ID 2012-07-14-03-27-03-2c952ac1. Doctesting 1 file. sage -t --warn-long src/sage/rings/factorint.pyx @@ -781,7 +781,7 @@ a warning, not an error:: You can also pass in an explicit amount of time:: - [roed@sage sage-6.0]$ sage -t --long --warn-long 2.0 src/sage/rings/tests.py + [roed@localhost sage]$ ./sage -t --long --warn-long 2.0 src/sage/rings/tests.py Running doctests with ID 2012-07-14-03-30-13-c9164c9d. Doctesting 1 file. sage -t --long --warn-long 2.0 tests.py @@ -808,7 +808,7 @@ Finally, you can disable any warnings about long tests with Doctests start from a random seed:: - [kliem@sage sage-9.2]$ sage -t src/sage/doctest/tests/random_seed.rst + [kliem@localhost sage]$ ./sage -t src/sage/doctest/tests/random_seed.rst Running doctests with ID 2020-06-23-23-22-59-49f37a55. ... Doctesting 1 file. @@ -834,7 +834,9 @@ Doctests start from a random seed:: This seed can be set explicitly to reproduce possible failures:: - [kliem@sage sage-9.2]$ sage -t --warn-long 89.5 --random-seed=112986622569797306072457879734474628454 src/sage/doctest/tests/random_seed.rst + [kliem@localhost sage]$ ./sage -t --warn-long 89.5 \ + --random-seed=112986622569797306072457879734474628454 \ + src/sage/doctest/tests/random_seed.rst Running doctests with ID 2020-06-23-23-24-28-14a52269. ... Doctesting 1 file. @@ -869,13 +871,12 @@ Run optional doctests You can run tests that require optional packages by using the ``--optional`` flag. Obviously, you need to have installed the -necessary optional packages in order for these tests to succeed. See -http://www.sagemath.org/packages/optional/ in order to download -optional packages. +necessary optional packages in order for these tests to succeed. By default, Sage only runs doctests that are not marked with the ``optional`` tag. This is equivalent to running :: - [roed@sage sage-6.0]$ sage -t --optional=sagemath_doc_html,sage src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=sagemath_doc_html,sage \ + src/sage/rings/real_mpfr.pyx Running doctests with ID 2012-06-21-16-18-30-a368a200. Doctesting 1 file. sage -t src/sage/rings/real_mpfr.pyx @@ -889,7 +890,8 @@ By default, Sage only runs doctests that are not marked with the ``optional`` ta If you want to also run tests that require magma, you can do the following:: - [roed@sage sage-6.0]$ sage -t --optional=sagemath_doc_html,sage,magma src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=sagemath_doc_html,sage,magma \ + src/sage/rings/real_mpfr.pyx Running doctests with ID 2012-06-21-16-18-30-a00a7319 Doctesting 1 file. sage -t src/sage/rings/real_mpfr.pyx @@ -903,7 +905,7 @@ If you want to also run tests that require magma, you can do the following:: In order to just run the tests that are marked as requiring magma, omit ``sage`` and ``sagemath_doc_html``:: - [roed@sage sage-6.0]$ sage -t --optional=magma src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=magma src/sage/rings/real_mpfr.pyx Running doctests with ID 2012-06-21-16-18-33-a2bc1fdf Doctesting 1 file. sage -t src/sage/rings/real_mpfr.pyx @@ -919,7 +921,7 @@ If you want Sage to detect external software or other capabilities (such as magma, latex, internet) automatically and run all of the relevant tests, then add ``external``:: - $ sage -t --optional=external src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=external src/sage/rings/real_mpfr.pyx Running doctests with ID 2016-03-16-14-10-21-af2ebb67. Using --optional=external External software to be detected: cplex,gurobi,internet,latex,macaulay2,magma,maple,mathematica,matlab,octave,scilab @@ -936,7 +938,7 @@ relevant tests, then add ``external``:: To run all tests, regardless of whether they are marked optional, pass ``all`` as the ``optional`` tag:: - [roed@sage sage-6.0]$ sage -t --optional=all src/sage/rings/real_mpfr.pyx + [roed@localhost sage]$ ./sage -t --optional=all src/sage/rings/real_mpfr.pyx Running doctests with ID 2012-06-21-16-31-18-8c097f55 Doctesting 1 file. sage -t src/sage/rings/real_mpfr.pyx @@ -957,7 +959,7 @@ than one thread. To run doctests in parallel use the ``--nthreads`` flag (``-p`` is a shortened version). Pass in the number of threads you would like to use (by default Sage just uses 1):: - [roed@sage sage-6.0]$ sage -tp 2 src/sage/doctest/ + [roed@localhost sage]$ ./sage -tp 2 src/sage/doctest/ Running doctests with ID 2012-06-22-19-09-25-a3afdb8c. Sorting sources by runtime so that slower doctests are run first.... Doctesting 8 files using 2 threads. @@ -993,12 +995,12 @@ short). In addition to testing the code in Sage's Python and Cython files, this command will run the tests defined in Sage's documentation as well as testing the Sage notebook:: - [roed@sage sage-6.0]$ sage -t -a + [roed@localhost sage]$ ./sage -t -a Running doctests with ID 2012-06-22-19-10-27-e26fce6d. Doctesting entire Sage library. Sorting sources by runtime so that slower doctests are run first.... Doctesting 2020 files. - sage -t /Users/roed/sage/sage-5.3/src/sage/plot/plot.py + sage -t /Users/roed/sage/src/sage/plot/plot.py [304 tests, 69.0 s] ... @@ -1014,7 +1016,8 @@ short) then you will drop into an interactive Python debugger whenever a Python exception occurs. As an example, I modified :mod:`sage.schemes.elliptic_curves.constructor` to produce an error:: - [roed@sage sage-6.0]$ sage -t --debug src/sage/schemes/elliptic_curves/constructor.py + [roed@localhost sage]$ ./sage -t --debug \ + src/sage/schemes/elliptic_curves/constructor.py Running doctests with ID 2012-06-23-12-09-04-b6352629. Doctesting 1 file. ********************************************************************** @@ -1023,22 +1026,22 @@ a Python exception occurs. As an example, I modified EllipticCurve([0,0]) Exception raised: Traceback (most recent call last): - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/doctest/forker.py", line 573, in _run + File ".../site-packages/sage/doctest/forker.py", line 573, in _run self.execute(example, compiled, test.globs) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/doctest/forker.py", line 835, in execute + File ".../site-packages/sage/doctest/forker.py", line 835, in execute exec compiled in globs File "", line 1, in EllipticCurve([Integer(0),Integer(0)]) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/constructor.py", line 346, in EllipticCurve + File ".../site-packages/sage/schemes/elliptic_curves/constructor.py", line 346, in EllipticCurve return ell_rational_field.EllipticCurve_rational_field(x, y) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/ell_rational_field.py", line 216, in __init__ + File ".../site-packages/sage/schemes/elliptic_curves/ell_rational_field.py", line 216, in __init__ EllipticCurve_number_field.__init__(self, Q, ainvs) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/ell_number_field.py", line 159, in __init__ + File ".../site-packages/sage/schemes/elliptic_curves/ell_number_field.py", line 159, in __init__ EllipticCurve_field.__init__(self, [field(x) for x in ainvs]) - File "/Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/ell_generic.py", line 156, in __init__ + File ".../site-packages/sage/schemes/elliptic_curves/ell_generic.py", line 156, in __init__ "Invariants %s define a singular curve."%ainvs ArithmeticError: Invariants [0, 0, 0, 0, 0] define a singular curve. - > /Users/roed/sage/sage-5.3/local/lib/python2.7/site-packages/sage/schemes/elliptic_curves/ell_generic.py(156)__init__() + > .../site-packages/sage/schemes/elliptic_curves/ell_generic.py(156)__init__() -> "Invariants %s define a singular curve."%ainvs (Pdb) l 151 if len(ainvs) == 2: @@ -1075,8 +1078,9 @@ you know what test caused the problem (if you want this output to appear in real time use the ``--verbose`` flag). To have doctests run under the control of gdb, use the ``--gdb`` flag:: - [roed@sage sage-6.0]$ sage -t --gdb src/sage/schemes/elliptic_curves/constructor.py - exec gdb --eval-commands="run" --args /home/roed/sage-9.7/local/var/lib/sage/venv-python3.9/bin/python3 sage-runtests --serial --timeout=0 --stats_path=/home/roed/.sage/timings2.json --optional=pip,sage,sage_spkg src/sage/schemes/elliptic_curves/constructor.py + [roed@localhost sage]$ ./sage -t --gdb \ + src/sage/schemes/elliptic_curves/constructor.py + exec gdb --eval-commands="run" --args /home/roed/sage/local/var/lib/sage/venv-python3.9/bin/python3 sage-runtests --serial --timeout=0 --stats_path=/home/roed/.sage/timings2.json --optional=pip,sage,sage_spkg src/sage/schemes/elliptic_curves/constructor.py GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later @@ -1110,7 +1114,7 @@ Once you're done fixing whatever problems where revealed by the doctests, you can rerun just those files that failed their most recent test by using the ``--failed`` flag (``-f`` for short):: - [roed@sage sage-6.0]$ sage -t -fa + [roed@localhost sage]$ ./sage -t -fa Running doctests with ID 2012-07-07-00-45-35-d8b5a408. Doctesting entire Sage library. Only doctesting files that failed last test. @@ -1138,7 +1142,8 @@ Show skipped optional tests To print a summary at the end of each file with the number of optional tests skipped, use the ``--show-skipped`` flag:: - [roed@sage sage-6.0]$ sage -t --show-skipped src/sage/rings/finite_rings/integer_mod.pyx + [roed@localhost sage]$ ./sage -t --show-skipped \ + src/sage/rings/finite_rings/integer_mod.pyx Running doctests with ID 2013-03-14-15-32-05-8136f5e3. Doctesting 1 file. sage -t sage/rings/finite_rings/integer_mod.pyx @@ -1163,7 +1168,7 @@ you to run tests repeatedly in an attempt to search for Heisenbugs. The flag ``--global-iterations`` takes an integer and runs the whole set of tests that many times serially:: - [roed@sage sage-6.0]$ sage -t --global-iterations 2 src/sage/sandpiles + [roed@localhost sage]$ ./sage -t --global-iterations 2 src/sage/sandpiles Running doctests with ID 2012-07-07-00-59-28-e7048ad9. Doctesting 3 files (2 global iterations). sage -t src/sage/sandpiles/__init__.py @@ -1194,7 +1199,7 @@ set of tests that many times serially:: You can also iterate in a different order: the ``--file-iterations`` flag runs the tests in each file ``N`` times before proceeding:: - [roed@sage sage-6.0]$ sage -t --file-iterations 2 src/sage/sandpiles + [roed@localhost sage]$ ./sage -t --file-iterations 2 src/sage/sandpiles Running doctests with ID 2012-07-07-01-01-43-8f954206. Doctesting 3 files (2 file iterations). sage -t src/sage/sandpiles/__init__.py @@ -1222,7 +1227,7 @@ On a slow machine the default timeout of 5 minutes may not be enough for the slowest files. Use the ``--timeout`` flag (``-T`` for short) to set it to something else:: - [roed@sage sage-6.0]$ sage -tp 2 --all --timeout 1 + [roed@localhost sage]$ ./sage -tp 2 --all --timeout 1 Running doctests with ID 2012-07-07-01-09-37-deb1ab83. Doctesting entire Sage library. Sorting sources by runtime so that slower doctests are run first.... @@ -1237,10 +1242,10 @@ Using absolute paths By default filenames are printed using relative paths. To use absolute paths instead pass in the ``--abspath`` flag:: - [roed@sage sage-6.0]$ sage -t --abspath src/sage/doctest/control.py + [roed@localhost sage]$ ./sage -t --abspath src/sage/doctest/control.py Running doctests with ID 2012-07-07-01-13-03-a023e212. Doctesting 1 file. - sage -t /home/roed/sage-6.0/src/sage/doctest/control.py + sage -t /home/roed/sage/src/sage/doctest/control.py [133 tests, 4.7 s] ------------------------------------------------------------------------ All tests passed! @@ -1258,7 +1263,7 @@ convenient to test only the files that have changed. To do so use the ``--new`` flag, which tests files that have been modified or added since the last commit:: - [roed@sage sage-6.0]$ sage -t --new + [roed@localhost sage]$ ./sage -t --new Running doctests with ID 2012-07-07-01-15-52-645620ee. Doctesting files changed since last git commit. Doctesting 1 file. @@ -1279,7 +1284,7 @@ By default, tests are run in the order in which they appear in the file. To run tests in a random order (which can reveal subtle bugs), use the ``--randorder`` flag and pass in a random seed:: - [roed@sage sage-6.0]$ sage -t --new --randorder 127 + [roed@localhost sage]$ ./sage -t --new --randorder 127 Running doctests with ID 2012-07-07-01-19-06-97c8484e. Doctesting files changed since last git commit. Doctesting 1 file. @@ -1309,7 +1314,7 @@ Auxiliary files To specify a logfile (rather than use the default which is created for ``sage -t --all``), use the ``--logfile`` flag:: - [roed@sage sage-6.0]$ sage -t --logfile test1.log src/sage/doctest/control.py + [roed@localhost sage]$ ./sage -t --logfile test1.log src/sage/doctest/control.py Running doctests with ID 2012-07-07-01-25-49-e7c0e52d. Doctesting 1 file. sage -t src/sage/doctest/control.py @@ -1320,7 +1325,7 @@ To specify a logfile (rather than use the default which is created for Total time for all tests: 6.7 seconds cpu time: 0.1 seconds cumulative wall time: 4.3 seconds - [roed@sage sage-6.0]$ cat test1.log + [roed@localhost sage]$ cat test1.log Running doctests with ID 2012-07-07-01-25-49-e7c0e52d. Doctesting 1 file. sage -t src/sage/doctest/control.py @@ -1338,9 +1343,315 @@ To give a json file storing the timings for each file, use the that slower tests are run first (and thus multiple processes are utilized most efficiently):: - [roed@sage sage-6.0]$ sage -tp 2 --stats-path ~/.sage/timings2.json --all + [roed@localhost sage]$ ./sage -tp 2 --stats-path ~/.sage/timings2.json --all Running doctests with ID 2012-07-07-01-28-34-2df4251d. Doctesting entire Sage library. Sorting sources by runtime so that slower doctests are run first.... Doctesting 2067 files using 2 threads. ... + +.. _section-doctesting-venv: + +Options for testing in virtual environments +------------------------------------------- + +The distribution packages of the modularized Sage library can be tested in virtual environments. +Sage has infrastructure to create such virtual environments using ``tox``, which is explained +in detail in :ref:`section-modularized-doctesting`. Our examples in this section +refer to this setting, but it applies the same to any user-created virtual environments. + +The virtual environments, set up in directories such as +``pkgs/sagemath-standard/.tox/sagepython-sagewheels-nopypi-norequirements`` +contain installations of built (non-editable) wheels. + +To test all modules of Sage that are installed in a virtual environment, +use the option ``--installed`` (instead of ``--all``):: + + [mkoeppe@localhost sage]$ pkgs/sagemath-standard/.tox/sagepython-.../sage -t \ + -p4 --installed + +This tests against the doctests as they appear in the installed copies of the files +(in ``site-packages/sage/...``). +Note that these installed copies should never be edited, as they can +be overwritten without warning. + +When testing a modularized distribution package other than sagemath-standard, +the top-level module :mod:`sage.all` is not available. Use the option ``--environment`` +to select an appropriate top-level module:: + + [mkoeppe@localhost sage]$ pkgs/sagemath-categories/.tox/sagepython-.../sage -t \ + -p4 --environment sage.all__sagemath_categories \ + --installed + +To test the installed modules against the doctests as they appear in the source +tree (``src/sage/...``):: + + [mkoeppe@localhost sage]$ pkgs/sagemath-categories/.tox/sagepython-.../sage -t \ + -p4 --environment sage.all__sagemath_categories \ + src/sage/structure + +Note that testing all doctests as they appear in the source tree does not make sense +because many of the source files may not be installed in the virtual environment. +Use the option ``--if-installed`` to skip the source files of all Python/Cython modules +that are not installed in the virtual environment:: + + [mkoeppe@localhost sage]$ pkgs/sagemath-categories/.tox/sagepython-.../sage -t \ + -p4 --environment sage.all__sagemath_categories \ + --if-installed src/sage/schemes + +This option can also be combined with ``--all``:: + + [mkoeppe@localhost sage]$ pkgs/sagemath-categories/.tox/sagepython-.../sage -t \ + -p4 --environment sage.all__sagemath_categories \ + --if-installed --all + + +.. _section-fixdoctests: + +The doctest fixer +================= + +Sage provides a development tool that assists with updating doctests. + + +Updating doctest outputs +------------------------ + +By default, ``./sage --fixdoctests`` runs the doctester and replaces the expected outputs +of all examples by the actual outputs from the current version of Sage:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests \ + --overwrite src/sage/arith/weird.py + +For example, when applied to this Python file:: + + | r""" + | ... + | + | EXAMPLES:: + | + | sage: 2 + 2 + | 5 + | sage: factor("91") + | "7" * "13" + | ... + +the doctest fixer edits the file as follows:: + + | r""" + | ... + | + | EXAMPLES:: + | + | sage: 2 + 2 + | 4 + | sage: factor("91") + | Traceback (most recent call last): + | ... + | TypeError: unable to factor '91' + | ... + +As this command edits the source file, it may be a good practice to first use ``git commit`` +to save any changes made in the file. + +After running the doctest fixer, it is a good idea to use ``git diff`` to check +all edits that the automated tool made. + +An alternative to this workflow is to use the option ``--keep-both``. When expected and +actual output of an example differ, it duplicates the example, marking the two copies +``# optional - EXPECTED`` and ``# optional - GOT``. (Thus, when re-running the doctester, +neither of the two copies is run; this makes ``./sage --fixdoctests`` idempotent.) + +When exceptions are expected by an example, it is standard practice to abbreviate +the tracebacks using ``...``. The doctest fixer uses this abbreviation automatically +when formatting the actual output, as shown in the above example. +To disable it so that the details of the exception +can be inspected, use the option ``--full-tracebacks``. This is particularly useful +in combination with ``--keep-both``:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests --keep-both --full-tracebacks \ + --overwrite src/sage/arith/weird.py + +This will give the following result on the above example:: + + | r""" + | ... + | + | EXAMPLES:: + | + | sage: 2 + 2 # optional - EXPECTED + | 5 + | sage: 2 + 2 # optional - GOT + | 4 + | sage: factor("91") # optional - EXPECTED + | "7" * "13" + | sage: factor("91") # optional - GOT + | Traceback (most recent call last): + | ... + | File "", line 1, in + | factor("91") + | File ".../src/sage/arith/misc.py", line 2680, in factor + | raise TypeError("unable to factor {!r}".format(n)) + | TypeError: unable to factor '91' + | ... + | """ + +To make sure that all doctests are updated, you may have to use the option ``--long``:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests --long \ + --overwrite src/sage/arith/weird.py + +If you are not comfortable with allowing this tool to edit your source files, you can use +the option ``--no-overwrite``, which will create a new file with the extension ``.fixed`` +instead of overwriting the source file:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests \ + --no-overwrite src/sage/arith/weird.py + + +.. _section-fixdoctests-optional-needs: + +Managing ``# optional`` and ``# needs`` tags +-------------------------------------------- + +When a file uses a ``# sage.doctest: optional/needs FEATURE`` directive, the +doctest fixer automatically removes the redundant ``# optional/needs FEATURE`` +tags from all ``sage:`` lines. Likewise, when a block-scoped tag +``sage: # optional/needs FEATURE`` is used, then the doctest fixer removes +redundant tags from all doctests in this scope. For example:: + + | # sage.doctest: optional - sirocco, needs sage.rings.number_field + | r""" + | ... + | + | EXAMPLES:: + | + | sage: # needs sage.modules sage.rings.number_field + | sage: Q5 = QuadraticField(5) + | sage: V = Q5^42 # needs sage.modules + | sage: T = transmogrify(V) # optional - bliss sirocco + +is automatically transformed to:: + + | # sage.doctest: optional - sirocco, needs sage.rings.number_field + | r""" + | ... + | + | EXAMPLES:: + | + | sage: # needs sage.modules + | sage: Q5 = QuadraticField(5) + | sage: V = Q5^42 + | sage: T = transmogrify(V) # optional - bliss + +The doctest fixer also aligns the ``# optional/needs FEATURE`` tags on +individual doctests at a fixed set of tab stops. + +The doctester may issue style warnings when ``# optional/needs`` tags are +repeated on a whole block of doctests, suggesting to use a block-scoped tag +instead. The doctest fixer makes these changes automatically. + +There are situations in which the doctester and doctest fixer show too +much restraint and a manual intervention would improve the formatting +of the doctests. In the example below, the doctester does not issue a +style warning because the first doctest line does not carry the ``# needs`` +tag:: + + | EXAMPLES:: + | + | sage: set_verbose(-1) + | sage: P. = ProjectiveSpace(QQbar, 2) # needs sage.rings.number_field + | sage: C = Curve([x^3*y + 2*x^2*y^2 + x*y^3 # needs sage.rings.number_field + | ....: + x^3*z + 7*x^2*y*z + | ....: + 14*x*y^2*z + 9*y^3*z], P) + | sage: Q = P([0,0,1]) # needs sage.rings.number_field + | sage: C.tangents(Q) # needs sage.rings.number_field + | [x + 4.147899035704788?*y, + | x + (1.426050482147607? + 0.3689894074818041?*I)*y, + | x + (1.426050482147607? - 0.3689894074818041?*I)*y] + +To change this example, there are two approaches: + +#. Just add the line ``sage: # needs sage.rings.number_field`` at + the beginning and run the doctest fixer, which will remove the tags on the individual + doctests that have now become redundant. + +#. Insert a blank line after the first doctest line, splitting the block into two. + Now the ``# needs`` tag is repeated on the whole second block, so running the doctest + fixer will add a block-scoped tag and remove the individual tags:: + + | EXAMPLES:: + | + | sage: set_verbose(-1) + | + | sage: # needs sage.rings.number_field + | sage: P. = ProjectiveSpace(QQbar, 2) + | sage: C = Curve([x^3*y + 2*x^2*y^2 + x*y^3 + | ....: + x^3*z + 7*x^2*y*z + | ....: + 14*x*y^2*z + 9*y^3*z], P) + | sage: Q = P([0,0,1]) + | sage: C.tangents(Q) + | [x + 4.147899035704788?*y, + | x + (1.426050482147607? + 0.3689894074818041?*I)*y, + | x + (1.426050482147607? - 0.3689894074818041?*I)*y] + +In places where the doctester issues a doctest dataflow warning +(``Variable ... referenced here was set only in doctest marked '# optional - FEATURE'``), +the doctest fixer automatically adds the missing ``# optional/needs`` tags. + +Sometimes code changes can make existing ``# optional/needs FEATURE`` tags unnecessary. +In an installation or virtual environment where ``FEATURE`` is not available, +you can invoke the doctest fixer with the option ``--probe FEATURE``. +Then it will run examples marked ``# optional/needs - FEATURE`` silently, and if the example +turns out to work anyway, the tag is automatically removed. + +.. note:: + + Probing works best when the doctests within a docstring do not reuse the same variable + for different values. + +To have the doctest fixer take care of the ``# optional/needs`` tags, +but not change the expected results of examples, use the option ``--only-tags``. +This mode is suitable for mostly unattended runs on many files. + +.. warning:: + + While the doctest fixer guarantees to preserve any comments that + appear before ``# optional/needs`` and all parenthesized comments + of the form ``# optional - FEATURE (EXPLANATION)``, any free-form comments + that may be mixed with the doctest tags will be lost. + +If you don't want to update any doctests, you can use the +option ``--no-test``. In this mode, the doctest fixer does not run +the doctester and only normalizes the style of the ``# optional`` tags. + + +Use in virtual environments +--------------------------- + +The doctest fixer can also run tests using the Sage doctester installed in +a virtual environment:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests --overwrite \ + --distribution sagemath-categories \ + src/sage/geometry/schemes/generic/*.py + +This command, using ``--distribution``, is equivalent to a command +that uses the more specific options ``--venv`` and ``--environment``:: + + [mkoeppe@localhost sage]$ ./sage --fixdoctests --overwrite \ + --venv pkgs/sagemath-categories/.tox/sagepython-... \ + --environment sage.all__sagemath_categories + src/sage/geometry/schemes/generic/*.py + +Either way, the options ``--keep-both``, ``--full-tracebacks``, and +``--if-installed`` are implied. + +In this mode of operation, when the doctester encounters a global name +that is unknown in its virtual environment (:class:`NameError`), +the doctest fixer will look up the name in its own environment (typically +a full installation of the Sage library) and add a ``# needs ...`` tag +to the doctest. + +Likewise, when the doctester runs into a :class:`ModuleNotFoundError`, +the doctest fixer will automatically add a ``# needs ...`` tag. diff --git a/src/doc/en/developer/packaging_sage_library.rst b/src/doc/en/developer/packaging_sage_library.rst index 83f883dd53e..abda71bbed8 100644 --- a/src/doc/en/developer/packaging_sage_library.rst +++ b/src/doc/en/developer/packaging_sage_library.rst @@ -536,21 +536,28 @@ Not shown in the diagram are build dependencies and optional dependencies for te the Sage doctester (:mod:`sage.doctest`), and some related modules from :mod:`sage.misc`. +.. _section-modularized-doctesting: + Testing distribution packages ============================= Of course, we need tools for testing modularized distributions of portions of the Sage library. -- Modularized distributions must be testable separately! +- Distribution packages of the modularized Sage library must be testable separately! - But we want to keep integration testing with other portions of Sage too! -Preparing doctests ------------------- +Preparing doctests for modularized testing +------------------------------------------ + +Section :ref:`section-doctest-writing` explains how to write doctests +for Sage. Here we show how to prepare existing or new doctests so that +they are suitable for modularized testing. -Whenever an optional package is needed for a particular test, we use the -doctest annotation ``# optional``. This mechanism can also be used for making a +Per section :ref:`section-further_conventions`, +whenever an optional package is needed for a particular test, we use the +doctest tag ``# optional``. This mechanism can also be used for making a doctest conditional on the presence of a portion of the Sage library. The available tags take the form of package or module names such as @@ -564,29 +571,32 @@ hints to the user. For example, the package :mod:`sage.tensor` is purely algebraic and has no dependency on symbolics. However, there are a small number of doctests that depend on :class:`sage.symbolic.ring.SymbolicRing` for integration -testing. Hence, these doctests are marked ``# optional - -sage.symbolic``. +testing. Hence, these doctests are marked as depending on the feature +:class:`sage.symbolic <~sage.features.sagemath.sage__symbolic>`. -When defining new features for the purpose of doctest annotations, it may be a good +By convention, because :class:`sage.symbolic <~sage.features.sagemath.sage__symbolic>` +is present in a standard installation of Sage, we use the keyword ``# needs`` +instead of ``# optional``. These two keywords have identical semantics; +the tool :ref:`sage --fixdoctests ` +rewrites the doctest tags according to the convention. + +When defining new features for the purpose of conditionalizing doctests, it may be a good idea to hide implementation details from feature names. For example, all doctests that -use finite fields have to depend on PARI. However, we have defined a feature +use large finite fields have to depend on PARI. However, we have defined a feature :mod:`sage.rings.finite_rings` (which implies the presence of :mod:`sage.libs.pari`). -Annotating the doctests ``# optional - sage.rings.finite_rings`` expresses the -dependency in a clearer way than using ``# optional - sage.libs.pari``, and it +Marking the doctests ``# needs sage.rings.finite_rings`` expresses the +dependency in a clearer way than using ``# needs sage.libs.pari``, and it will be a smaller maintenance burden when implementation details change. Testing the distribution in virtual environments with tox --------------------------------------------------------- -So how to test that this works? - -Sure, we could go into the installation directory -``SAGE_VENV/lib/python3.9/site-packages/`` and do ``rm -rf -sage/symbolic`` and test that things still work. But that's not a good -way of testing. +Chapter :ref:`chapter-doctesting` explains in detail how to run the +Sage doctester with various options. -Instead, we use a virtual environment in which we only install the +To test a distribution package of the modularized Sage library, +we use a virtual environment in which we only install the distribution to be tested (and its Python dependencies). Let's try it out first with the entire Sage library, represented by @@ -641,7 +651,7 @@ command:: This command does not make any changes to the normal installation of Sage. The virtual environment is created in a subdirectory of -``SAGE_ROOT/pkgs/sagemath-standard-no-symbolics/.tox/``. After the command +``SAGE_ROOT/pkgs/sagemath-standard/.tox/``. After the command finishes, we can start the separate installation of the Sage library in its virtual environment:: @@ -655,10 +665,10 @@ The whole ``.tox`` directory can be safely deleted at any time. We can do the same with other distributions, for example the large distribution **sagemath-standard-no-symbolics** -(from :trac:`32601`), which is intended to provide +(from :trac:`35095`), which is intended to provide everything that is currently in the standard Sage library, i.e., without depending on optional packages, but without the packages -:mod:`sage.symbolic`, :mod:`sage.functions`, :mod:`sage.calculus`, etc. +:mod:`sage.symbolic`, :mod:`sage.calculus`, etc. Again we can run the test with ``tox`` in a separate virtual environment:: @@ -682,4 +692,4 @@ Building these small distributions serves as a valuable regression testsuite. However, a current issue with both of these distributions is that they are not separately testable: The doctests for these modules depend on a lot of other functionality from higher-level parts -of the Sage library. +of the Sage library. This is being addressed in :issue:`35095`. diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 9551f10f0d3..475a34f5dea 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2724,9 +2724,9 @@ REFERENCES: doubles of finite groups*. PhD Thesis, University of California, Santa Cruz. 1999. -.. [GoMa2010] Christopher Goff and Geoffrey Mason, +.. [GoMa2010] Christopher Goff and Geoffrey Mason, *Generalized twisted quantum doubles and the McKay correspondence*, - J. Algebra 324 (2010), no. 11, 3007–3016. + J. Algebra 324 (2010), no. 11, 3007–3016. .. [GJ2016] Muddappa Seetharama Gowda and Juyoung Jeong. Spectral cones in Euclidean Jordan algebras. @@ -3102,8 +3102,9 @@ REFERENCES: with tunable clustering*, Phys. Rev. E (2002). vol 65, no 2, 026107. :doi:`10.1103/PhysRevE.65.026107`. -.. [HKL2021] Clemens Heuberger, Daniel Krenn, Gabriel F. Lipnik, "Asymptotic - Analysis of `q`-Recursive Sequences", :arxiv:`2105.04334`, 2021. +.. [HKL2022] Clemens Heuberger, Daniel Krenn, Gabriel F. Lipnik, "Asymptotic + Analysis of `q`-Recursive Sequences". Algorithmica **84** (2022), + 2480–2532. :doi:`10.1007/s00453-022-00950-y`. .. [HKOTY1999] \G. Hatayama, A. Kuniba, M. Okado, T. Tagaki, and Y. Yamada, *Remarks on fermionic formula*. Contemp. Math., **248** (1999). @@ -5082,6 +5083,11 @@ REFERENCES: .. [Pos2005] \A. Postnikov, Affine approach to quantum Schubert calculus, Duke Math. J. 128 (2005) 473-509 +.. [Pot1998] Potemine, I.Y., + Minimal Terminal Q-Factorial Models of Drinfeld Coarse Moduli Schemes. + Mathematical Physics, Analysis and Geometry 1, 171-191 (1998). + :doi:`10.1023/A:1009724323513` + .. [PPW2013] \D. Perkinson, J. Perlman, and J. Wilmes. *Primer for the algebraic geometry of sandpiles*. Tropical and Non-Archimedean diff --git a/src/doc/fr/tutorial/latex.rst b/src/doc/fr/tutorial/latex.rst index ae326102be1..bed8a4fad93 100644 --- a/src/doc/fr/tutorial/latex.rst +++ b/src/doc/fr/tutorial/latex.rst @@ -306,17 +306,18 @@ d'appeler latex (ou plus généralement le moteur choisi via ``latex.engine()``) au lieu MathJax. Les méthodes ``latex.add_to_mathjax_avoid_list`` et ``latex.mathjax_avoid_list`` permettent de gérer le contenu de cette liste. :: - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] Supposons maintenant que, dans le bloc-notes, un appel à ``view()`` ou diff --git a/src/doc/ja/tutorial/latex.rst b/src/doc/ja/tutorial/latex.rst index e02d8507eb6..9f4dc85090b 100644 --- a/src/doc/ja/tutorial/latex.rst +++ b/src/doc/ja/tutorial/latex.rst @@ -280,17 +280,18 @@ LaTeX処理のカスタマイズ :: - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] diff --git a/src/doc/pt/tutorial/latex.rst b/src/doc/pt/tutorial/latex.rst index 3ed1a642ba8..56cb8e778c7 100644 --- a/src/doc/pt/tutorial/latex.rst +++ b/src/doc/pt/tutorial/latex.rst @@ -328,17 +328,18 @@ que for definido pelo comando ``latex.engine()``). Essa lista é gerenciada pelos comandos ``latex.add_to_mathjax_avoid_list`` e ``latex.mathjax_avoid_list``. :: - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] - sage: latex.mathjax_avoid_list(['foo', 'bar']) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list(['foo', 'bar']) + sage: latex.mathjax_avoid_list() ['foo', 'bar'] - sage: latex.add_to_mathjax_avoid_list('tikzpicture') # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.add_to_mathjax_avoid_list('tikzpicture') + sage: latex.mathjax_avoid_list() ['foo', 'bar', 'tikzpicture'] - sage: latex.mathjax_avoid_list([]) # not tested - sage: latex.mathjax_avoid_list() # not tested + sage: latex.mathjax_avoid_list([]) + sage: latex.mathjax_avoid_list() [] Suponha que uma expressão em LaTeX é produzida no Notebook com o diff --git a/src/sage/algebras/fusion_rings/f_matrix.py b/src/sage/algebras/fusion_rings/f_matrix.py index a8dc22901c8..e178b567a3e 100644 --- a/src/sage/algebras/fusion_rings/f_matrix.py +++ b/src/sage/algebras/fusion_rings/f_matrix.py @@ -1726,11 +1726,11 @@ def _partition_eqns(self, eqns=None, verbose=True): if eqns is None: eqns = self.ideal_basis graph = self.equations_graph(eqns) - partition = {tuple(c): [] for c in graph.connected_components()} + partition = {tuple(c): [] for c in graph.connected_components(sort=True)} for eq_tup in eqns: - partition[tuple(graph.connected_component_containing_vertex(variables(eq_tup)[0]))].append(eq_tup) + partition[tuple(graph.connected_component_containing_vertex(variables(eq_tup)[0], sort=True))].append(eq_tup) if verbose: - print("Partitioned {} equations into {} components of size:".format(len(eqns), len(graph.connected_components()))) + print("Partitioned {} equations into {} components of size:".format(len(eqns), graph.connected_components_number())) print(graph.connected_components_sizes()) return partition diff --git a/src/sage/all__sagemath_bliss.py b/src/sage/all__sagemath_bliss.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/all__sagemath_coxeter3.py b/src/sage/all__sagemath_coxeter3.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/all__sagemath_mcqd.py b/src/sage/all__sagemath_mcqd.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/all__sagemath_meataxe.py b/src/sage/all__sagemath_meataxe.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/all__sagemath_sirocco.py b/src/sage/all__sagemath_sirocco.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/all__sagemath_tdlib.py b/src/sage/all__sagemath_tdlib.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/calculus/ode.pyx b/src/sage/calculus/ode.pyx index ea26f16998a..74bac5b0811 100644 --- a/src/sage/calculus/ode.pyx +++ b/src/sage/calculus/ode.pyx @@ -324,14 +324,15 @@ class ode_solver(): following (WARNING: the following is *not* automatically doctested):: - sage: T = ode_solver() # not tested - sage: T.algorithm = "bsimp" # not tested - sage: vander = van_der_pol() # not tested - sage: T.function = vander # not tested - sage: T.ode_solve(y_0=[1, 0], t_span=[0, 2000], # not tested + sage: # not tested + sage: T = ode_solver() + sage: T.algorithm = "bsimp" + sage: vander = van_der_pol() + sage: T.function = vander + sage: T.ode_solve(y_0=[1, 0], t_span=[0, 2000], ....: num_points=1000) - sage: from tempfile import NamedTemporaryFile # not tested - sage: with NamedTemporaryFile(suffix=".png") as f: # not tested + sage: from tempfile import NamedTemporaryFile + sage: with NamedTemporaryFile(suffix=".png") as f: ....: T.plot_solution(i=0, filename=f.name) """ diff --git a/src/sage/categories/category_singleton.pyx b/src/sage/categories/category_singleton.pyx index ea8f9265033..543ce0375f7 100644 --- a/src/sage/categories/category_singleton.pyx +++ b/src/sage/categories/category_singleton.pyx @@ -135,13 +135,14 @@ class Category_singleton(Category): One sees that containment tests for the singleton class is a lot faster than for a usual class:: - sage: timeit("R in MyRings()", number=10000) # not tested + sage: # not tested + sage: timeit("R in MyRings()", number=10000) 10000 loops, best of 3: 7.12 µs per loop - sage: timeit("R1 in MyRings()", number=10000) # not tested + sage: timeit("R1 in MyRings()", number=10000) 10000 loops, best of 3: 6.98 µs per loop - sage: timeit("R in MyRingsSingleton()", number=10000) # not tested + sage: timeit("R in MyRingsSingleton()", number=10000) 10000 loops, best of 3: 3.08 µs per loop - sage: timeit("R2 in MyRingsSingleton()", number=10000) # not tested + sage: timeit("R2 in MyRingsSingleton()", number=10000) 10000 loops, best of 3: 2.99 µs per loop So this is an improvement, but not yet competitive with a pure diff --git a/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py b/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py index 3bbe54cefc0..3dff3b983a6 100644 --- a/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py +++ b/src/sage/categories/complex_reflection_or_generalized_coxeter_groups.py @@ -766,7 +766,7 @@ def irreducible_component_index_sets(self): for i,j in itertools.combinations(I,2) if s[i]*s[j] != s[j]*s[i] ]], format="vertices_and_edges") - return G.connected_components() + return G.connected_components(sort=False) @abstract_method(optional=True) def irreducible_components(self): diff --git a/src/sage/categories/lie_algebras.py b/src/sage/categories/lie_algebras.py index 04597de0308..0bd0c3185e7 100644 --- a/src/sage/categories/lie_algebras.py +++ b/src/sage/categories/lie_algebras.py @@ -45,24 +45,24 @@ class LieAlgebras(Category_over_base_ring): We construct a typical parent in this category, and do some computations with it:: - sage: A = C.example(); A # optional - sage.groups sage.modules + sage: A = C.example(); A # needs sage.groups sage.modules An example of a Lie algebra: the Lie algebra from the associative algebra Symmetric group algebra of order 3 over Rational Field generated by ([2, 1, 3], [2, 3, 1]) - sage: A.category() # optional - sage.groups sage.modules + sage: A.category() # needs sage.groups sage.modules Category of Lie algebras over Rational Field - sage: A.base_ring() # optional - sage.groups sage.modules + sage: A.base_ring() # needs sage.groups sage.modules Rational Field - sage: a, b = A.lie_algebra_generators() # optional - sage.groups sage.modules - sage: a.bracket(b) # optional - sage.groups sage.modules + sage: a, b = A.lie_algebra_generators() # needs sage.groups sage.modules + sage: a.bracket(b) # needs sage.groups sage.modules -[1, 3, 2] + [3, 2, 1] - sage: b.bracket(2*a + b) # optional - sage.groups sage.modules + sage: b.bracket(2*a + b) # needs sage.groups sage.modules 2*[1, 3, 2] - 2*[3, 2, 1] - sage: A.bracket(a, b) # optional - sage.groups sage.modules + sage: A.bracket(a, b) # needs sage.groups sage.modules -[1, 3, 2] + [3, 2, 1] Please see the source code of `A` (with ``A??``) for how to @@ -70,9 +70,9 @@ class LieAlgebras(Category_over_base_ring): TESTS:: - sage: C = LieAlgebras(QQ) # optional - sage.combinat sage.modules - sage: TestSuite(C).run() # optional - sage.combinat sage.modules - sage: TestSuite(C.example()).run() # optional - sage.combinat sage.modules + sage: C = LieAlgebras(QQ) # needs sage.combinat sage.modules + sage: TestSuite(C).run() # needs sage.combinat sage.modules + sage: TestSuite(C.example()).run() # needs sage.combinat sage.modules .. TODO:: @@ -151,15 +151,15 @@ def example(self, gens=None): EXAMPLES:: - sage: LieAlgebras(QQ).example() # optional - sage.groups sage.modules + sage: LieAlgebras(QQ).example() # needs sage.groups sage.modules An example of a Lie algebra: the Lie algebra from the associative algebra Symmetric group algebra of order 3 over Rational Field generated by ([2, 1, 3], [2, 3, 1]) Another set of generators can be specified as an optional argument:: - sage: F. = FreeAlgebra(QQ) # optional - sage.combinat sage.modules - sage: LieAlgebras(QQ).example(F.gens()) # optional - sage.combinat sage.modules + sage: F. = FreeAlgebra(QQ) # needs sage.combinat sage.modules + sage: LieAlgebras(QQ).example(F.gens()) # needs sage.combinat sage.modules An example of a Lie algebra: the Lie algebra from the associative algebra Free Algebra on 3 generators (x, y, z) over Rational Field generated by (x, y, z) @@ -189,14 +189,14 @@ def extra_super_categories(self): [Category of finite sets] sage: LieAlgebras(ZZ).FiniteDimensional().extra_super_categories() [] - sage: C = LieAlgebras(GF(5)).FiniteDimensional() # optional - sage.rings.finite_rings - sage: C.is_subcategory(Sets().Finite()) # optional - sage.rings.finite_rings + sage: C = LieAlgebras(GF(5)).FiniteDimensional() # needs sage.rings.finite_rings + sage: C.is_subcategory(Sets().Finite()) # needs sage.rings.finite_rings True sage: C = LieAlgebras(ZZ).FiniteDimensional() sage: C.is_subcategory(Sets().Finite()) False - sage: C = LieAlgebras(GF(5)).WithBasis().FiniteDimensional() # optional - sage.rings.finite_rings - sage: C.is_subcategory(Sets().Finite()) # optional - sage.rings.finite_rings + sage: C = LieAlgebras(GF(5)).WithBasis().FiniteDimensional() # needs sage.rings.finite_rings + sage: C.is_subcategory(Sets().Finite()) # needs sage.rings.finite_rings True """ if self.base_ring() in Sets().Finite(): @@ -209,8 +209,8 @@ class Nilpotent(CategoryWithAxiom_over_base_ring): TESTS:: - sage: C = LieAlgebras(QQ).Nilpotent() # optional - sage.combinat sage.modules - sage: TestSuite(C).run() # optional - sage.combinat sage.modules + sage: C = LieAlgebras(QQ).Nilpotent() # needs sage.combinat sage.modules + sage: TestSuite(C).run() # needs sage.combinat sage.modules """ class ParentMethods: @abstract_method @@ -220,8 +220,8 @@ def step(self): EXAMPLES:: - sage: h = lie_algebras.Heisenberg(ZZ, oo) # optional - sage.combinat sage.modules - sage: h.step() # optional - sage.combinat sage.modules + sage: h = lie_algebras.Heisenberg(ZZ, oo) # needs sage.combinat sage.modules + sage: h.step() # needs sage.combinat sage.modules 2 """ @@ -231,8 +231,8 @@ def is_nilpotent(self): EXAMPLES:: - sage: h = lie_algebras.Heisenberg(ZZ, oo) # optional - sage.combinat sage.modules - sage: h.is_nilpotent() # optional - sage.combinat sage.modules + sage: h = lie_algebras.Heisenberg(ZZ, oo) # needs sage.combinat sage.modules + sage: h.is_nilpotent() # needs sage.combinat sage.modules True """ return True @@ -257,30 +257,31 @@ def bracket(self, lhs, rhs): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x, y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.bracket(x, x + y) # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() + sage: x, y = L.lie_algebra_generators() + sage: L.bracket(x, x + y) -[1, 3, 2] + [3, 2, 1] - sage: L.bracket(x, 0) # optional - sage.combinat sage.modules + sage: L.bracket(x, 0) 0 - sage: L.bracket(0, x) # optional - sage.combinat sage.modules + sage: L.bracket(0, x) 0 Constructing the product space:: - sage: L = lie_algebras.Heisenberg(QQ, 1) # optional - sage.combinat sage.modules - sage: Z = L.bracket(L, L); Z # optional - sage.combinat sage.modules + sage: L = lie_algebras.Heisenberg(QQ, 1) # needs sage.combinat sage.modules + sage: Z = L.bracket(L, L); Z # needs sage.combinat sage.modules Ideal (z) of Heisenberg algebra of rank 1 over Rational Field - sage: L.bracket(L, Z) # optional - sage.combinat sage.modules + sage: L.bracket(L, Z) # needs sage.combinat sage.modules Ideal () of Heisenberg algebra of rank 1 over Rational Field Constructing ideals:: - sage: p, q, z = L.basis(); p, q, z # optional - sage.combinat sage.modules + sage: p, q, z = L.basis(); p, q, z # needs sage.combinat sage.modules (p1, q1, z) - sage: L.bracket(3*p, L) # optional - sage.combinat sage.modules + sage: L.bracket(3*p, L) # needs sage.combinat sage.modules Ideal (3*p1) of Heisenberg algebra of rank 1 over Rational Field - sage: L.bracket(L, q + p) # optional - sage.combinat sage.modules + sage: L.bracket(L, q + p) # needs sage.combinat sage.modules Ideal (p1 + q1) of Heisenberg algebra of rank 1 over Rational Field """ if lhs in LieAlgebras: @@ -300,15 +301,15 @@ def universal_enveloping_algebra(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L.universal_enveloping_algebra() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L.universal_enveloping_algebra() # needs sage.combinat sage.modules Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} :: - sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) # optional - sage.combinat sage.modules - sage: L.universal_enveloping_algebra() # optional - sage.combinat sage.modules + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) # needs sage.combinat sage.modules + sage: L.universal_enveloping_algebra() # needs sage.combinat sage.modules Multivariate Polynomial Ring in x0, x1, x2 over Rational Field .. SEEALSO:: @@ -333,15 +334,15 @@ def _construct_UEA(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L._construct_UEA() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L._construct_UEA() # needs sage.combinat sage.modules Noncommutative Multivariate Polynomial Ring in b0, b1, b2 over Rational Field, nc-relations: {} :: - sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) # optional - sage.combinat sage.modules - sage: L.universal_enveloping_algebra() # indirect doctest # optional - sage.combinat sage.modules + sage: L = LieAlgebra(QQ, 3, 'x', abelian=True) # needs sage.combinat sage.modules + sage: L.universal_enveloping_algebra() # indirect doctest # needs sage.combinat sage.modules Multivariate Polynomial Ring in x0, x1, x2 over Rational Field """ @@ -397,8 +398,8 @@ def module(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L.module() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L.module() # needs sage.combinat sage.modules Vector space of dimension 3 over Rational Field """ @@ -413,10 +414,10 @@ def from_vector(self, v, order=None, coerce=False): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: u = L.from_vector(vector(QQ, (1, 0, 0))); u # needs sage.combinat sage.modules (1, 0, 0) - sage: parent(u) is L # optional - sage.combinat sage.modules + sage: parent(u) is L # needs sage.combinat sage.modules True """ @@ -433,11 +434,12 @@ def lift(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: lifted = L.lift(2*a + b - c); lifted # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: lifted = L.lift(2*a + b - c); lifted 2*b0 + b1 - b2 - sage: lifted.parent() is L.universal_enveloping_algebra() # optional - sage.combinat sage.modules + sage: lifted.parent() is L.universal_enveloping_algebra() True """ M = LiftMorphism(self, self._construct_UEA()) @@ -450,9 +452,9 @@ def subalgebra(self, gens, names=None, index_set=None, category=None): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.subalgebra([2*a - c, b + c]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.subalgebra([2*a - c, b + c]) # needs sage.combinat sage.modules An example of a finite dimensional Lie algebra with basis: the 2-dimensional abelian Lie algebra over Rational Field with basis matrix: @@ -461,9 +463,9 @@ def subalgebra(self, gens, names=None, index_set=None, category=None): :: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.subalgebra([x + y]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: x,y = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.subalgebra([x + y]) # needs sage.combinat sage.modules Traceback (most recent call last): ... NotImplementedError: subalgebras not yet implemented: see #17416 @@ -478,9 +480,9 @@ def ideal(self, *gens, **kwds): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.ideal([2*a - c, b + c]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.ideal([2*a - c, b + c]) # needs sage.combinat sage.modules An example of a finite dimensional Lie algebra with basis: the 2-dimensional abelian Lie algebra over Rational Field with basis matrix: @@ -489,9 +491,9 @@ def ideal(self, *gens, **kwds): :: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x, y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.ideal([x + y]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: x, y = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.ideal([x + y]) # needs sage.combinat sage.modules Traceback (most recent call last): ... NotImplementedError: ideals not yet implemented: see #16824 @@ -511,8 +513,8 @@ def is_ideal(self, A): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L.is_ideal(L) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L.is_ideal(L) # needs sage.combinat sage.modules True """ if A == self: @@ -528,9 +530,9 @@ def killing_form(self, x, y): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.killing_form(a, b + c) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.killing_form(a, b + c) # needs sage.combinat sage.modules 0 """ @@ -543,21 +545,23 @@ def is_abelian(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L.is_abelian() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() + sage: L.is_abelian() False - sage: R = QQ['x,y'] # optional - sage.combinat sage.modules - sage: L = LieAlgebras(QQ).example(R.gens()) # optional - sage.combinat sage.modules - sage: L.is_abelian() # optional - sage.combinat sage.modules + sage: R = QQ['x,y'] + sage: L = LieAlgebras(QQ).example(R.gens()) + sage: L.is_abelian() True :: - sage: L. = LieAlgebra(QQ, 1) # todo: not implemented - #16823 # optional - sage.combinat sage.modules - sage: L.is_abelian() # todo: not implemented - #16823 # optional - sage.combinat sage.modules + sage: # not implemented, needs sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, 1) + sage: L.is_abelian() True - sage: L. = LieAlgebra(QQ, 2) # todo: not implemented - #16823 # optional - sage.combinat sage.modules - sage: L.is_abelian() # todo: not implemented - #16823 # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, 2) + sage: L.is_abelian() False """ G = self.lie_algebra_generators() @@ -573,14 +577,14 @@ def is_commutative(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L.is_commutative() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L.is_commutative() # needs sage.combinat sage.modules False :: - sage: L. = LieAlgebra(QQ, 1) # todo: not implemented - #16823 # optional - sage.combinat sage.modules - sage: L.is_commutative() # todo: not implemented - #16823 # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, 1) # not implemented # needs sage.combinat sage.modules + sage: L.is_commutative() # not implemented # needs sage.combinat sage.modules True """ return self.is_abelian() @@ -592,8 +596,8 @@ def is_solvable(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L.is_solvable() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L.is_solvable() # needs sage.combinat sage.modules True """ @@ -604,8 +608,8 @@ def is_nilpotent(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: L.is_nilpotent() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: L.is_nilpotent() # needs sage.combinat sage.modules True """ @@ -630,36 +634,36 @@ def bch(self, X, Y, prec=None): The BCH formula for the generators of a free nilpotent Lie algebra of step 4:: - sage: L = LieAlgebra(QQ, 2, step=4) # optional - sage.combinat sage.modules - sage: L.inject_variables() # optional - sage.combinat sage.modules + sage: L = LieAlgebra(QQ, 2, step=4) # needs sage.combinat sage.modules + sage: L.inject_variables() # needs sage.combinat sage.modules Defining X_1, X_2, X_12, X_112, X_122, X_1112, X_1122, X_1222 - sage: L.bch(X_1, X_2) # optional - sage.combinat sage.modules + sage: L.bch(X_1, X_2) # needs sage.combinat sage.modules X_1 + X_2 + 1/2*X_12 + 1/12*X_112 + 1/12*X_122 + 1/24*X_1122 An example of the BCH formula in a quotient:: - sage: Q = L.quotient(X_112 + X_122) # optional - sage.combinat sage.modules - sage: x, y = Q.basis().list()[:2] # optional - sage.combinat sage.modules - sage: Q.bch(x, y) # optional - sage.combinat sage.modules + sage: Q = L.quotient(X_112 + X_122) # needs sage.combinat sage.modules + sage: x, y = Q.basis().list()[:2] # needs sage.combinat sage.modules + sage: Q.bch(x, y) # needs sage.combinat sage.modules X_1 + X_2 + 1/2*X_12 - 1/24*X_1112 The BCH formula for a non-nilpotent Lie algebra requires the precision to be explicitly stated:: - sage: L. = LieAlgebra(QQ) # optional - sage.combinat sage.modules - sage: L.bch(X, Y) # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(QQ) # needs sage.combinat sage.modules + sage: L.bch(X, Y) # needs sage.combinat sage.modules Traceback (most recent call last): ... ValueError: the Lie algebra is not known to be nilpotent, so you must specify the precision - sage: L.bch(X, Y, 4) # optional - sage.combinat sage.modules + sage: L.bch(X, Y, 4) # needs sage.combinat sage.modules X + 1/12*[X, [X, Y]] + 1/24*[X, [[X, Y], Y]] + 1/2*[X, Y] + 1/12*[[X, Y], Y] + Y The BCH formula requires a coercion from the rationals:: - sage: L. = LieAlgebra(ZZ, 2, step=2) # optional - sage.combinat sage.modules - sage: L.bch(X, Y) # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(ZZ, 2, step=2) # needs sage.combinat sage.modules + sage: L.bch(X, Y) # needs sage.combinat sage.modules Traceback (most recent call last): ... TypeError: the BCH formula is not well defined @@ -688,8 +692,8 @@ def lie_group(self, name='G', **kwds): EXAMPLES:: - sage: L = lie_algebras.Heisenberg(QQ, 1) # optional - sage.combinat sage.modules - sage: G = L.lie_group('G'); G # optional - sage.combinat sage.modules sage.symbolic + sage: L = lie_algebras.Heisenberg(QQ, 1) # needs sage.combinat sage.modules + sage: G = L.lie_group('G'); G # needs sage.combinat sage.modules sage.symbolic Lie group G of Heisenberg algebra of rank 1 over Rational Field """ @@ -707,15 +711,15 @@ def _test_jacobi_identity(self, **options): By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L._test_jacobi_identity() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L._test_jacobi_identity() # needs sage.combinat sage.modules However, the elements tested can be customized with the ``elements`` keyword argument:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L._test_jacobi_identity(elements=[x + y, x, 2*y, x.bracket(y)]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: x,y = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L._test_jacobi_identity(elements=[x + y, x, 2*y, x.bracket(y)]) # needs sage.combinat sage.modules See the documentation for :class:`TestSuite` for more information. """ @@ -746,15 +750,15 @@ def _test_antisymmetry(self, **options): By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L._test_antisymmetry() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L._test_antisymmetry() # needs sage.combinat sage.modules However, the elements tested can be customized with the ``elements`` keyword argument:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L._test_antisymmetry(elements=[x + y, x, 2*y, x.bracket(y)]) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: x,y = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L._test_antisymmetry(elements=[x + y, x, 2*y, x.bracket(y)]) # needs sage.combinat sage.modules See the documentation for :class:`TestSuite` for more information. """ @@ -775,27 +779,28 @@ def _test_distributivity(self, **options): TESTS:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: L._test_distributivity() # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() # needs sage.combinat sage.modules + sage: L._test_distributivity() # needs sage.combinat sage.modules EXAMPLES: By default, this method runs the tests only on the elements returned by ``self.some_elements()``:: - sage: L = LieAlgebra(QQ, 3, 'x,y,z', representation="polynomial") # optional - sage.combinat sage.modules - sage: L.some_elements() # optional - sage.combinat sage.modules + sage: L = LieAlgebra(QQ, 3, 'x,y,z', representation="polynomial") # needs sage.combinat sage.modules + sage: L.some_elements() # needs sage.combinat sage.modules [x + y + z] - sage: L._test_distributivity() # optional - sage.combinat sage.modules + sage: L._test_distributivity() # needs sage.combinat sage.modules However, the elements tested can be customized with the ``elements`` keyword argument:: - sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) # todo: not implemented - #16821 # optional - sage.combinat sage.modules - sage: h1 = L.gen(0) # todo: not implemented - #16821 # optional - sage.combinat sage.modules - sage: h2 = L.gen(1) # todo: not implemented - #16821 # optional - sage.combinat sage.modules - sage: e2 = L.gen(3) # todo: not implemented - #16821 # optional - sage.combinat sage.modules - sage: L._test_distributivity(elements=[h1, h2, e2]) # todo: not implemented - #16821 # optional - sage.combinat sage.modules + sage: # not implemented, needs sage.combinat sage.modules + sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) + sage: h1 = L.gen(0) + sage: h2 = L.gen(1) + sage: e2 = L.gen(3) + sage: L._test_distributivity(elements=[h1, h2, e2]) See the documentation for :class:`TestSuite` for more information. """ @@ -818,11 +823,12 @@ def bracket(self, rhs): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: x.bracket(y) # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x.bracket(y) -[1, 3, 2] + [3, 2, 1] - sage: x.bracket(0) # optional - sage.combinat sage.modules + sage: x.bracket(0) 0 """ return self._bracket_(rhs) @@ -837,13 +843,14 @@ def _bracket_(self, y): EXAMPLES:: - sage: L = LieAlgebras(QQ).example() # optional - sage.combinat sage.modules - sage: x,y = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: x._bracket_(y) # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).example() + sage: x,y = L.lie_algebra_generators() + sage: x._bracket_(y) -[1, 3, 2] + [3, 2, 1] - sage: y._bracket_(x) # optional - sage.combinat sage.modules + sage: y._bracket_(x) [1, 3, 2] - [3, 2, 1] - sage: x._bracket_(x) # optional - sage.combinat sage.modules + sage: x._bracket_(x) 0 """ @@ -859,10 +866,10 @@ def to_vector(self, order=None): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: u = L((1, 0, 0)).to_vector(); u # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: u = L((1, 0, 0)).to_vector(); u # needs sage.combinat sage.modules (1, 0, 0) - sage: parent(u) # optional - sage.combinat sage.modules + sage: parent(u) # needs sage.combinat sage.modules Vector space of dimension 3 over Rational Field """ @@ -874,16 +881,17 @@ def lift(self): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: elt = 3*a + b - c # optional - sage.combinat sage.modules - sage: elt.lift() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() + sage: a, b, c = L.lie_algebra_generators() + sage: elt = 3*a + b - c + sage: elt.lift() 3*b0 + b1 - b2 :: - sage: L. = LieAlgebra(QQ, abelian=True) # optional - sage.combinat sage.modules - sage: x.lift() # optional - sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, abelian=True) # needs sage.combinat sage.modules + sage: x.lift() # needs sage.combinat sage.modules x """ @@ -893,9 +901,9 @@ def killing_form(self, x): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: a.killing_form(b) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: a.killing_form(b) # needs sage.combinat sage.modules 0 """ return self.parent().killing_form(self, x) @@ -912,26 +920,28 @@ def exp(self, lie_group=None): EXAMPLES:: - sage: L. = LieAlgebra(QQ, 2, step=2) # optional - sage.combinat sage.modules - sage: g = (X + Y + Z).exp(); g # optional - sage.combinat sage.modules sage.symbolic + sage: # needs sage.combinat sage.modules + sage: L. = LieAlgebra(QQ, 2, step=2) + sage: g = (X + Y + Z).exp(); g # needs sage.symbolic exp(X + Y + Z) - sage: h = X.exp(); h # optional - sage.combinat sage.modules sage.symbolic + sage: h = X.exp(); h # needs sage.symbolic exp(X) - sage: g.parent() # optional - sage.combinat sage.modules sage.symbolic + sage: g.parent() # needs sage.symbolic Lie group G of Free Nilpotent Lie algebra on 3 generators (X, Y, Z) over Rational Field - sage: g.parent() is h.parent() # optional - sage.combinat sage.modules sage.symbolic + sage: g.parent() is h.parent() # needs sage.symbolic True The Lie group can be specified explicitly:: - sage: H = L.lie_group('H') # optional - sage.combinat sage.modules sage.symbolic - sage: k = Z.exp(lie_group=H); k # optional - sage.combinat sage.modules sage.symbolic + sage: # needs sage.combinat sage.modules sage.symbolic + sage: H = L.lie_group('H') + sage: k = Z.exp(lie_group=H); k exp(Z) - sage: k.parent() # optional - sage.combinat sage.modules sage.symbolic + sage: k.parent() Lie group H of Free Nilpotent Lie algebra on 3 generators (X, Y, Z) over Rational Field - sage: g.parent() == k.parent() # optional - sage.combinat sage.modules sage.symbolic + sage: g.parent() == k.parent() False """ if lie_group is None: @@ -949,13 +959,13 @@ def __init__(self, domain, codomain): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: f = L.lift # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: f = L.lift # needs sage.combinat sage.modules We skip the category test since this is currently not an element of a homspace:: - sage: TestSuite(f).run(skip="_test_category") # optional - sage.combinat sage.modules + sage: TestSuite(f).run(skip="_test_category") # needs sage.combinat sage.modules """ Morphism.__init__(self, Hom(domain, codomain)) @@ -965,9 +975,9 @@ def _call_(self, x): EXAMPLES:: - sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # optional - sage.combinat sage.modules - sage: a, b, c = L.lie_algebra_generators() # optional - sage.combinat sage.modules - sage: L.lift(3*a + b - c) # optional - sage.combinat sage.modules + sage: L = LieAlgebras(QQ).FiniteDimensional().WithBasis().example() # needs sage.combinat sage.modules + sage: a, b, c = L.lie_algebra_generators() # needs sage.combinat sage.modules + sage: L.lift(3*a + b - c) # needs sage.combinat sage.modules 3*b0 + b1 - b2 """ return x.lift() diff --git a/src/sage/categories/loop_crystals.py b/src/sage/categories/loop_crystals.py index 2d5f1ca1914..83c403323cd 100644 --- a/src/sage/categories/loop_crystals.py +++ b/src/sage/categories/loop_crystals.py @@ -868,7 +868,7 @@ def one_dimensional_configuration_sum(self, q=None, group_components=True): B = P0.algebra(q.parent()) if group_components: G = self.digraph(index_set=self.cartan_type().classical().index_set()) - C = G.connected_components() + C = G.connected_components(sort=False) return B.sum(q**(c[0].energy_function())*B.sum(B(P0(b.weight())) for b in c) for c in C) return B.sum(q**(b.energy_function())*B(P0(b.weight())) for b in self) diff --git a/src/sage/categories/posets.py b/src/sage/categories/posets.py index 24a83e02dbd..47408110da2 100644 --- a/src/sage/categories/posets.py +++ b/src/sage/categories/posets.py @@ -71,13 +71,14 @@ class Posets(Category): :trac:`10130` will be resolved, this will be automatically provided by this category:: - sage: x < y # todo: not implemented + sage: # not implemented + sage: x < y True - sage: x < x # todo: not implemented + sage: x < x False - sage: x <= x # todo: not implemented + sage: x <= x True - sage: y >= x # todo: not implemented + sage: y >= x True .. SEEALSO:: :func:`Poset`, :class:`FinitePosets`, :class:`LatticePosets` diff --git a/src/sage/coding/guruswami_sudan/gs_decoder.py b/src/sage/coding/guruswami_sudan/gs_decoder.py index ce92530637d..3021cbbfab6 100644 --- a/src/sage/coding/guruswami_sudan/gs_decoder.py +++ b/src/sage/coding/guruswami_sudan/gs_decoder.py @@ -1,4 +1,4 @@ -# sage.doctest: optional - sage.modules sage.rings.finite_rings +# sage.doctest: needs sage.modules sage.rings.finite_rings r""" Guruswami-Sudan decoder for (Generalized) Reed-Solomon codes @@ -754,7 +754,7 @@ def list_size(self): def decode_to_message(self, r): r""" - Decodes ``r`` to the list of polynomials whose encoding by + Decode ``r`` to the list of polynomials whose encoding by :meth:`self.code()` is within Hamming distance :meth:`self.decoding_radius` of ``r``. @@ -795,7 +795,7 @@ def decode_to_message(self, r): Traceback (most recent call last): ... ValueError: The provided root-finding algorithm has a wrong signature. - See the documentation of `GSD.rootfinding_algorithm()` for details + See the documentation of `...rootfinding_algorithm()` for details """ return [self.connected_encoder().unencode(c) for c in self.decode_to_code(r)] diff --git a/src/sage/coding/linear_rank_metric.py b/src/sage/coding/linear_rank_metric.py index 01e1b9bb4f8..2b687775102 100644 --- a/src/sage/coding/linear_rank_metric.py +++ b/src/sage/coding/linear_rank_metric.py @@ -391,7 +391,7 @@ def __init__(self, base_field, sub_field, length, default_encoder_name, sage: from sage.coding.linear_rank_metric import AbstractLinearRankMetricCode sage: class RankRepetitionCode(AbstractLinearRankMetricCode): ....: def __init__(self, base_field, sub_field, length): - ....: super().__init__(self, base_field, sub_field, length, + ....: super().__init__(base_field, sub_field, length, ....: "GeneratorMatrix", "NearestNeighbor") ....: beta = base_field.gen() ....: self._generator_matrix = matrix(base_field, diff --git a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py index fc414b8f8fb..8707841b137 100644 --- a/src/sage/combinat/cluster_algebra_quiver/mutation_type.py +++ b/src/sage/combinat/cluster_algebra_quiver/mutation_type.py @@ -896,7 +896,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): dg_tmp = DiGraph( dg ) dg_tmp.delete_vertices( c1 ) - components = dg_tmp.connected_components() + components = dg_tmp.connected_components(sort=False) # if not len(components) == 2: if len(components) != 2: return _false_return(4) @@ -938,7 +938,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): else: c2.reverse() dg_tmp.delete_edge( tuple( c2 ) ) - components = dg_tmp.connected_components() + components = dg_tmp.connected_components(sort=False) if len(components) != 2: return _false_return(7) else: @@ -1190,7 +1190,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): edge = long_cycle[0][0] sg = DiGraph( dg ) sg. delete_vertices(edge) - connected_components = sg.connected_components() + connected_components = sg.connected_components(sort=False) cycle = [] if connected_components: cycle.append( ( edge[0], edge[1], len( connected_components[0] ) + 1 ) ) @@ -1200,7 +1200,7 @@ def _connected_mutation_type_AAtildeD(dg, ret_conn_vert=False): for edge in tmp: sg = DiGraph( dg ) sg. delete_vertices(edge) - connected_components = sg.connected_components() + connected_components = sg.connected_components(sort=False) if len( connected_components ) == 2: #if len( list_intersection( [ connected_components[0], list_substract( long_cycle[0], [edge] )[0] ] ) ) > 0: if len( set(connected_components[0]).intersection( set(long_cycle[0]).difference([edge]).pop() ) ) > 0: diff --git a/src/sage/combinat/cluster_algebra_quiver/quiver.py b/src/sage/combinat/cluster_algebra_quiver/quiver.py index 7b2ad09b1b2..dff95dcef24 100644 --- a/src/sage/combinat/cluster_algebra_quiver/quiver.py +++ b/src/sage/combinat/cluster_algebra_quiver/quiver.py @@ -1007,7 +1007,7 @@ def mutation_type(self): # checking the type for each connected component mutation_type = [] - connected_components = sorted(dg.connected_components()) + connected_components = sorted(dg.connected_components(sort=False)) for component in connected_components: # constructing the digraph for this component dg_component = dg.subgraph( component ) @@ -1157,7 +1157,7 @@ def canonical_label(self, certificate=False): if dg.is_connected(): Q._mutation_type = self._mutation_type else: - CC = sorted( self._digraph.connected_components() ) + CC = sorted(self._digraph.connected_components(sort=False)) CC_new = sorted(zip([sorted(iso[i] for i in L) for L in CC], range(len(CC)))) comp_iso = [L[1] for L in CC_new] diff --git a/src/sage/combinat/constellation.py b/src/sage/combinat/constellation.py index 0f5350204aa..8f7274cc256 100644 --- a/src/sage/combinat/constellation.py +++ b/src/sage/combinat/constellation.py @@ -475,7 +475,7 @@ def connected_components(self): G.add_vertices(list(range(self.degree()))) for p in self._g: G.add_edges(enumerate(p.domain()), loops=False) - m = G.connected_components() + m = G.connected_components(sort=False) if len(m) == 1: return [self] for mm in m: diff --git a/src/sage/combinat/crystals/littelmann_path.py b/src/sage/combinat/crystals/littelmann_path.py index 1af0fed2dcb..c384a818e56 100644 --- a/src/sage/combinat/crystals/littelmann_path.py +++ b/src/sage/combinat/crystals/littelmann_path.py @@ -809,7 +809,7 @@ def weight(x): return P0.sum(int(c)*P0.basis()[i] for i,c in w if i in P0.index_set()) if group_components: G = self.digraph(index_set=self.cartan_type().classical().index_set()) - C = G.connected_components() + C = G.connected_components(sort=False) return sum(q**(c[0].energy_function())*B.sum(B(weight(b)) for b in c) for c in C) return B.sum(q**(b.energy_function())*B(weight(b)) for b in self) @@ -1081,7 +1081,7 @@ def energy_function(self): sage: La = R.weight_space().basis() sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]+La[2]) sage: G = LS.digraph(index_set=[1,2]) - sage: C = G.connected_components() + sage: C = G.connected_components(sort=False) sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] [True, True, True, True] @@ -1093,7 +1093,7 @@ def energy_function(self): sage: [(x.weight(), x.energy_function()) for x in hw] [(-2*Lambda[0] + Lambda[2], 0), (-2*Lambda[0] + Lambda[1], 1), (0, 2)] sage: G = LS.digraph(index_set=J) - sage: C = G.connected_components() + sage: C = G.connected_components(sort=False) sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] [True, True, True] @@ -1101,7 +1101,7 @@ def energy_function(self): sage: La = R.weight_space().basis() sage: LS = crystals.ProjectedLevelZeroLSPaths(La[1]+La[2]) sage: G = LS.digraph(index_set=[1,2]) - sage: C = G.connected_components() + sage: C = G.connected_components(sort=False) sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] @@ -1110,7 +1110,7 @@ def energy_function(self): sage: La = R.weight_space().basis() sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]+La[2]) sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set()) - sage: C = G.connected_components() + sage: C = G.connected_components(sort=False) sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time [True, True, True, True, True, True, True, True, True, True, True] @@ -1118,7 +1118,7 @@ def energy_function(self): sage: La = R.weight_space().basis() sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1]+La[2]) sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set()) - sage: C = G.connected_components() + sage: C = G.connected_components(sort=False) sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] diff --git a/src/sage/combinat/designs/bibd.py b/src/sage/combinat/designs/bibd.py index 58e459532b6..e484070c4e8 100644 --- a/src/sage/combinat/designs/bibd.py +++ b/src/sage/combinat/designs/bibd.py @@ -4,7 +4,7 @@ This module gathers everything related to Balanced Incomplete Block Designs. One can build a BIBD (or check that it can be built) with :func:`balanced_incomplete_block_design`:: - sage: BIBD = designs.balanced_incomplete_block_design(7,3,1) + sage: BIBD = designs.balanced_incomplete_block_design(7,3,1) # needs sage.schemes In particular, Sage can build a `(v,k,1)`-BIBD when one exists for all `k\leq 5`. The following functions are available: @@ -75,12 +75,12 @@ def biplane(n, existence=False): - ``existence`` (boolean) -- instead of building the design, return: - - ``True`` -- meaning that Sage knows how to build the design + - ``True`` -- meaning that Sage knows how to build the design - - ``Unknown`` -- meaning that Sage does not know how to build the - design, but that the design may exist (see :mod:`sage.misc.unknown`). + - ``Unknown`` -- meaning that Sage does not know how to build the + design, but that the design may exist (see :mod:`sage.misc.unknown`). - - ``False`` -- meaning that the design does not exist. + - ``False`` -- meaning that the design does not exist. .. SEEALSO:: @@ -88,21 +88,22 @@ def biplane(n, existence=False): EXAMPLES:: - sage: designs.biplane(4) # optional - sage.rings.finite_rings + sage: designs.biplane(4) # needs sage.rings.finite_rings (16,6,2)-Balanced Incomplete Block Design - sage: designs.biplane(7, existence=True) + sage: designs.biplane(7, existence=True) # needs sage.schemes True - sage: designs.biplane(11) + sage: designs.biplane(11) # needs sage.schemes (79,13,2)-Balanced Incomplete Block Design TESTS:: - sage: designs.biplane(9) + sage: designs.biplane(9) # needs sage.libs.gap (56,11,2)-Balanced Incomplete Block Design Check all known biplanes:: - sage: [n for n in [0,1,2,3,4,7,9,11] if designs.biplane(n, existence=True) is True] + sage: [n for n in [0,1,2,3,4,7,9,11] # needs sage.schemes + ....: if designs.biplane(n, existence=True) is True] [0, 1, 2, 3, 4, 7, 9, 11] """ k = n+2 @@ -153,9 +154,10 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa EXAMPLES:: - sage: designs.balanced_incomplete_block_design(7, 3, 1).blocks() + sage: designs.balanced_incomplete_block_design(7, 3, 1).blocks() # needs sage.schemes [[0, 1, 3], [0, 2, 4], [0, 5, 6], [1, 2, 6], [1, 4, 5], [2, 3, 5], [3, 4, 6]] - sage: B = designs.balanced_incomplete_block_design(66, 6, 1, use_LJCR=True) # optional - internet + sage: B = designs.balanced_incomplete_block_design(66, 6, 1, # optional - internet + ....: use_LJCR=True) sage: B # optional - internet (66,6,1)-Balanced Incomplete Block Design sage: B.blocks() # optional - internet @@ -169,11 +171,11 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa sage: designs.balanced_incomplete_block_design(85,5,existence=True) True - sage: _ = designs.balanced_incomplete_block_design(85,5) + sage: _ = designs.balanced_incomplete_block_design(85,5) # needs sage.libs.pari A BIBD from a Finite Projective Plane:: - sage: _ = designs.balanced_incomplete_block_design(21,5) + sage: _ = designs.balanced_incomplete_block_design(21,5) # needs sage.schemes Some trivial BIBD:: @@ -184,22 +186,23 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa Existence of BIBD with `k=3,4,5`:: - sage: [v for v in range(50) if designs.balanced_incomplete_block_design(v,3,existence=True)] + sage: [v for v in range(50) if designs.balanced_incomplete_block_design(v,3,existence=True)] # needs sage.schemes [1, 3, 7, 9, 13, 15, 19, 21, 25, 27, 31, 33, 37, 39, 43, 45, 49] - sage: [v for v in range(100) if designs.balanced_incomplete_block_design(v,4,existence=True)] + sage: [v for v in range(100) if designs.balanced_incomplete_block_design(v,4,existence=True)] # needs sage.schemes [1, 4, 13, 16, 25, 28, 37, 40, 49, 52, 61, 64, 73, 76, 85, 88, 97] - sage: [v for v in range(150) if designs.balanced_incomplete_block_design(v,5,existence=True)] + sage: [v for v in range(150) if designs.balanced_incomplete_block_design(v,5,existence=True)] # needs sage.schemes [1, 5, 21, 25, 41, 45, 61, 65, 81, 85, 101, 105, 121, 125, 141, 145] For `k > 5` there are currently very few constructions:: - sage: [v for v in range(300) if designs.balanced_incomplete_block_design(v,6,existence=True) is True] + sage: [v for v in range(300) if designs.balanced_incomplete_block_design(v,6,existence=True) is True] # needs sage.schemes [1, 6, 31, 66, 76, 91, 96, 106, 111, 121, 126, 136, 141, 151, 156, 171, 181, 186, 196, 201, 211, 241, 271] - sage: [v for v in range(300) if designs.balanced_incomplete_block_design(v,6,existence=True) is Unknown] + sage: [v for v in range(300) if designs.balanced_incomplete_block_design(v,6,existence=True) is Unknown] # needs sage.schemes [51, 61, 81, 166, 216, 226, 231, 246, 256, 261, 276, 286, 291] Here are some constructions with `k \geq 7` and `v` a prime power:: + sage: # needs sage.libs.pari sage: designs.balanced_incomplete_block_design(169,7) (169,7,1)-Balanced Incomplete Block Design sage: designs.balanced_incomplete_block_design(617,8) @@ -218,22 +221,22 @@ def balanced_incomplete_block_design(v, k, lambd=1, existence=False, use_LJCR=Fa sage: designs.balanced_incomplete_block_design(176, 50, 14, existence=True) True - sage: designs.balanced_incomplete_block_design(64,28,12) + sage: designs.balanced_incomplete_block_design(64,28,12) # needs sage.libs.pari (64,28,12)-Balanced Incomplete Block Design - sage: designs.balanced_incomplete_block_design(37,9,8) + sage: designs.balanced_incomplete_block_design(37,9,8) # needs sage.libs.pari (37,9,8)-Balanced Incomplete Block Design - sage: designs.balanced_incomplete_block_design(15,7,3) + sage: designs.balanced_incomplete_block_design(15,7,3) # needs sage.schemes (15,7,3)-Balanced Incomplete Block Design Some BIBDs from the recursive construction :: - sage: designs.balanced_incomplete_block_design(76,16,4) + sage: designs.balanced_incomplete_block_design(76,16,4) # needs sage.libs.pari (76,16,4)-Balanced Incomplete Block Design - sage: designs.balanced_incomplete_block_design(10,4,2) + sage: designs.balanced_incomplete_block_design(10,4,2) # needs sage.libs.pari (10,4,2)-Balanced Incomplete Block Design - sage: designs.balanced_incomplete_block_design(50,25,24) + sage: designs.balanced_incomplete_block_design(50,25,24) # needs sage.schemes (50,25,24)-Balanced Incomplete Block Design - sage: designs.balanced_incomplete_block_design(29,15,15) + sage: designs.balanced_incomplete_block_design(29,15,15) # needs sage.libs.pari (29,15,15)-Balanced Incomplete Block Design """ # Trivial BIBD @@ -387,15 +390,15 @@ def BruckRyserChowla_check(v, k, lambd): Nonexistence of projective planes of order 6 and 14 sage: from sage.combinat.designs.bibd import BruckRyserChowla_check - sage: BruckRyserChowla_check(43,7,1) + sage: BruckRyserChowla_check(43,7,1) # needs sage.schemes False - sage: BruckRyserChowla_check(211,15,1) + sage: BruckRyserChowla_check(211,15,1) # needs sage.schemes False Existence of symmetric BIBDs with parameters `(79,13,2)` and `(56,11,2)` sage: from sage.combinat.designs.bibd import BruckRyserChowla_check - sage: BruckRyserChowla_check(79,13,2) + sage: BruckRyserChowla_check(79,13,2) # needs sage.schemes True sage: BruckRyserChowla_check(56,11,2) True @@ -413,7 +416,7 @@ def BruckRyserChowla_check(v, k, lambd): Clearly wrong parameters satisfying the theorem:: sage: from sage.combinat.designs.bibd import BruckRyserChowla_check - sage: BruckRyserChowla_check(13,25,50) + sage: BruckRyserChowla_check(13,25,50) # needs sage.schemes True """ @@ -569,23 +572,23 @@ def BIBD_from_TD(v,k,existence=False): First construction:: sage: from sage.combinat.designs.bibd import BIBD_from_TD - sage: BIBD_from_TD(25,5,existence=True) + sage: BIBD_from_TD(25,5,existence=True) # needs sage.schemes True - sage: _ = BlockDesign(25,BIBD_from_TD(25,5)) + sage: _ = BlockDesign(25,BIBD_from_TD(25,5)) # needs sage.schemes Second construction:: sage: from sage.combinat.designs.bibd import BIBD_from_TD - sage: BIBD_from_TD(21,5,existence=True) + sage: BIBD_from_TD(21,5,existence=True) # needs sage.schemes True - sage: _ = BlockDesign(21,BIBD_from_TD(21,5)) + sage: _ = BlockDesign(21,BIBD_from_TD(21,5)) # needs sage.schemes Third construction:: sage: from sage.combinat.designs.bibd import BIBD_from_TD - sage: BIBD_from_TD(85,5,existence=True) + sage: BIBD_from_TD(85,5,existence=True) # needs sage.schemes True - sage: _ = BlockDesign(85,BIBD_from_TD(85,5)) + sage: _ = BlockDesign(85,BIBD_from_TD(85,5)) # needs sage.schemes No idea:: @@ -866,8 +869,8 @@ def BIBD_from_PBD(PBD, v, k, check=True, base_cases=None): sage: from sage.combinat.designs.bibd import PBD_4_5_8_9_12 sage: from sage.combinat.designs.bibd import BIBD_from_PBD sage: from sage.combinat.designs.bibd import is_pairwise_balanced_design - sage: PBD = PBD_4_5_8_9_12(17) - sage: bibd = is_pairwise_balanced_design(BIBD_from_PBD(PBD,52,4),52,[4]) + sage: PBD = PBD_4_5_8_9_12(17) # needs sage.schemes + sage: bibd = is_pairwise_balanced_design(BIBD_from_PBD(PBD,52,4),52,[4]) # needs sage.schemes """ if base_cases is None: base_cases = {} @@ -904,11 +907,11 @@ def _relabel_bibd(B,n,p=None): - ``n`` (integer) -- number of points. - - ``p`` (optional) -- the point that will be labeled with n-1. + - ``p`` (optional) -- the point that will be labeled with `n-1`. EXAMPLES:: - sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest + sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest # needs sage.schemes [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10], [0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28], [0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34], @@ -950,7 +953,7 @@ def PBD_4_5_8_9_12(v, check=True): EXAMPLES:: - sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest + sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest # needs sage.schemes [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10], [0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28], [0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34], @@ -959,7 +962,7 @@ def PBD_4_5_8_9_12(v, check=True): Check that :trac:`16476` is fixed:: sage: from sage.combinat.designs.bibd import PBD_4_5_8_9_12 - sage: for v in (0,1,4,5,8,9,12,13,16,17,20,21,24,25): + sage: for v in (0,1,4,5,8,9,12,13,16,17,20,21,24,25): # needs sage.schemes ....: _ = PBD_4_5_8_9_12(v) """ if v % 4 not in [0, 1]: @@ -1029,7 +1032,7 @@ def _PBD_4_5_8_9_12_closure(B): EXAMPLES:: - sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest + sage: designs.balanced_incomplete_block_design(40,4).blocks() # indirect doctest # needs sage.schemes [[0, 1, 2, 12], [0, 3, 6, 9], [0, 4, 8, 10], [0, 5, 7, 11], [0, 13, 26, 39], [0, 14, 25, 28], [0, 15, 27, 38], [0, 16, 22, 32], [0, 17, 23, 34], @@ -1121,7 +1124,7 @@ def v_5_1_BIBD(v, check=True): sage: from sage.combinat.designs.bibd import v_5_1_BIBD sage: i = 0 - sage: while i<200: + sage: while i<200: # needs sage.libs.pari ....: i += 20 ....: _ = v_5_1_BIBD(i+1) ....: _ = v_5_1_BIBD(i+5) @@ -1130,7 +1133,7 @@ def v_5_1_BIBD(v, check=True): Check that the needed difference families are there:: - sage: for v in [21,41,61,81,141,161,281]: + sage: for v in [21,41,61,81,141,161,281]: # needs sage.libs.pari ....: assert designs.difference_family(v,5,existence=True) ....: _ = designs.difference_family(v,5) """ @@ -1316,14 +1319,14 @@ def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): sage: from sage.combinat.designs.bibd import BIBD_from_arc_in_desarguesian_projective_plane sage: from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign - sage: D = BIBD_from_arc_in_desarguesian_projective_plane(232,8) # optional - sage.libs.gap sage.modules sage.rings.finite_rings - sage: BalancedIncompleteBlockDesign(232,D) # optional - sage.libs.gap sage.modules sage.rings.finite_rings + sage: D = BIBD_from_arc_in_desarguesian_projective_plane(232,8) # needs sage.libs.gap sage.modules sage.rings.finite_rings + sage: BalancedIncompleteBlockDesign(232,D) # needs sage.libs.gap sage.modules sage.rings.finite_rings (232,8,1)-Balanced Incomplete Block Design A `(120,8,1)`-BIBD:: - sage: D = BIBD_from_arc_in_desarguesian_projective_plane(120,8) # optional - sage.libs.gap sage.modules sage.rings.finite_rings - sage: BalancedIncompleteBlockDesign(120,D) # optional - sage.libs.gap sage.modules sage.rings.finite_rings + sage: D = BIBD_from_arc_in_desarguesian_projective_plane(120,8) # needs sage.libs.gap sage.modules sage.rings.finite_rings + sage: BalancedIncompleteBlockDesign(120,D) # needs sage.libs.gap sage.modules sage.rings.finite_rings (120,8,1)-Balanced Incomplete Block Design Other parameters:: @@ -1569,14 +1572,13 @@ def arc(self, s=2, solver=None, verbose=0, *, integrality_tolerance=1e-3): EXAMPLES:: + sage: # needs sage.schemes sage: B = designs.balanced_incomplete_block_design(21, 5) - sage: a2 = B.arc() - sage: a2 # random + sage: a2 = B.arc(); a2 # random [5, 9, 10, 12, 15, 20] sage: len(a2) 6 - sage: a4 = B.arc(4) - sage: a4 # random + sage: a4 = B.arc(4); a4 # random [0, 1, 2, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20] sage: len(a4) 16 @@ -1591,13 +1593,14 @@ def arc(self, s=2, solver=None, verbose=0, *, integrality_tolerance=1e-3): sage: 1 + r*3 16 - sage: B.trace(a2).is_t_design(2, return_parameters=True) + sage: B.trace(a2).is_t_design(2, return_parameters=True) # needs sage.schemes (True, (2, 6, 2, 1)) - sage: B.trace(a4).is_t_design(2, return_parameters=True) + sage: B.trace(a4).is_t_design(2, return_parameters=True) # needs sage.schemes (True, (2, 16, 4, 1)) Some other examples which are not maximal:: + sage: # needs sage.numerical.mip sage: B = designs.balanced_incomplete_block_design(25, 4) sage: a2 = B.arc(2) sage: r = (25-1)//(4-1) @@ -1609,6 +1612,7 @@ def arc(self, s=2, solver=None, verbose=0, *, integrality_tolerance=1e-3): sage: B.trace(a2).is_t_design(2) False + sage: # needs sage.numerical.mip sage: a3 = B.arc(3) sage: len(a3), 1 + 2*r (15, 17) @@ -1622,9 +1626,9 @@ def arc(self, s=2, solver=None, verbose=0, *, integrality_tolerance=1e-3): Test consistency with relabeling:: - sage: b = designs.balanced_incomplete_block_design(7,3) - sage: b.relabel(list("abcdefg")) - sage: set(b.arc()).issubset(b.ground_set()) + sage: b = designs.balanced_incomplete_block_design(7,3) # needs sage.schemes + sage: b.relabel(list("abcdefg")) # needs sage.schemes + sage: set(b.arc()).issubset(b.ground_set()) # needs sage.schemes True """ s = int(s) diff --git a/src/sage/combinat/designs/block_design.py b/src/sage/combinat/designs/block_design.py index 28f3746ad88..98774d77a42 100644 --- a/src/sage/combinat/designs/block_design.py +++ b/src/sage/combinat/designs/block_design.py @@ -1,4 +1,4 @@ -# sage.doctest: optional - sage.rings.finite_rings +# sage.doctest: needs sage.rings.finite_rings r""" Block designs @@ -54,14 +54,17 @@ #***************************************************************************** from sage.arith.misc import binomial, integer_floor, is_prime_power from sage.categories.sets_cat import EmptySetError -from sage.modules.free_module import VectorSpace +from sage.misc.lazy_import import lazy_import +from sage.misc.unknown import Unknown from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ + from .incidence_structures import IncidenceStructure -from sage.rings.finite_rings.finite_field_constructor import FiniteField -from sage.misc.unknown import Unknown -from sage.matrix.matrix_space import MatrixSpace -from sage.libs.gap.libgap import libgap + +lazy_import('sage.libs.gap.libgap', 'libgap') +lazy_import('sage.matrix.matrix_space', 'MatrixSpace') +lazy_import('sage.modules.free_module', 'VectorSpace') +lazy_import('sage.rings.finite_rings.finite_field_constructor', 'FiniteField') BlockDesign = IncidenceStructure @@ -214,8 +217,7 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=True, ch The set of `d`-dimensional subspaces in a `n`-dimensional projective space forms `2`-designs (or balanced incomplete block designs):: - sage: PG = designs.ProjectiveGeometryDesign(4, 2, GF(2)) - sage: PG + sage: PG = designs.ProjectiveGeometryDesign(4, 2, GF(2)); PG Incidence structure with 31 points and 155 blocks sage: PG.is_t_design(return_parameters=True) (True, (2, 31, 7, 7)) @@ -226,8 +228,7 @@ def ProjectiveGeometryDesign(n, d, F, algorithm=None, point_coordinates=True, ch Check with ``F`` being a prime power:: - sage: PG = designs.ProjectiveGeometryDesign(3, 2, 4) - sage: PG + sage: PG = designs.ProjectiveGeometryDesign(3, 2, 4); PG Incidence structure with 85 points and 85 blocks Use coordinates:: @@ -900,10 +901,10 @@ def CremonaRichmondConfiguration(): EXAMPLES:: - sage: H = designs.CremonaRichmondConfiguration(); H # optional - networkx + sage: H = designs.CremonaRichmondConfiguration(); H # needs networkx Incidence structure with 15 points and 15 blocks - sage: g = graphs.TutteCoxeterGraph() # optional - networkx - sage: H.incidence_graph().is_isomorphic(g) # optional - networkx + sage: g = graphs.TutteCoxeterGraph() # needs networkx + sage: H.incidence_graph().is_isomorphic(g) # needs networkx True """ from sage.graphs.generators.smallgraphs import TutteCoxeterGraph @@ -956,16 +957,16 @@ def HadamardDesign(n): EXAMPLES:: - sage: designs.HadamardDesign(7) # optional - sage.modules + sage: designs.HadamardDesign(7) # needs sage.modules Incidence structure with 7 points and 7 blocks - sage: print(designs.HadamardDesign(7)) # optional - sage.modules + sage: print(designs.HadamardDesign(7)) # needs sage.modules Incidence structure with 7 points and 7 blocks For example, the Hadamard 2-design with `n = 11` is a design whose parameters are `2-(11, 5, 2)`. We verify that `NJ = 5J` for this design. :: - sage: D = designs.HadamardDesign(11); N = D.incidence_matrix() # optional - sage.modules - sage: J = matrix(ZZ, 11, 11, [1]*11*11); N*J # optional - sage.modules + sage: D = designs.HadamardDesign(11); N = D.incidence_matrix() # needs sage.modules + sage: J = matrix(ZZ, 11, 11, [1]*11*11); N*J # needs sage.modules [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] @@ -1009,7 +1010,7 @@ def Hadamard3Design(n): EXAMPLES:: - sage: designs.Hadamard3Design(12) # optional - sage.modules + sage: designs.Hadamard3Design(12) # needs sage.modules Incidence structure with 12 points and 22 blocks We verify that any two blocks of the Hadamard `3`-design `3-(8, 4, 1)` @@ -1019,9 +1020,9 @@ def Hadamard3Design(n): :: - sage: D = designs.Hadamard3Design(8) # optional - sage.modules - sage: N = D.incidence_matrix() # optional - sage.modules - sage: N.transpose()*N # optional - sage.modules + sage: D = designs.Hadamard3Design(8) # needs sage.modules + sage: N = D.incidence_matrix() # needs sage.modules + sage: N.transpose()*N # needs sage.modules [4 2 2 2 2 2 2 2 2 2 2 2 2 0] [2 4 2 2 2 2 2 2 2 2 2 2 0 2] [2 2 4 2 2 2 2 2 2 2 2 0 2 2] diff --git a/src/sage/combinat/designs/database.py b/src/sage/combinat/designs/database.py index 0bb4d88a0fa..3e095e619ee 100644 --- a/src/sage/combinat/designs/database.py +++ b/src/sage/combinat/designs/database.py @@ -81,7 +81,7 @@ def _MOLS_from_string(s,k): EXAMPLES:: - sage: _ = designs.mutually_orthogonal_latin_squares(2,10) # indirect doctest # optional - sage.modules + sage: _ = designs.mutually_orthogonal_latin_squares(2,10) # indirect doctest # needs sage.modules """ from sage.matrix.constructor import Matrix matrices = [[] for _ in range(k)] @@ -101,13 +101,13 @@ def MOLS_10_2(): sage: from sage.combinat.designs.latin_squares import are_mutually_orthogonal_latin_squares sage: from sage.combinat.designs.database import MOLS_10_2 - sage: MOLS = MOLS_10_2() # optional - sage.modules - sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # optional - sage.modules + sage: MOLS = MOLS_10_2() # needs sage.modules + sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # needs sage.modules True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(2,10) # optional - sage.modules + sage: designs.orthogonal_arrays.is_available(2,10) True """ from sage.matrix.constructor import Matrix @@ -143,8 +143,8 @@ def MOLS_12_5(): sage: from sage.combinat.designs.latin_squares import are_mutually_orthogonal_latin_squares sage: from sage.combinat.designs.database import MOLS_12_5 - sage: MOLS = MOLS_12_5() # optional - sage.modules - sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # optional - sage.modules + sage: MOLS = MOLS_12_5() # needs sage.modules + sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # needs sage.modules True """ M = """ @@ -175,13 +175,13 @@ def MOLS_14_4(): sage: from sage.combinat.designs.latin_squares import are_mutually_orthogonal_latin_squares sage: from sage.combinat.designs.database import MOLS_14_4 - sage: MOLS = MOLS_14_4() # optional - sage.modules - sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # optional - sage.modules + sage: MOLS = MOLS_14_4() # needs sage.modules + sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # needs sage.modules True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(4,14) + sage: designs.orthogonal_arrays.is_available(4,14) # needs sage.schemes True REFERENCE: @@ -219,13 +219,13 @@ def MOLS_15_4(): sage: from sage.combinat.designs.latin_squares import are_mutually_orthogonal_latin_squares sage: from sage.combinat.designs.database import MOLS_15_4 - sage: MOLS = MOLS_15_4() # optional - sage.modules - sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # optional - sage.modules + sage: MOLS = MOLS_15_4() # needs sage.modules + sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # needs sage.modules True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(4,15) + sage: designs.orthogonal_arrays.is_available(4,15) # needs sage.schemes True """ M = """ @@ -258,8 +258,8 @@ def MOLS_18_3(): sage: from sage.combinat.designs.latin_squares import are_mutually_orthogonal_latin_squares sage: from sage.combinat.designs.database import MOLS_18_3 - sage: MOLS = MOLS_18_3() # optional - sage.modules - sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # optional - sage.modules + sage: MOLS = MOLS_18_3() # needs sage.modules + sage: print(are_mutually_orthogonal_latin_squares(MOLS)) # needs sage.modules True The design is available from the general constructor:: @@ -328,7 +328,7 @@ def OA_7_18(): The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(7,18) + sage: designs.orthogonal_arrays.is_available(7,18) # needs sage.schemes True """ M = """ @@ -377,13 +377,13 @@ def OA_9_40(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_9_40 - sage: OA = OA_9_40() # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,9,40,2) # optional - sage.rings.finite_rings + sage: OA = OA_9_40() # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,9,40,2) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(9,40) + sage: designs.orthogonal_arrays.is_available(9,40) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -416,13 +416,13 @@ def OA_7_66(): sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array sage: from sage.combinat.designs.database import OA_7_66 - sage: OA = OA_7_66() - sage: is_orthogonal_array(OA,7,66,2) + sage: OA = OA_7_66() # needs sage.schemes + sage: is_orthogonal_array(OA,7,66,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(7,66) + sage: designs.orthogonal_arrays.is_available(7,66) # needs sage.schemes True """ @@ -456,13 +456,13 @@ def OA_7_68(): sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array sage: from sage.combinat.designs.database import OA_7_68 - sage: OA = OA_7_68() - sage: is_orthogonal_array(OA,7,68,2) + sage: OA = OA_7_68() # needs sage.schemes + sage: is_orthogonal_array(OA,7,68,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(7,68) + sage: designs.orthogonal_arrays.is_available(7,68) # needs sage.schemes True """ @@ -496,13 +496,13 @@ def OA_8_69(): sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array sage: from sage.combinat.designs.database import OA_8_69 - sage: OA = OA_8_69() - sage: is_orthogonal_array(OA,8,69,2) + sage: OA = OA_8_69() # needs sage.schemes + sage: is_orthogonal_array(OA,8,69,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(8,69) + sage: designs.orthogonal_arrays.is_available(8,69) # needs sage.schemes True """ # base block of a (73,9,1) BIBD @@ -568,13 +568,13 @@ def OA_7_74(): sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array sage: from sage.combinat.designs.database import OA_7_74 - sage: OA = OA_7_74() - sage: is_orthogonal_array(OA,7,74,2) + sage: OA = OA_7_74() # needs sage.schemes + sage: is_orthogonal_array(OA,7,74,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(7,74) + sage: designs.orthogonal_arrays.is_available(7,74) # needs sage.schemes True """ @@ -608,13 +608,13 @@ def OA_8_76(): sage: from sage.combinat.designs.orthogonal_arrays import is_orthogonal_array sage: from sage.combinat.designs.database import OA_8_76 - sage: OA = OA_8_76() - sage: is_orthogonal_array(OA,8,76,2) + sage: OA = OA_8_76() # needs sage.schemes + sage: is_orthogonal_array(OA,8,76,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(8,76) + sage: designs.orthogonal_arrays.is_available(8,76) # needs sage.schemes True """ # base block of a (91,10,1) BIBD @@ -676,13 +676,13 @@ def OA_11_80(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_11_80 - sage: OA = OA_11_80() # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,11,80,2) # optional - sage.rings.finite_rings + sage: OA = OA_11_80() # needs sage.rings.finite_rings sage.schemes + sage: is_orthogonal_array(OA,11,80,2) # needs sage.rings.finite_rings sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(11,80) + sage: designs.orthogonal_arrays.is_available(11,80) # needs sage.rings.finite_rings sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -718,13 +718,13 @@ def OA_15_112(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_15_112 - sage: OA = OA_15_112() # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,15,112,2) # optional - sage.rings.finite_rings + sage: OA = OA_15_112() # needs sage.rings.finite_rings sage.schemes + sage: is_orthogonal_array(OA,15,112,2) # needs sage.rings.finite_rings sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(15,112) + sage: designs.orthogonal_arrays.is_available(15,112) # needs sage.rings.finite_rings sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -768,13 +768,13 @@ def OA_9_120(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_9_120 - sage: OA = OA_9_120() # optional - sage.modules - sage: is_orthogonal_array(OA,9,120,2) # optional - sage.modules + sage: OA = OA_9_120() # needs sage.modules sage.schemes + sage: is_orthogonal_array(OA,9,120,2) # needs sage.modules sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(9,120) + sage: designs.orthogonal_arrays.is_available(9,120) # needs sage.schemes True """ RBIBD_120 = RBIBD_120_8_1() @@ -816,20 +816,19 @@ def OA_9_135(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_9_135 - sage: OA = OA_9_135() # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,9,135,2) # optional - sage.rings.finite_rings + sage: OA = OA_9_135() # needs sage.rings.finite_rings sage.schemes + sage: is_orthogonal_array(OA,9,135,2) # needs sage.rings.finite_rings sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(9,135) + sage: designs.orthogonal_arrays.is_available(9,135) # needs sage.schemes True As this orthogonal array requires a `(273,17,1)` cyclic difference set, we check that it is available:: - sage: G,D = designs.difference_family(273,17,1) - sage: G + sage: G,D = designs.difference_family(273,17,1); G # needs sage.libs.pari Ring of integers modulo 273 """ from .bibd import BIBD_from_difference_family @@ -896,13 +895,13 @@ def OA_11_160(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_11_160 - sage: OA = OA_11_160() # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,11,160,2) # optional - sage.rings.finite_rings + sage: OA = OA_11_160() # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,11,160,2) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(11,160) + sage: designs.orthogonal_arrays.is_available(11,160) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -939,13 +938,13 @@ def OA_16_176(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_16_176 - sage: OA = OA_16_176() # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,16,176,2) # optional - sage.rings.finite_rings + sage: OA = OA_16_176() # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,16,176,2) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(16,176) + sage: designs.orthogonal_arrays.is_available(16,176) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -998,13 +997,13 @@ def OA_11_185(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_11_185 - sage: OA = OA_11_185() # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,11,185,2) # optional - sage.rings.finite_rings + sage: OA = OA_11_185() # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,11,185,2) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(11,185) + sage: designs.orthogonal_arrays.is_available(11,185) # needs sage.schemes True """ @@ -1074,13 +1073,13 @@ def OA_10_205(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_10_205 - sage: OA = OA_10_205() - sage: is_orthogonal_array(OA,10,205,2) + sage: OA = OA_10_205() # needs sage.schemes + sage: is_orthogonal_array(OA,10,205,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(10,205) + sage: designs.orthogonal_arrays.is_available(10,205) # needs sage.schemes True """ # Base block of a cyclic PG(2,4^2) @@ -1138,13 +1137,13 @@ def OA_16_208(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_16_208 - sage: OA = OA_16_208() # not tested -- too long # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,16,208,2) # not tested -- too long # optional - sage.rings.finite_rings + sage: OA = OA_16_208() # not tested (too long) # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,16,208,2) # not tested (too long) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(16,208) + sage: designs.orthogonal_arrays.is_available(16,208) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -1197,13 +1196,13 @@ def OA_15_224(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_15_224 - sage: OA = OA_15_224() # not tested -- too long # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,15,224,2) # not tested -- too long # optional - sage.rings.finite_rings + sage: OA = OA_15_224() # not tested (too long) # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,15,224,2) # not tested (too long) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(15,224) + sage: designs.orthogonal_arrays.is_available(15,224) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -1248,13 +1247,13 @@ def OA_11_254(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_11_254 - sage: OA = OA_11_254() - sage: is_orthogonal_array(OA,11,254,2) + sage: OA = OA_11_254() # needs sage.schemes + sage: is_orthogonal_array(OA,11,254,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(11,254) + sage: designs.orthogonal_arrays.is_available(11,254) # needs sage.schemes True """ @@ -1283,13 +1282,13 @@ def OA_20_352(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_20_352 - sage: OA = OA_20_352() # not tested (~25s) # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,20,352,2) # not tested (~25s) # optional - sage.rings.finite_rings + sage: OA = OA_20_352() # not tested (~25s) # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,20,352,2) # not tested (~25s) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(20,352) + sage: designs.orthogonal_arrays.is_available(20,352) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -1342,13 +1341,13 @@ def OA_20_416(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_20_416 - sage: OA = OA_20_416() # not tested (~35s) # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,20,416,2) # not tested # optional - sage.rings.finite_rings + sage: OA = OA_20_416() # not tested (~35s) # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,20,416,2) # not tested # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(20,416) + sage: designs.orthogonal_arrays.is_available(20,416) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -1402,13 +1401,13 @@ def OA_20_544(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_20_544 - sage: OA = OA_20_544() # not tested (too long ~1mn) # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,20,544,2) # not tested # optional - sage.rings.finite_rings + sage: OA = OA_20_544() # not tested (too long ~1mn) # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,20,544,2) # not tested # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(20,544) + sage: designs.orthogonal_arrays.is_available(20,544) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -1466,13 +1465,13 @@ def OA_17_560(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_17_560 - sage: OA = OA_17_560() # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,17,560,2) # optional - sage.rings.finite_rings + sage: OA = OA_17_560() # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,17,560,2) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(17,560) + sage: designs.orthogonal_arrays.is_available(17,560) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF @@ -1530,13 +1529,13 @@ def OA_11_640(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_11_640 - sage: OA = OA_11_640() # not tested (too long) # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,11,640,2) # not tested (too long) # optional - sage.rings.finite_rings + sage: OA = OA_11_640() # not tested (too long) # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,11,640,2) # not tested (too long) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(11,640) + sage: designs.orthogonal_arrays.is_available(11,640) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -1580,13 +1579,13 @@ def OA_10_796(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_10_796 - sage: OA = OA_10_796() - sage: is_orthogonal_array(OA,10,796,2) + sage: OA = OA_10_796() # needs sage.schemes + sage: is_orthogonal_array(OA,10,796,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(10,796) + sage: designs.orthogonal_arrays.is_available(10,796) # needs sage.schemes True """ from sage.combinat.designs.orthogonal_arrays import OA_relabel @@ -1658,13 +1657,13 @@ def OA_10_469(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_10_469 - sage: OA = OA_10_469() # long time - sage: is_orthogonal_array(OA,10,469,2) # long time + sage: OA = OA_10_469() # long time # needs sage.schemes + sage: is_orthogonal_array(OA,10,469,2) # long time # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(10,469) + sage: designs.orthogonal_arrays.is_available(10,469) # needs sage.schemes True """ from .orthogonal_arrays_build_recursive import _reorder_matrix @@ -1787,13 +1786,13 @@ def OA_10_520(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_10_520 - sage: OA = OA_10_520() - sage: is_orthogonal_array(OA,10,520,2) + sage: OA = OA_10_520() # needs sage.schemes + sage: is_orthogonal_array(OA,10,520,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(10,520) + sage: designs.orthogonal_arrays.is_available(10,520) # needs sage.schemes True """ return OA_520_plus_x(0) @@ -1809,13 +1808,13 @@ def OA_12_522(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_12_522 - sage: OA = OA_12_522() - sage: is_orthogonal_array(OA,12,522,2) + sage: OA = OA_12_522() # needs sage.schemes + sage: is_orthogonal_array(OA,12,522,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(12,522) + sage: designs.orthogonal_arrays.is_available(12,522) # needs sage.schemes True """ return OA_520_plus_x(2) @@ -1831,13 +1830,13 @@ def OA_14_524(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_14_524 - sage: OA = OA_14_524() - sage: is_orthogonal_array(OA,14,524,2) + sage: OA = OA_14_524() # needs sage.schemes + sage: is_orthogonal_array(OA,14,524,2) # needs sage.schemes True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(14,524) + sage: designs.orthogonal_arrays.is_available(14,524) # needs sage.schemes True """ return OA_520_plus_x(4) @@ -1857,13 +1856,13 @@ def OA_15_896(): sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: from sage.combinat.designs.database import OA_15_896 - sage: OA = OA_15_896() # not tested -- too long (~2min) # optional - sage.rings.finite_rings - sage: is_orthogonal_array(OA,15,896,2) # not tested -- too long # optional - sage.rings.finite_rings + sage: OA = OA_15_896() # not tested (too long, ~2min) # needs sage.rings.finite_rings + sage: is_orthogonal_array(OA,15,896,2) # not tested (too long) # needs sage.rings.finite_rings True The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(15,896) + sage: designs.orthogonal_arrays.is_available(15,896) # needs sage.schemes True """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -1914,7 +1913,7 @@ def OA_9_1078(): The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(9,1078) + sage: designs.orthogonal_arrays.is_available(9,1078) # needs sage.schemes True """ return wilson_construction(None,9,11,89,[[(11,9)]]) @@ -1940,7 +1939,7 @@ def OA_25_1262(): The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(25,1262) + sage: designs.orthogonal_arrays.is_available(25,1262) # needs sage.schemes True """ @@ -1981,7 +1980,7 @@ def OA_9_1612(): The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(9,1612) + sage: designs.orthogonal_arrays.is_available(9,1612) # needs sage.schemes True """ return wilson_construction(None,9,17,89,[[(11,9)]]) @@ -2011,7 +2010,7 @@ def OA_10_1620(): The design is available from the general constructor:: - sage: designs.orthogonal_arrays.is_available(10,1620) + sage: designs.orthogonal_arrays.is_available(10,1620) # needs sage.schemes True """ return wilson_construction(None,10,11,144,[[(9,4)]]) @@ -2194,8 +2193,8 @@ def QDM_25_6_1_1_5(): sage: from sage.combinat.designs.database import QDM_25_6_1_1_5 sage: from sage.combinat.designs.designs_pyx import is_quasi_difference_matrix - sage: G,M = QDM_25_6_1_1_5() # optional - sage.modules - sage: is_quasi_difference_matrix(M,G,6,1,1,5) # optional - sage.modules + sage: G,M = QDM_25_6_1_1_5() # needs sage.modules + sage: is_quasi_difference_matrix(M,G,6,1,1,5) # needs sage.modules True """ M = [ @@ -2431,8 +2430,8 @@ def QDM_57_9_1_1_8(): sage: from sage.combinat.designs.database import QDM_57_9_1_1_8 sage: from sage.combinat.designs.designs_pyx import is_quasi_difference_matrix - sage: G,M = QDM_57_9_1_1_8() - sage: is_quasi_difference_matrix(M,G,9,1,1,8) + sage: G,M = QDM_57_9_1_1_8() # needs sage.schemes + sage: is_quasi_difference_matrix(M,G,9,1,1,8) # needs sage.schemes True """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as G @@ -2718,7 +2717,7 @@ def QDM_57_9_1_1_8(): sage: from sage.combinat.designs.designs_pyx import is_quasi_difference_matrix sage: from sage.combinat.designs.orthogonal_arrays import QDM_from_Vmt sage: from sage.combinat.designs.database import Vmt_vectors - sage: for (m,t),(vec,source) in sorted(Vmt_vectors.items()): # optional - sage.rings.finite_rings + sage: for (m,t),(vec,source) in sorted(Vmt_vectors.items()): # needs sage.rings.finite_rings ....: G,M = QDM_from_Vmt(m,t,vec) ....: if m*t < 600: ....: assert is_quasi_difference_matrix(M,G,m+2,1,1,t,verbose=1),(m,t) @@ -3147,7 +3146,7 @@ def DM_12_6_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(12,6) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(12,6) REFERENCES: @@ -3189,7 +3188,7 @@ def DM_21_6_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(21,6) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(21,6) # needs sage.rings.finite_rings """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic M = [[ 8, 17, 20, 2], @@ -3224,7 +3223,7 @@ def DM_24_8_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(24,8) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(24,8) """ M = ("0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 "+ "0000 0010 0100 0110 1000 1010 1100 1110 2000 2010 2100 2110 "+ @@ -3265,13 +3264,13 @@ def DM_28_6_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_28_6_1 - sage: G,M = DM_28_6_1() # optional - sage.modules - sage: is_difference_matrix(M,G,6,1) # optional - sage.modules + sage: G,M = DM_28_6_1() # needs sage.modules + sage: is_difference_matrix(M,G,6,1) # needs sage.modules True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(28,6) # optional - sage.modules + sage: _ = designs.difference_matrix(28,6) # needs sage.modules """ z=2 M = [ @@ -3314,7 +3313,7 @@ def DM_33_6_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(33,6) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(33,6) # needs sage.rings.finite_rings """ M = [ [ 0, 0, 0, 0, 0, 0], @@ -3356,7 +3355,7 @@ def DM_35_6_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(35,6) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(35,6) # needs sage.rings.finite_rings """ M = [ [ 0, 15, 30, 10, 25, 1, 16, 31, 11, 26, 2, 17, 32, 12, 6, 3, 18, 33, 27, 21, 4, 19, 13, 7, 22, 5, 34, 28, 8, 23, 20, 14, 29, 9, 24], @@ -3382,13 +3381,13 @@ def DM_36_9_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_36_9_1 - sage: G,M = DM_36_9_1() # optional - sage.modules - sage: is_difference_matrix(M,G,9,1) # optional - sage.modules + sage: G,M = DM_36_9_1() # needs sage.modules + sage: is_difference_matrix(M,G,9,1) # needs sage.modules True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(36,9) # optional - sage.modules + sage: _ = designs.difference_matrix(36,9) # needs sage.modules """ M = [ [(0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0), (0,0,0,0)], @@ -3441,7 +3440,7 @@ def DM_39_6_1(): The design is available from the general constructor:: - sage: designs.difference_matrix(39,6,existence=True) # optional - sage.rings.finite_rings + sage: designs.difference_matrix(39,6,existence=True) # needs sage.rings.finite_rings True """ M = [ @@ -3485,7 +3484,7 @@ def DM_44_6_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(44,6) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(44,6) """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic from sage.categories.cartesian_product import cartesian_product @@ -3541,13 +3540,13 @@ def DM_45_7_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_45_7_1 - sage: G,M = DM_45_7_1() # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,G,7,1) # optional - sage.rings.finite_rings + sage: G,M = DM_45_7_1() + sage: is_difference_matrix(M,G,7,1) True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(45,7) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(45,7) # needs sage.rings.finite_rings """ from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.categories.cartesian_product import cartesian_product @@ -3594,13 +3593,13 @@ def DM_48_9_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_48_9_1 - sage: G,M = DM_48_9_1() # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,G,9,1) # optional - sage.rings.finite_rings + sage: G,M = DM_48_9_1() # needs sage.rings.finite_rings + sage: is_difference_matrix(M,G,9,1) # needs sage.rings.finite_rings True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(48,9) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(48,9) # needs sage.rings.finite_rings """ from sage.rings.finite_rings.finite_field_constructor import FiniteField F16 = FiniteField(16,'x') @@ -3649,7 +3648,7 @@ def DM_51_6_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(51,6) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(51,6) # needs sage.rings.finite_rings """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic G = AdditiveCyclic(51) @@ -3685,13 +3684,13 @@ def DM_52_6_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_52_6_1 - sage: G,M = DM_52_6_1() # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,G,6,1) # optional - sage.rings.finite_rings + sage: G,M = DM_52_6_1() # needs sage.rings.finite_rings + sage: is_difference_matrix(M,G,6,1) # needs sage.rings.finite_rings True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(52,6) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(52,6) # needs sage.rings.finite_rings """ from sage.rings.finite_rings.finite_field_constructor import FiniteField F4 = FiniteField(4,'z') @@ -3763,7 +3762,7 @@ def DM_55_7_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(55,7) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(55,7) # needs sage.rings.finite_rings """ from sage.rings.finite_rings.integer_mod_ring import IntegerModRing as AdditiveCyclic G = AdditiveCyclic(55) @@ -3796,13 +3795,13 @@ def DM_56_8_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_56_8_1 - sage: G,M = DM_56_8_1() # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,G,8,1) # optional - sage.rings.finite_rings + sage: G,M = DM_56_8_1() # needs sage.rings.finite_rings + sage: is_difference_matrix(M,G,8,1) # needs sage.rings.finite_rings True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(56,8) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(56,8) # needs sage.rings.finite_rings """ from sage.rings.finite_rings.finite_field_constructor import FiniteField F8 = FiniteField(8,'z') @@ -3843,13 +3842,13 @@ def DM_57_8_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_57_8_1 - sage: G,M = DM_57_8_1() # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,G,8,1) # optional - sage.rings.finite_rings + sage: G,M = DM_57_8_1() # needs sage.rings.finite_rings + sage: is_difference_matrix(M,G,8,1) # needs sage.rings.finite_rings True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(57,8) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(57,8) # needs sage.rings.finite_rings """ M = orthogonal_array(8,8) M = [R for R in M if any(x!=R[0] for x in R)] # removing the 0..0, 1..1, 7..7 rows. @@ -3887,7 +3886,7 @@ def DM_60_6_1(): Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(60,6) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(60,6) """ M60 = [[(0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0), (0, 0)], [(1, 10), (1, 6), (0, 17), (0, 7), (1, 5), (0, 9), (0, 3), (1, 13), (1, 17), (0, 13)], @@ -3923,13 +3922,13 @@ def DM_75_8_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_75_8_1 - sage: G,M = DM_75_8_1() # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,G,8,1) # optional - sage.rings.finite_rings + sage: G,M = DM_75_8_1() + sage: is_difference_matrix(M,G,8,1) True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(75,8) # optional - sage.rings.finite_rings + sage: _ = designs.difference_matrix(75,8) # needs sage.rings.finite_rings """ from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.categories.cartesian_product import cartesian_product @@ -3971,13 +3970,13 @@ def DM_273_17_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_273_17_1 - sage: G,M = DM_273_17_1() - sage: is_difference_matrix(M,G,17,1) + sage: G,M = DM_273_17_1() # needs sage.schemes + sage: is_difference_matrix(M,G,17,1) # needs sage.schemes True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(273,17) + sage: _ = designs.difference_matrix(273,17) # needs sage.schemes """ M = orthogonal_array(17,17) M = [R for R in M if any(x!=R[0] for x in R)] # removing the 0..0, 1..1, ... rows. @@ -3999,13 +3998,13 @@ def DM_993_32_1(): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: from sage.combinat.designs.database import DM_993_32_1 - sage: G,M = DM_993_32_1() - sage: is_difference_matrix(M,G,32,1) + sage: G,M = DM_993_32_1() # needs sage.schemes + sage: is_difference_matrix(M,G,32,1) # needs sage.schemes True Can be obtained from the constructor:: - sage: _ = designs.difference_matrix(993,32) + sage: _ = designs.difference_matrix(993,32) # needs sage.schemes """ M = orthogonal_array(32,32) M = [R for R in M if any(x!=R[0] for x in R)] # removing the 0..0, 1..1, ... rows. @@ -4080,19 +4079,19 @@ def RBIBD_120_8_1(): sage: from sage.combinat.designs.database import RBIBD_120_8_1 sage: from sage.combinat.designs.bibd import is_pairwise_balanced_design - sage: RBIBD = RBIBD_120_8_1() # optional - sage.modules - sage: is_pairwise_balanced_design(RBIBD,120,[8]) # optional - sage.modules + sage: RBIBD = RBIBD_120_8_1() # needs sage.modules + sage: is_pairwise_balanced_design(RBIBD,120,[8]) # needs sage.modules True It is indeed resolvable, and the parallel classes are given by 17 slices of consecutive 15 blocks:: - sage: for i in range(17): # optional - sage.modules + sage: for i in range(17): # needs sage.modules ....: assert len(set(sum(RBIBD[i*15:(i+1)*15],[]))) == 120 The BIBD is available from the constructor:: - sage: _ = designs.balanced_incomplete_block_design(120,8) # optional - sage.modules + sage: _ = designs.balanced_incomplete_block_design(120,8) # needs sage.modules """ from .incidence_structures import IncidenceStructure n=273 @@ -4148,15 +4147,15 @@ def BIBD_45_9_8(from_code=False): sage: from sage.combinat.designs.database import BIBD_45_9_8 sage: from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign - sage: B = BalancedIncompleteBlockDesign(45, BIBD_45_9_8(), lambd=8); B # optional - sage.rings.finite_rings + sage: B = BalancedIncompleteBlockDesign(45, BIBD_45_9_8(), lambd=8); B (45,9,8)-Balanced Incomplete Block Design TESTS: From the definition (takes around 12s):: - sage: B2 = Hypergraph(BIBD_45_9_8(from_code=True)) # not tested # optional - sage.rings.finite_rings - sage: B2.is_isomorphic(B) # not tested # optional - sage.rings.finite_rings + sage: B2 = Hypergraph(BIBD_45_9_8(from_code=True)) # not tested # needs sage.rings.finite_rings + sage: B2.is_isomorphic(B) # not tested # needs sage.rings.finite_rings True REFERENCE: @@ -4580,8 +4579,8 @@ def BIBD_79_13_2(): EXAMPLES: sage: from sage.combinat.designs.database import BIBD_79_13_2 - sage: D = IncidenceStructure(BIBD_79_13_2()) # optional - sage.libs.gap - sage: D.is_t_design(t=2, v=79, k=13, l=2) # optional - sage.libs.gap + sage: D = IncidenceStructure(BIBD_79_13_2()) # needs sage.libs.gap + sage: D.is_t_design(t=2, v=79, k=13, l=2) # needs sage.libs.gap True """ from sage.libs.gap.libgap import libgap @@ -4658,8 +4657,8 @@ def BIBD_56_11_2(): EXAMPLES: sage: from sage.combinat.designs.database import BIBD_56_11_2 - sage: D = IncidenceStructure(BIBD_56_11_2()) # optional - sage.libs.gap - sage: D.is_t_design(t=2, v=56, k=11, l=2) # optional - sage.libs.gap + sage: D = IncidenceStructure(BIBD_56_11_2()) # needs sage.libs.gap + sage: D.is_t_design(t=2, v=56, k=11, l=2) # needs sage.libs.gap True """ from sage.libs.gap.libgap import libgap diff --git a/src/sage/combinat/designs/designs_pyx.pyx b/src/sage/combinat/designs/designs_pyx.pyx index be8745f2acb..565b7d83184 100644 --- a/src/sage/combinat/designs/designs_pyx.pyx +++ b/src/sage/combinat/designs/designs_pyx.pyx @@ -36,6 +36,7 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology="O EXAMPLES:: + sage: # needs sage.schemes sage: from sage.combinat.designs.designs_pyx import is_orthogonal_array sage: OA = designs.orthogonal_arrays.build(8,9) sage: is_orthogonal_array(OA,8,9) @@ -48,26 +49,27 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology="O sage: is_orthogonal_array(OA,8,9,verbose=True) Columns 0 and 3 are not orthogonal False - sage: is_orthogonal_array(OA,8,9,verbose=True,terminology="MOLS") + sage: is_orthogonal_array(OA,8,9, verbose=True, terminology="MOLS") Squares 0 and 3 are not orthogonal False TESTS:: - sage: is_orthogonal_array(OA,8,9,t=3) + sage: # needs sage.schemes + sage: is_orthogonal_array(OA,8,9, t=3) Traceback (most recent call last): ... NotImplementedError: only implemented for t=2 - sage: is_orthogonal_array([[3]*8],8,9,verbose=True) + sage: is_orthogonal_array([[3]*8],8,9, verbose=True) The number of rows is 1 instead of 9^2=81 False - sage: is_orthogonal_array([[3]*8],8,9,verbose=True,terminology="MOLS") + sage: is_orthogonal_array([[3]*8],8,9, verbose=True, terminology="MOLS") All squares do not have dimension n^2=9^2 False - sage: is_orthogonal_array([[3]*7],8,9,verbose=True) + sage: is_orthogonal_array([[3]*7],8,9, verbose=True) Some row does not have length 8 False - sage: is_orthogonal_array([[3]*7],8,9,verbose=True,terminology="MOLS") + sage: is_orthogonal_array([[3]*7],8,9, verbose=True, terminology="MOLS") The number of squares is not 6 False @@ -77,7 +79,7 @@ def is_orthogonal_array(OA, int k, int n, int t=2, verbose=False, terminology="O sage: from itertools import product sage: n = 0 - sage: for a in product(product((0,1), repeat=3), repeat=4): + sage: for a in product(product((0,1), repeat=3), repeat=4): # needs sage.schemes ....: if is_orthogonal_array(a,3,2): ....: n += 1 sage: n @@ -183,16 +185,16 @@ def is_group_divisible_design(groups,blocks,v,G=None,K=None,lambd=1,verbose=Fals EXAMPLES:: sage: from sage.combinat.designs.designs_pyx import is_group_divisible_design - sage: TD = designs.transversal_design(4,10) # optional - sage.modules - sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] # optional - sage.modules - sage: is_group_divisible_design(groups,TD,40,lambd=1) # optional - sage.modules + sage: TD = designs.transversal_design(4,10) # needs sage.modules + sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] + sage: is_group_divisible_design(groups,TD,40,lambd=1) # needs sage.modules True TESTS:: - sage: TD = designs.transversal_design(4,10) # optional - sage.modules - sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] # optional - sage.modules - sage: is_group_divisible_design(groups, TD, 40, lambd=2, verbose=True) # optional - sage.modules + sage: TD = designs.transversal_design(4,10) # needs sage.modules + sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] + sage: is_group_divisible_design(groups, TD, 40, lambd=2, verbose=True) # needs sage.modules the pair (0,10) has been seen 1 times but lambda=2 False sage: is_group_divisible_design([[1,2],[3,4]],[[1,2]],40,lambd=1,verbose=True) @@ -214,6 +216,7 @@ def is_group_divisible_design(groups,blocks,v,G=None,K=None,lambd=1,verbose=Fals a block has size 2 while K=[1] False + sage: # needs sage.schemes sage: p = designs.projective_plane(3) sage: is_group_divisible_design(None, p.blocks(), 13) (True, [[0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12]]) @@ -362,24 +365,24 @@ def is_pairwise_balanced_design(blocks,v,K=None,lambd=1,verbose=False): sage: sts = designs.steiner_triple_system(9) sage: is_pairwise_balanced_design(sts,9,[3],1) True - sage: TD = designs.transversal_design(4,10).blocks() # optional - sage.modules - sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] # optional - sage.modules - sage: is_pairwise_balanced_design(TD + groups, 40, [4,10], 1, verbose=True) # optional - sage.modules + sage: TD = designs.transversal_design(4,10).blocks() # needs sage.modules + sage: groups = [list(range(i*10,(i+1)*10)) for i in range(4)] + sage: is_pairwise_balanced_design(TD + groups, 40, [4,10], 1, verbose=True) # needs sage.modules True TESTS:: sage: from sage.combinat.designs.designs_pyx import is_pairwise_balanced_design - sage: is_pairwise_balanced_design(TD + groups, 40, [4,10], 2, verbose=True) # optional - sage.modules + sage: is_pairwise_balanced_design(TD + groups, 40, [4,10], 2, verbose=True) # needs sage.modules the pair (0,1) has been seen 1 times but lambda=2 False - sage: is_pairwise_balanced_design(TD + groups, 40, [10], 1, verbose=True) # optional - sage.modules + sage: is_pairwise_balanced_design(TD + groups, 40, [10], 1, verbose=True) # needs sage.modules a block has size 4 while K=[10] False - sage: is_pairwise_balanced_design([[2,2]],40,[2],1,verbose=True) + sage: is_pairwise_balanced_design([[2,2]], 40, [2], 1, verbose=True) The following block has repeated elements: [2, 2] False - sage: is_pairwise_balanced_design([["e",2]],40,[2],1,verbose=True) + sage: is_pairwise_balanced_design([["e",2]], 40, [2], 1, verbose=True) e does not belong to [0,...,39] False """ @@ -415,11 +418,12 @@ def is_projective_plane(blocks, verbose=False): EXAMPLES:: sage: from sage.combinat.designs.designs_pyx import is_projective_plane - sage: p = designs.projective_plane(4) - sage: b = p.blocks() - sage: is_projective_plane(b, verbose=True) + sage: p = designs.projective_plane(4) # needs sage.schemes + sage: b = p.blocks() # needs sage.schemes + sage: is_projective_plane(b, verbose=True) # needs sage.schemes True + sage: # needs sage.schemes sage: p = designs.projective_plane(2) sage: b = p.blocks() sage: is_projective_plane(b) @@ -437,6 +441,7 @@ def is_projective_plane(blocks, verbose=False): First block has less than 3 points. False + sage: # needs sage.schemes sage: p = designs.projective_plane(2) sage: b = p.blocks() sage: b[2].append(4) @@ -486,38 +491,39 @@ def is_difference_matrix(M,G,k,lmbda=1,verbose=False): sage: from sage.combinat.designs.designs_pyx import is_difference_matrix sage: q = 3**3 - sage: F = GF(q,'x') # optional - sage.rings.finite_rings - sage: M = [[x*y for y in F] for x in F] # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,F,q,verbose=1) # optional - sage.rings.finite_rings + sage: F = GF(q,'x') # needs sage.rings.finite_rings + sage: M = [[x*y for y in F] for x in F] # needs sage.rings.finite_rings + sage: is_difference_matrix(M,F,q,verbose=1) # needs sage.rings.finite_rings True sage: B = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ....: [0, 1, 2, 3, 4, 2, 3, 4, 0, 1], ....: [0, 2, 4, 1, 3, 3, 0, 2, 4, 1]] - sage: G = GF(5) # optional - sage.rings.finite_rings - sage: B = [[G(b) for b in R] for R in B] # optional - sage.rings.finite_rings - sage: is_difference_matrix(list(zip(*B)),G,3,2) # optional - sage.rings.finite_rings + sage: G = GF(5) + sage: B = [[G(b) for b in R] for R in B] + sage: is_difference_matrix(list(zip(*B)),G,3,2) True Bad input:: - sage: for R in M: R.append(None) # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,F,q,verbose=1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: for R in M: R.append(None) + sage: is_difference_matrix(M,F,q,verbose=1) The matrix has 28 columns but k=27 False - sage: for R in M: _=R.pop(-1) # optional - sage.rings.finite_rings - sage: M.append([None]*3**3) # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,F,q,verbose=1) # optional - sage.rings.finite_rings + sage: for R in M: _=R.pop(-1) + sage: M.append([None]*3**3) + sage: is_difference_matrix(M,F,q,verbose=1) The matrix has 28 rows instead of lambda(|G|-1+2u)+mu=1(27-1+2.0)+1=27 False - sage: _= M.pop(-1) # optional - sage.rings.finite_rings - sage: for R in M: R[-1] = 0 # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,F,q,verbose=1) # optional - sage.rings.finite_rings + sage: _= M.pop(-1) + sage: for R in M: R[-1] = 0 + sage: is_difference_matrix(M,F,q,verbose=1) Columns 0 and 26 generate 0 exactly 27 times instead of the expected mu(=1) False - sage: for R in M: R[-1] = 1 # optional - sage.rings.finite_rings - sage: M[-1][-1] = 0 # optional - sage.rings.finite_rings - sage: is_difference_matrix(M,F,q,verbose=1) # optional - sage.rings.finite_rings + sage: for R in M: R[-1] = 1 + sage: M[-1][-1] = 0 + sage: is_difference_matrix(M,F,q,verbose=1) Columns 0 and 26 do not generate all elements of G exactly lambda(=1) times. The element x appeared 0 times as a difference. False @@ -560,17 +566,17 @@ def is_quasi_difference_matrix(M,G,int k,int lmbda,int mu,int u,verbose=False): sage: from sage.combinat.designs.designs_pyx import is_quasi_difference_matrix sage: q = 3**3 - sage: F = GF(q,'x') # optional - sage.rings.finite_rings - sage: M = [[x*y for y in F] for x in F] # optional - sage.rings.finite_rings - sage: is_quasi_difference_matrix(M,F,q,1,1,0,verbose=1) # optional - sage.rings.finite_rings + sage: F = GF(q,'x') # needs sage.rings.finite_rings + sage: M = [[x*y for y in F] for x in F] # needs sage.rings.finite_rings + sage: is_quasi_difference_matrix(M,F,q,1,1,0,verbose=1) # needs sage.rings.finite_rings True sage: B = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ....: [0, 1, 2, 3, 4, 2, 3, 4, 0, 1], ....: [0, 2, 4, 1, 3, 3, 0, 2, 4, 1]] - sage: G = GF(5) # optional - sage.rings.finite_rings - sage: B = [[G(b) for b in R] for R in B] # optional - sage.rings.finite_rings - sage: is_quasi_difference_matrix(list(zip(*B)),G,3,2,2,0) # optional - sage.rings.finite_rings + sage: G = GF(5) + sage: B = [[G(b) for b in R] for R in B] + sage: is_quasi_difference_matrix(list(zip(*B)),G,3,2,2,0) True A quasi-difference matrix from the database:: diff --git a/src/sage/combinat/designs/difference_family.py b/src/sage/combinat/designs/difference_family.py index 080439ce6bc..382240627ab 100644 --- a/src/sage/combinat/designs/difference_family.py +++ b/src/sage/combinat/designs/difference_family.py @@ -5,7 +5,7 @@ This module gathers everything related to difference families. One can build a difference family (or check that it can be built) with :func:`difference_family`:: - sage: G,F = designs.difference_family(13,4,1) # optional - sage.modules + sage: G,F = designs.difference_family(13,4,1) # needs sage.libs.pari sage.modules It defines the following functions: @@ -70,9 +70,9 @@ def group_law(G): sage: from sage.combinat.designs.difference_family import group_law sage: group_law(Zmod(3)) (0, , ) - sage: group_law(SymmetricGroup(5)) # optional - sage.groups + sage: group_law(SymmetricGroup(5)) # needs sage.groups ((), , ) - sage: group_law(VectorSpace(QQ, 3)) # optional - sage.modules + sage: group_law(VectorSpace(QQ, 3)) # needs sage.modules ((0, 0, 0), , ) """ import operator @@ -176,13 +176,14 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): sage: is_difference_family(G, D) True - sage: G = AdditiveAbelianGroup([3]*4) # optional - sage.modules - sage: a,b,c,d = G.gens() # optional - sage.modules - sage: D = [[d, -a+d, -c+d, a-b-d, b+c+d], # optional - sage.modules + sage: # needs sage.modules + sage: G = AdditiveAbelianGroup([3]*4) + sage: a,b,c,d = G.gens() + sage: D = [[d, -a+d, -c+d, a-b-d, b+c+d], ....: [c, a+b-d, -b+c, a-b+d, a+b+c], ....: [-a-b+c+d, a-b-c-d, -a+c-d, b-c+d, a+b], ....: [-b-d, a+b+d, a-b+c-d, a-b+c, -b+c+d]] - sage: is_difference_family(G, D) # optional - sage.modules + sage: is_difference_family(G, D) True The following example has a third block with a non-trivial stabilizer:: @@ -195,23 +196,25 @@ def is_difference_family(G, D, v=None, k=None, l=None, verbose=False): The function also supports multiplicative groups (non necessarily Abelian):: - sage: G = DihedralGroup(8) # optional - sage.groups - sage: x,y = G.gens() # optional - sage.groups - sage: i = G.one() # optional - sage.groups - sage: D1 = [[i,x,x^4], [i,x^2, y*x], [i,x^5,y], [i,x^6,y*x^2], [i,x^7,y*x^5]] # optional - sage.groups - sage: is_difference_family(G, D1, 16, 3, 2) # optional - sage.groups + sage: # needs sage.groups + sage: G = DihedralGroup(8) + sage: x,y = G.gens() + sage: i = G.one() + sage: D1 = [[i,x,x^4], [i,x^2, y*x], [i,x^5,y], [i,x^6,y*x^2], [i,x^7,y*x^5]] + sage: is_difference_family(G, D1, 16, 3, 2) True sage: from sage.combinat.designs.bibd import BIBD_from_difference_family - sage: bibd = BIBD_from_difference_family(G, D1, lambd=2) # optional - sage.groups + sage: bibd = BIBD_from_difference_family(G, D1, lambd=2) TESTS:: - sage: K = GF(3^2,'z') # optional - sage.rings.finite_rings - sage: z = K.gen() # optional - sage.rings.finite_rings - sage: D = [[1,z+1,2]] # optional - sage.rings.finite_rings - sage: _ = is_difference_family(K, D, verbose=True) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K = GF(3^2,'z') + sage: z = K.gen() + sage: D = [[1,z+1,2]] + sage: _ = is_difference_family(K, D, verbose=True) the number of differences (=6) must be a multiple of v-1=8 - sage: _ # optional - sage.rings.finite_rings + sage: _ False """ identity, mul, inv = group_law(G) @@ -353,23 +356,23 @@ def singer_difference_set(q,d): EXAMPLES:: sage: from sage.combinat.designs.difference_family import singer_difference_set, is_difference_family - sage: G,D = singer_difference_set(3,2) # optional - sage.rings.finite_rings - sage: is_difference_family(G, D, verbose=True) # optional - sage.rings.finite_rings + sage: G,D = singer_difference_set(3,2) # needs sage.rings.finite_rings + sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings It is a (13,4,1)-difference family True - sage: G,D = singer_difference_set(4,2) # optional - sage.rings.finite_rings - sage: is_difference_family(G, D, verbose=True) # optional - sage.rings.finite_rings + sage: G,D = singer_difference_set(4,2) # needs sage.rings.finite_rings + sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings It is a (21,5,1)-difference family True - sage: G,D = singer_difference_set(3,3) # optional - sage.rings.finite_rings - sage: is_difference_family(G, D, verbose=True) # optional - sage.rings.finite_rings + sage: G,D = singer_difference_set(3,3) # needs sage.rings.finite_rings + sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings It is a (40,13,4)-difference family True - sage: G,D = singer_difference_set(9,3) # optional - sage.rings.finite_rings - sage: is_difference_family(G, D, verbose=True) # optional - sage.rings.finite_rings + sage: G,D = singer_difference_set(9,3) # needs sage.rings.finite_rings + sage: is_difference_family(G, D, verbose=True) # needs sage.rings.finite_rings It is a (820,91,10)-difference family True """ @@ -417,13 +420,14 @@ def df_q_6_1(K, existence=False, check=True): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: from sage.combinat.designs.difference_family import is_difference_family, df_q_6_1 - sage: prime_powers = [v for v in range(31,500,30) if is_prime_power(v)] # optional - sage.rings.finite_rings - sage: parameters = [v for v in prime_powers # optional - sage.rings.finite_rings + sage: prime_powers = [v for v in range(31,500,30) if is_prime_power(v)] + sage: parameters = [v for v in prime_powers ....: if df_q_6_1(GF(v,'a'), existence=True) is True] - sage: parameters # optional - sage.rings.finite_rings + sage: parameters [31, 151, 181, 211, 241, 271, 331, 361, 421] - sage: for v in parameters: # optional - sage.rings.finite_rings + sage: for v in parameters: ....: K = GF(v, 'a') ....: df = df_q_6_1(K, check=True) ....: assert is_difference_family(K, df, v, 6, 1) @@ -479,13 +483,13 @@ def radical_difference_set(K, k, l=1, existence=False, check=True): sage: from sage.combinat.designs.difference_family import radical_difference_set - sage: D = radical_difference_set(GF(7), 3, 1); D # optional - sage.rings.finite_rings + sage: D = radical_difference_set(GF(7), 3, 1); D # needs sage.rings.finite_rings [[1, 2, 4]] - sage: sorted(x-y for x in D[0] for y in D[0] if x != y) # optional - sage.rings.finite_rings + sage: sorted(x-y for x in D[0] for y in D[0] if x != y) # needs sage.rings.finite_rings [1, 2, 3, 4, 5, 6] - sage: D = radical_difference_set(GF(16,'a'), 6, 2) # optional - sage.rings.finite_rings - sage: sorted(x-y for x in D[0] for y in D[0] if x != y) # optional - sage.rings.finite_rings + sage: D = radical_difference_set(GF(16,'a'), 6, 2) # needs sage.rings.finite_rings + sage: sorted(x-y for x in D[0] for y in D[0] if x != y) # needs sage.rings.finite_rings [1, 1, a, @@ -498,7 +502,7 @@ def radical_difference_set(K, k, l=1, existence=False, check=True): a^3 + a^2 + a + 1, a^3 + a^2 + a + 1] - sage: for k in range(2,50): # optional - sage.rings.finite_rings + sage: for k in range(2,50): # needs sage.rings.finite_rings ....: for l in reversed(divisors(k*(k-1))): ....: v = k*(k-1)//l + 1 ....: if is_prime_power(v) and radical_difference_set(GF(v,'a'),k,l,existence=True) is True: @@ -757,12 +761,12 @@ def one_radical_difference_family(K, k): ....: one_radical_difference_family, ....: is_difference_family) - sage: one_radical_difference_family(GF(13),4) # optional - sage.rings.finite_rings + sage: one_radical_difference_family(GF(13),4) # needs sage.rings.finite_rings [[0, 1, 3, 9]] The parameters that appear in [Bu95]_:: - sage: df = one_radical_difference_family(GF(449), 8); df # optional - sage.rings.finite_rings + sage: df = one_radical_difference_family(GF(449), 8); df # needs sage.rings.finite_rings [[0, 1, 18, 25, 176, 324, 359, 444], [0, 9, 88, 162, 222, 225, 237, 404], [0, 11, 140, 198, 275, 357, 394, 421], @@ -771,7 +775,7 @@ def one_radical_difference_family(K, k): [0, 70, 99, 197, 230, 362, 403, 435], [0, 121, 141, 193, 293, 331, 335, 382], [0, 191, 285, 295, 321, 371, 390, 392]] - sage: is_difference_family(GF(449), df, 449, 8, 1) # optional - sage.rings.finite_rings + sage: is_difference_family(GF(449), df, 449, 8, 1) # needs sage.rings.finite_rings True """ q = K.cardinality() @@ -843,10 +847,10 @@ def radical_difference_family(K, k, l=1, existence=False, check=True): sage: from sage.combinat.designs.difference_family import radical_difference_family - sage: radical_difference_family(GF(73), 9) # optional - sage.rings.finite_rings + sage: radical_difference_family(GF(73), 9) # needs sage.rings.finite_rings [[1, 2, 4, 8, 16, 32, 37, 55, 64]] - sage: radical_difference_family(GF(281), 5) # optional - sage.rings.finite_rings + sage: radical_difference_family(GF(281), 5) # needs sage.rings.finite_rings [[1, 86, 90, 153, 232], [4, 50, 63, 79, 85], [5, 36, 149, 169, 203], @@ -862,7 +866,7 @@ def radical_difference_family(K, k, l=1, existence=False, check=True): [111, 123, 155, 181, 273], [156, 209, 224, 264, 271]] - sage: for k in range(5,10): # optional - sage.rings.finite_rings + sage: for k in range(5,10): # needs sage.rings.finite_rings ....: print("k = {}".format(k)) ....: list_q = [] ....: for q in range(k*(k-1)+1, 2000, k*(k-1)): @@ -946,10 +950,10 @@ def twin_prime_powers_difference_set(p, check=True): EXAMPLES:: sage: from sage.combinat.designs.difference_family import twin_prime_powers_difference_set - sage: G, D = twin_prime_powers_difference_set(3) # optional - sage.rings.finite_rings - sage: G # optional - sage.rings.finite_rings + sage: G, D = twin_prime_powers_difference_set(3) + sage: G The Cartesian product of (Finite Field of size 3, Finite Field of size 5) - sage: D # optional - sage.rings.finite_rings + sage: D [[(1, 1), (1, 4), (2, 2), (2, 3), (0, 0), (1, 0), (2, 0)]] """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -997,20 +1001,21 @@ def are_mcfarland_1973_parameters(v, k, lmbda, return_parameters=False): EXAMPLES:: + sage: # needs sage.rings.finite_rings sage: from sage.combinat.designs.difference_family import are_mcfarland_1973_parameters - sage: are_mcfarland_1973_parameters(64, 28, 12) # optional - sage.rings.finite_rings + sage: are_mcfarland_1973_parameters(64, 28, 12) True - sage: are_mcfarland_1973_parameters(64, 28, 12, return_parameters=True) # optional - sage.rings.finite_rings + sage: are_mcfarland_1973_parameters(64, 28, 12, return_parameters=True) (True, (2, 2)) - sage: are_mcfarland_1973_parameters(60, 13, 5) # optional - sage.rings.finite_rings + sage: are_mcfarland_1973_parameters(60, 13, 5) False - sage: are_mcfarland_1973_parameters(98125, 19500, 3875) # optional - sage.rings.finite_rings + sage: are_mcfarland_1973_parameters(98125, 19500, 3875) True - sage: are_mcfarland_1973_parameters(98125, 19500, 3875, True) # optional - sage.rings.finite_rings + sage: are_mcfarland_1973_parameters(98125, 19500, 3875, True) (True, (5, 3)) sage: from sage.combinat.designs.difference_family import are_mcfarland_1973_parameters - sage: for v in range(1, 100): # optional - sage.rings.finite_rings + sage: for v in range(1, 100): # needs sage.rings.finite_rings ....: for k in range(1,30): ....: for l in range(1,15): ....: if are_mcfarland_1973_parameters(v,k,l): @@ -1088,11 +1093,11 @@ def mcfarland_1973_construction(q, s): sage: from sage.combinat.designs.difference_family import ( ....: mcfarland_1973_construction, is_difference_family) - sage: G,D = mcfarland_1973_construction(3, 1) # optional - sage.modules sage.rings.finite_rings - sage: assert is_difference_family(G, D, 45, 12, 3) # optional - sage.modules sage.rings.finite_rings + sage: G,D = mcfarland_1973_construction(3, 1) # needs sage.modules + sage: assert is_difference_family(G, D, 45, 12, 3) # needs sage.modules - sage: G,D = mcfarland_1973_construction(2, 2) # optional - sage.modules sage.rings.finite_rings - sage: assert is_difference_family(G, D, 64, 28, 12) # optional - sage.modules sage.rings.finite_rings + sage: G,D = mcfarland_1973_construction(2, 2) # needs sage.modules + sage: assert is_difference_family(G, D, 64, 28, 12) # needs sage.modules """ from sage.rings.finite_rings.finite_field_constructor import GF from sage.modules.free_module import VectorSpace @@ -1151,7 +1156,7 @@ def hadamard_difference_set_product_parameters(N): EXAMPLES:: sage: from sage.combinat.designs.difference_family import hadamard_difference_set_product_parameters - sage: hadamard_difference_set_product_parameters(8) # optional - sage.rings.finite_rings + sage: hadamard_difference_set_product_parameters(8) # needs sage.rings.finite_rings (2, 2) """ if N % 2: @@ -1191,16 +1196,16 @@ def hadamard_difference_set_product(G1, D1, G2, D2): sage: from sage.combinat.designs.difference_family import hadamard_difference_set_product sage: from sage.combinat.designs.difference_family import is_difference_family - sage: G1,D1 = designs.difference_family(16,6,2) # optional - sage.rings.finite_rings - sage: G2,D2 = designs.difference_family(36,15,6) # optional - sage.rings.finite_rings + sage: G1,D1 = designs.difference_family(16,6,2) # needs sage.rings.finite_rings + sage: G2,D2 = designs.difference_family(36,15,6) # needs sage.rings.finite_rings - sage: G11,D11 = hadamard_difference_set_product(G1,D1,G1,D1) # optional - sage.rings.finite_rings - sage: assert is_difference_family(G11, D11, 256, 120, 56) # optional - sage.rings.finite_rings - sage: assert designs.difference_family(256, 120, 56, existence=True) is True # optional - sage.rings.finite_rings + sage: G11,D11 = hadamard_difference_set_product(G1,D1,G1,D1) # needs sage.rings.finite_rings + sage: assert is_difference_family(G11, D11, 256, 120, 56) # needs sage.rings.finite_rings + sage: assert designs.difference_family(256, 120, 56, existence=True) is True # needs sage.rings.finite_rings - sage: G12,D12 = hadamard_difference_set_product(G1,D1,G2,D2) # optional - sage.rings.finite_rings - sage: assert is_difference_family(G12, D12, 576, 276, 132) # optional - sage.rings.finite_rings - sage: assert designs.difference_family(576, 276, 132, existence=True) is True # optional - sage.rings.finite_rings + sage: G12,D12 = hadamard_difference_set_product(G1,D1,G2,D2) # needs sage.rings.finite_rings + sage: assert is_difference_family(G12, D12, 576, 276, 132) # needs sage.rings.finite_rings + sage: assert designs.difference_family(576, 276, 132, existence=True) is True # needs sage.rings.finite_rings """ from sage.categories.cartesian_product import cartesian_product @@ -1317,11 +1322,11 @@ def _create_m_sequence(q, n, check=True): EXAMPLES:: sage: from sage.combinat.designs.difference_family import _create_m_sequence - sage: _create_m_sequence(3, 2) # random # optional - sage.rings.finite_rings + sage: _create_m_sequence(3, 2) # random # needs sage.rings.finite_rings [1, 0, 1, 2, 2, 0, 2, 1] - sage: _create_m_sequence(4, 2, check=False) # random # optional - sage.rings.finite_rings + sage: _create_m_sequence(4, 2, check=False) # random # needs sage.rings.finite_rings [1, 0, a, a + 1, a, a, 0, a + 1, 1, a + 1, a + 1, 0, 1, a, 1] - sage: _create_m_sequence(6, 2) # optional - sage.rings.finite_rings + sage: _create_m_sequence(6, 2) Traceback (most recent call last): ... ValueError: q must be a prime power @@ -1371,12 +1376,13 @@ def _get_submodule_of_order(G, order): TESTS: + sage: # needs sage.modules sage: from sage.combinat.designs.difference_family import _get_submodule_of_order - sage: G = AdditiveAbelianGroup([48]) # optional - sage.modules - sage: _get_submodule_of_order(G, 6).order() # optional - sage.modules + sage: G = AdditiveAbelianGroup([48]) + sage: _get_submodule_of_order(G, 6).order() 6 - sage: G = AdditiveAbelianGroup([13^2 - 1]) # optional - sage.modules - sage: _get_submodule_of_order(G, 12).order() # optional - sage.modules + sage: G = AdditiveAbelianGroup([13^2 - 1]) + sage: _get_submodule_of_order(G, 12).order() 12 """ for el in G: @@ -1410,13 +1416,13 @@ def relative_difference_set_from_m_sequence(q, N, check=True, return_group=False EXAMPLES:: sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence - sage: relative_difference_set_from_m_sequence(2, 4, # random # optional - sage.modules sage.rings.finite_rings + sage: relative_difference_set_from_m_sequence(2, 4, # random # needs sage.modules sage.rings.finite_rings ....: return_group=True) (Additive abelian group isomorphic to Z/15, [(0), (4), (5), (6), (7), (9), (11), (12)]) - sage: relative_difference_set_from_m_sequence(8, 2, check=False) # random # optional - sage.modules sage.rings.finite_rings + sage: relative_difference_set_from_m_sequence(8, 2, check=False) # random # needs sage.modules sage.rings.finite_rings [(0), (6), (30), (40), (41), (44), (56), (61)] - sage: relative_difference_set_from_m_sequence(6, 2) # optional - sage.modules sage.rings.finite_rings + sage: relative_difference_set_from_m_sequence(6, 2) # needs sage.modules Traceback (most recent call last): ... ValueError: q must be a prime power @@ -1425,17 +1431,17 @@ def relative_difference_set_from_m_sequence(q, N, check=True, return_group=False sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order sage: q, N = 5, 3 - sage: G, D = relative_difference_set_from_m_sequence(q, N, check=False, # optional - sage.modules sage.rings.finite_rings + sage: G, D = relative_difference_set_from_m_sequence(q, N, check=False, # needs sage.modules sage.rings.finite_rings ....: return_group=True) - sage: H = _get_submodule_of_order(G, q-1) # optional - sage.modules sage.rings.finite_rings - sage: is_relative_difference_set(D, G, H, # optional - sage.modules sage.rings.finite_rings + sage: H = _get_submodule_of_order(G, q-1) # needs sage.modules sage.rings.finite_rings + sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings ....: ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2))) True sage: q, N = 13, 2 - sage: G, D = relative_difference_set_from_m_sequence(q, N, check=False, # optional - sage.modules sage.rings.finite_rings + sage: G, D = relative_difference_set_from_m_sequence(q, N, check=False, # needs sage.modules sage.rings.finite_rings ....: return_group=True) - sage: H = _get_submodule_of_order(G, q-1) # optional - sage.modules sage.rings.finite_rings - sage: is_relative_difference_set(D, G, H, # optional - sage.modules sage.rings.finite_rings + sage: H = _get_submodule_of_order(G, q-1) # needs sage.modules sage.rings.finite_rings + sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings ....: ((q^N-1)//(q-1), q-1, q^(N-1), q^(N-2))) True """ @@ -1485,12 +1491,13 @@ def relative_difference_set_from_homomorphism(q, N, d, check=True, return_group= EXAMPLES:: sage: from sage.combinat.designs.difference_family import relative_difference_set_from_homomorphism - sage: relative_difference_set_from_homomorphism(7, 2, 3) #random + sage: relative_difference_set_from_homomorphism(7, 2, 3) # random # needs sage.modules sage.rings.finite_rings [(0), (3), (4), (2), (13), (7), (14)] - sage: relative_difference_set_from_homomorphism(9, 2, 4, check=False, return_group=True) #random + sage: relative_difference_set_from_homomorphism(9, 2, 4, # random # needs sage.modules sage.rings.finite_rings + ....: check=False, return_group=True) (Additive abelian group isomorphic to Z/80, [(0), (4), (6), (13), (7), (12), (15), (8), (9)]) - sage: relative_difference_set_from_homomorphism(9, 2, 5) # optional - sage.modules sage.rings.finite_rings + sage: relative_difference_set_from_homomorphism(9, 2, 5) # needs sage.modules sage.rings.finite_rings Traceback (most recent call last): ... ValueError: q-1 must be a multiple of d @@ -1499,17 +1506,17 @@ def relative_difference_set_from_homomorphism(q, N, d, check=True, return_group= sage: from sage.combinat.designs.difference_family import is_relative_difference_set, _get_submodule_of_order sage: q, N, d = 11, 2, 5 - sage: G, D = relative_difference_set_from_homomorphism(q, N, d, check=False, # optional - sage.modules sage.rings.finite_rings + sage: G, D = relative_difference_set_from_homomorphism(q, N, d, check=False, # needs sage.modules sage.rings.finite_rings ....: return_group=True) - sage: H = _get_submodule_of_order(G, (q-1)//d) # optional - sage.modules sage.rings.finite_rings - sage: is_relative_difference_set(D, G, H, # optional - sage.modules sage.rings.finite_rings + sage: H = _get_submodule_of_order(G, (q-1)//d) # needs sage.modules sage.rings.finite_rings + sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings ....: ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d)) True sage: q, N, d = 9, 2, 4 - sage: G, D = relative_difference_set_from_homomorphism(q, N, d, check=False, # optional - sage.modules sage.rings.finite_rings + sage: G, D = relative_difference_set_from_homomorphism(q, N, d, check=False, # needs sage.modules sage.rings.finite_rings ....: return_group=True) - sage: H = _get_submodule_of_order(G, (q-1)//d) # optional - sage.modules sage.rings.finite_rings - sage: is_relative_difference_set(D, G, H, # optional - sage.modules sage.rings.finite_rings + sage: H = _get_submodule_of_order(G, (q-1)//d) # needs sage.modules sage.rings.finite_rings + sage: is_relative_difference_set(D, G, H, # needs sage.modules sage.rings.finite_rings ....: ((q**N-1)//(q-1), (q-1)//d, q**(N-1), q**(N-2)*d)) True """ @@ -1540,6 +1547,7 @@ def relative_difference_set_from_homomorphism(q, N, d, check=True, return_group= return G2, second_diff_set return second_diff_set + def is_relative_difference_set(R, G, H, params, verbose=False): r""" Check if ``R`` is a difference set of ``G`` relative to ``H``, with the given parameters. @@ -1563,15 +1571,15 @@ def is_relative_difference_set(R, G, H, params, verbose=False): sage: from sage.combinat.designs.difference_family import _get_submodule_of_order, relative_difference_set_from_m_sequence, is_relative_difference_set sage: q, N = 5, 2 sage: params = ((q^N-1) // (q-1), q - 1, q^(N-1), q^(N-2)) - sage: G, R = relative_difference_set_from_m_sequence(q, N, return_group=True) # optional - sage.modules - sage: H = _get_submodule_of_order(G, q - 1) # optional - sage.modules - sage: is_relative_difference_set(R, G, H, params) # optional - sage.modules + sage: G, R = relative_difference_set_from_m_sequence(q, N, return_group=True) # needs sage.libs.pari sage.modules + sage: H = _get_submodule_of_order(G, q - 1) # needs sage.libs.pari sage.modules + sage: is_relative_difference_set(R, G, H, params) # needs sage.libs.pari sage.modules True If we pass the ``verbose`` argument, the function will explain why it failed:: - sage: R2 = [G[1], G[2], G[3], G[5], G[6]] # optional - sage.modules - sage: is_relative_difference_set(R2, G, H, params, verbose=True) # optional - sage.modules + sage: R2 = [G[1], G[2], G[3], G[5], G[6]] # needs sage.libs.pari sage.modules + sage: is_relative_difference_set(R2, G, H, params, verbose=True) # needs sage.libs.pari sage.modules There is a value in the difference set which is not repeated d times False """ @@ -1641,33 +1649,34 @@ def is_supplementary_difference_set(Ks, v=None, lmbda=None, G=None, verbose=Fals EXAMPLES:: sage: from sage.combinat.designs.difference_family import supplementary_difference_set_from_rel_diff_set, is_supplementary_difference_set - sage: G, [S1, S2, S3, S4] = supplementary_difference_set_from_rel_diff_set(17) # optional - sage.modules sage.rings.finite_rings - sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=16, G=G) # optional - sage.modules sage.rings.finite_rings + sage: G, [S1, S2, S3, S4] = supplementary_difference_set_from_rel_diff_set(17) # needs sage.modules sage.rings.finite_rings + sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=16, G=G) # needs sage.modules sage.rings.finite_rings True The parameter ``v`` can be given instead of ``G``:: - sage: is_supplementary_difference_set([S1, S2, S3, S4], v=16, lmbda=16) # optional - sage.modules sage.rings.finite_rings + sage: is_supplementary_difference_set([S1, S2, S3, S4], v=16, lmbda=16) # needs sage.modules sage.rings.finite_rings True - sage: is_supplementary_difference_set([S1, S2, S3, S4], v=20, lmbda=16) # optional - sage.modules sage.rings.finite_rings + sage: is_supplementary_difference_set([S1, S2, S3, S4], v=20, lmbda=16) # needs sage.modules sage.rings.finite_rings False If ``verbose=True``, the function will be verbose:: - sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=14, G=G, # optional - sage.modules sage.rings.finite_rings + sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=14, G=G, # needs sage.modules sage.rings.finite_rings ....: verbose=True) Number of pairs with difference (1) is 16, but lambda is 14 False TESTS:: - sage: is_supplementary_difference_set([[1], [1]], lmbda=0, G=Zmod(3)) # optional - sage.modules sage.rings.finite_rings + sage: # needs sage.modules sage.rings.finite_rings + sage: is_supplementary_difference_set([[1], [1]], lmbda=0, G=Zmod(3)) True - sage: is_supplementary_difference_set([S1, S2, S3, S4], v=17, lmbda=16, G=G) # optional - sage.modules sage.rings.finite_rings + sage: is_supplementary_difference_set([S1, S2, S3, S4], v=17, lmbda=16, G=G) False - sage: is_supplementary_difference_set([S1, S2, S3, S4], G=G) # optional - sage.modules sage.rings.finite_rings + sage: is_supplementary_difference_set([S1, S2, S3, S4], G=G) True - sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=16) # optional - sage.modules sage.rings.finite_rings + sage: is_supplementary_difference_set([S1, S2, S3, S4], lmbda=16) Traceback (most recent call last): ... ValueError: one of G or v must be specified @@ -1740,7 +1749,7 @@ def supplementary_difference_set_from_rel_diff_set(q, existence=False, check=Tru EXAMPLES:: sage: from sage.combinat.designs.difference_family import supplementary_difference_set_from_rel_diff_set - sage: supplementary_difference_set_from_rel_diff_set(17) #random + sage: supplementary_difference_set_from_rel_diff_set(17) #random # needs sage.libs.pari (Additive abelian group isomorphic to Z/16, [[(1), (5), (6), (7), (9), (13), (14), (15)], [(0), (2), (3), (5), (6), (10), (11), (13), (14)], @@ -1756,6 +1765,7 @@ def supplementary_difference_set_from_rel_diff_set(q, existence=False, check=Tru TESTS:: + sage: # needs sage.libs.pari sage: from sage.combinat.designs.difference_family import is_supplementary_difference_set sage: G, sets = supplementary_difference_set_from_rel_diff_set(17, check=False) sage: is_supplementary_difference_set(sets, lmbda=16, G=G) @@ -1780,8 +1790,8 @@ def supplementary_difference_set_from_rel_diff_set(q, existence=False, check=Tru Check that the function works even when s > 1:: - sage: G, sets = supplementary_difference_set_from_rel_diff_set(353, check=False) #long time - sage: is_supplementary_difference_set(sets, lmbda=352, G=G) #long time + sage: G, sets = supplementary_difference_set_from_rel_diff_set(353, check=False) # long time, needs sage.libs.pari + sage: is_supplementary_difference_set(sets, lmbda=352, G=G) # long time, needs sage.libs.pari True .. SEEALSO:: @@ -1922,30 +1932,31 @@ def get_fixed_relative_difference_set(G, rel_diff_set, as_elements=False): EXAMPLES:: sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, get_fixed_relative_difference_set - sage: G, s1 = relative_difference_set_from_m_sequence(5, 2, return_group=True) # optional - sage.modules - sage: get_fixed_relative_difference_set(G, s1) # random # optional - sage.modules + sage: G, s1 = relative_difference_set_from_m_sequence(5, 2, return_group=True) # needs sage.libs.pari sage.modules + sage: get_fixed_relative_difference_set(G, s1) # random # needs sage.libs.pari sage.modules [2, 10, 19, 23, 0] If ``as_elements=True``, the result will contain elements of the group:: - sage: get_fixed_relative_difference_set(G, s1, as_elements=True) # random # optional - sage.modules + sage: get_fixed_relative_difference_set(G, s1, as_elements=True) # random # needs sage.libs.pari sage.modules [(2), (10), (19), (23), (0)] TESTS:: + sage: # needs sage.libs.pari sage.modules sage: from sage.combinat.designs.difference_family import is_fixed_relative_difference_set - sage: G, s1 = relative_difference_set_from_m_sequence(5, 2, return_group=True) # optional - sage.modules - sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True) # optional - sage.modules - sage: is_fixed_relative_difference_set(s2, len(s2)) # optional - sage.modules + sage: G, s1 = relative_difference_set_from_m_sequence(5, 2, return_group=True) + sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True) + sage: is_fixed_relative_difference_set(s2, len(s2)) True - sage: G, s1 = relative_difference_set_from_m_sequence(9, 2, return_group=True) # optional - sage.modules - sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True) # optional - sage.modules - sage: is_fixed_relative_difference_set(s2, len(s2)) # optional - sage.modules + sage: G, s1 = relative_difference_set_from_m_sequence(9, 2, return_group=True) + sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True) + sage: is_fixed_relative_difference_set(s2, len(s2)) True - sage: type(s2[0]) # optional - sage.modules + sage: type(s2[0]) - sage: s2 = get_fixed_relative_difference_set(G, s1) # optional - sage.modules - sage: type(s2[0]) # optional - sage.modules + sage: s2 = get_fixed_relative_difference_set(G, s1) + sage: type(s2[0]) """ q = len(rel_diff_set) @@ -1985,21 +1996,22 @@ def is_fixed_relative_difference_set(R, q): EXAMPLES:: + sage: # needs sage.modules sage: from sage.combinat.designs.difference_family import relative_difference_set_from_m_sequence, get_fixed_relative_difference_set, is_fixed_relative_difference_set - sage: G, s1 = relative_difference_set_from_m_sequence(7, 2, return_group=True) # optional - sage.modules - sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True) # optional - sage.modules - sage: is_fixed_relative_difference_set(s2, len(s2)) # optional - sage.modules + sage: G, s1 = relative_difference_set_from_m_sequence(7, 2, return_group=True) # needs sage.libs.pari + sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=True) # needs sage.libs.pari + sage: is_fixed_relative_difference_set(s2, len(s2)) # needs sage.libs.pari True - sage: G = AdditiveAbelianGroup([15]) # optional - sage.modules - sage: s3 = [G[1], G[2], G[3], G[4]] # optional - sage.modules - sage: is_fixed_relative_difference_set(s3, len(s3)) # optional - sage.modules + sage: G = AdditiveAbelianGroup([15]) + sage: s3 = [G[1], G[2], G[3], G[4]] + sage: is_fixed_relative_difference_set(s3, len(s3)) False If the relative difference set does not contain elements of the group, the method returns false:: - sage: G, s1 = relative_difference_set_from_m_sequence(7, 2, return_group=True) # optional - sage.modules - sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=False) # optional - sage.modules - sage: is_fixed_relative_difference_set(s2, len(s2)) # optional - sage.modules + sage: G, s1 = relative_difference_set_from_m_sequence(7, 2, return_group=True) # needs sage.libs.pari sage.modules + sage: s2 = get_fixed_relative_difference_set(G, s1, as_elements=False) # needs sage.libs.pari sage.modules + sage: is_fixed_relative_difference_set(s2, len(s2)) # needs sage.libs.pari sage.modules False """ for el in R: @@ -2034,7 +2046,7 @@ def skew_supplementary_difference_set_over_polynomial_ring(n, existence=False, c EXAMPLES:: sage: from sage.combinat.designs.difference_family import skew_supplementary_difference_set_over_polynomial_ring - sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set_over_polynomial_ring(81) + sage: G, [S1, S2, S3, S4] = skew_supplementary_difference_set_over_polynomial_ring(81) # needs sage.libs.pari If ``existence=True``, the function returns a boolean:: @@ -2830,7 +2842,7 @@ def complementary_difference_setsI(n, check=True): Traceback (most recent call last): ... ValueError: the parameter 17 is not valid - sage: complementary_difference_setsI(15) + sage: complementary_difference_setsI(15) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: the parameter 15 is not valid @@ -2887,11 +2899,12 @@ def complementary_difference_setsII(n, check=True): EXAMPLES:: sage: from sage.combinat.designs.difference_family import complementary_difference_setsII - sage: complementary_difference_setsII(5) + sage: complementary_difference_setsII(5) # needs sage.libs.pari (Finite Field of size 5, [1, 2], [1, 3]) TESTS:: + sage: # needs sage.libs.pari sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets sage: G, A, B = complementary_difference_setsII(25, check=False) sage: are_complementary_difference_sets(G, A, B) @@ -2966,23 +2979,23 @@ def complementary_difference_setsIII(n, check=True): EXAMPLES:: sage: from sage.combinat.designs.difference_family import complementary_difference_setsIII - sage: complementary_difference_setsIII(11) + sage: complementary_difference_setsIII(11) # needs sage.libs.pari (Ring of integers modulo 11, [1, 2, 5, 7, 8], [0, 1, 3, 8, 10]) TESTS:: sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets - sage: G, A, B = complementary_difference_setsIII(21, check=False) - sage: are_complementary_difference_sets(G, A, B) + sage: G, A, B = complementary_difference_setsIII(21, check=False) # needs sage.libs.pari + sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari True - sage: G, A, B = complementary_difference_setsIII(65, check=False) - sage: are_complementary_difference_sets(G, A, B) + sage: G, A, B = complementary_difference_setsIII(65, check=False) # needs sage.libs.pari + sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari True sage: complementary_difference_setsIII(10) Traceback (most recent call last): ... ValueError: the parameter 10 is not valid - sage: complementary_difference_setsIII(17) + sage: complementary_difference_setsIII(17) # needs sage.libs.pari Traceback (most recent call last): ... ValueError: the parameter 17 is not valid @@ -3046,12 +3059,12 @@ def complementary_difference_sets(n, existence=False, check=True): EXAMPLES:: sage: from sage.combinat.designs.difference_family import complementary_difference_sets - sage: complementary_difference_sets(15) + sage: complementary_difference_sets(15) # needs sage.libs.pari (Ring of integers modulo 15, [1, 2, 4, 6, 7, 10, 12], [0, 1, 2, 6, 9, 13, 14]) If ``existence=True``, the function returns a boolean:: - sage: complementary_difference_sets(15, existence=True) + sage: complementary_difference_sets(15, existence=True) # needs sage.libs.pari True sage: complementary_difference_sets(16, existence=True) False @@ -3059,17 +3072,17 @@ def complementary_difference_sets(n, existence=False, check=True): TESTS:: sage: from sage.combinat.designs.difference_family import are_complementary_difference_sets - sage: G, A, B = complementary_difference_sets(29) - sage: are_complementary_difference_sets(G, A, B) + sage: G, A, B = complementary_difference_sets(29) # needs sage.libs.pari + sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari True - sage: G, A, B = complementary_difference_sets(65) - sage: are_complementary_difference_sets(G, A, B) + sage: G, A, B = complementary_difference_sets(65) # needs sage.libs.pari + sage: are_complementary_difference_sets(G, A, B) # needs sage.libs.pari True sage: complementary_difference_sets(10) Traceback (most recent call last): ... ValueError: the parameter n must be odd - sage: complementary_difference_sets(17) + sage: complementary_difference_sets(17) # needs sage.libs.pari Traceback (most recent call last): ... NotImplementedError: complementary difference sets of order 17 are not implemented yet @@ -3150,10 +3163,10 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch EXAMPLES:: - sage: G,D = designs.difference_family(73,4) - sage: G + sage: G,D = designs.difference_family(73,4) # needs sage.libs.pari + sage: G # needs sage.libs.pari Finite Field of size 73 - sage: D + sage: D # needs sage.libs.pari [[0, 1, 5, 18], [0, 3, 15, 54], [0, 9, 45, 16], @@ -3164,6 +3177,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch sage: print(designs.difference_family(73, 4, explain_construction=True)) The database contains a (73,4)-evenly distributed set + sage: # needs sage.libs.pari sage: G,D = designs.difference_family(15,7,3) sage: G Ring of integers modulo 15 @@ -3172,11 +3186,11 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch sage: print(designs.difference_family(15,7,3,explain_construction=True)) Singer difference set - sage: print(designs.difference_family(91,10,1,explain_construction=True)) + sage: print(designs.difference_family(91,10,1,explain_construction=True)) # needs sage.libs.pari Singer difference set - sage: print(designs.difference_family(64,28,12, explain_construction=True)) + sage: print(designs.difference_family(64,28,12, explain_construction=True)) # needs sage.libs.pari McFarland 1973 construction - sage: print(designs.difference_family(576, 276, 132, explain_construction=True)) + sage: print(designs.difference_family(576, 276, 132, explain_construction=True)) # needs sage.libs.pari Hadamard difference set product from N1=2 and N2=3 For `k=6,7` we look at the set of small prime powers for which a @@ -3191,7 +3205,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch sage: from itertools import islice sage: l6 = {True:[], False: [], Unknown: []} - sage: for q in islice(prime_power_mod(1,30), int(60)): + sage: for q in islice(prime_power_mod(1,30), int(60)): # needs sage.libs.pari ....: l6[designs.difference_family(q,6,existence=True)].append(q) sage: l6[True] [31, 121, 151, 181, 211, ..., 3061, 3121, 3181] @@ -3201,7 +3215,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch [] sage: l7 = {True: [], False: [], Unknown: []} - sage: for q in islice(prime_power_mod(1,42), int(60)): + sage: for q in islice(prime_power_mod(1,42), int(60)): # needs sage.libs.pari ....: l7[designs.difference_family(q,7,existence=True)].append(q) sage: l7[True] [169, 337, 379, 421, 463, 547, 631, 673, 757, 841, 883, 967, ..., 4621, 4957, 5167] @@ -3212,7 +3226,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch List available constructions:: - sage: for v in range(2,100): + sage: for v in range(2,100): # needs sage.libs.pari ....: constructions = [] ....: for k in range(2,10): ....: for l in range(1,10): @@ -3289,7 +3303,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch sage: Q15 = [76231] sage: Q4 = [13, 73, 97, 109, 181, 229, 241, 277, 337, 409, 421, 457] sage: Q8 = [1009, 3137, 3697] - sage: for Q,k in [(Q4,4),(Q5,5),(Q8,8),(Q9,9),(Q15,15)]: + sage: for Q,k in [(Q4,4),(Q5,5),(Q8,8),(Q9,9),(Q15,15)]: # needs sage.libs.pari ....: for q in Q: ....: assert designs.difference_family(q,k,1,existence=True) is True ....: _ = designs.difference_family(q,k,1) @@ -3298,7 +3312,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1)) - sage: for q in range(2,10): + sage: for q in range(2,10): # needs sage.libs.pari ....: if is_prime_power(q): ....: for d in [2,3,4]: ....: v,k,l = sgp(q,d) @@ -3307,13 +3321,13 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch Check twin primes difference sets:: - sage: for p in [3,5,7,9,11]: + sage: for p in [3,5,7,9,11]: # needs sage.libs.pari ....: v = p*(p+2); k = (v-1)/2; lmbda = (k-1)/2 ....: G,D = designs.difference_family(v,k,lmbda) Check Complementary difference sets:: - sage: for v in [15, 33, 35, 39, 51]: + sage: for v in [15, 33, 35, 39, 51]: # needs sage.libs.pari ....: G, D = designs.difference_family(v, (v-1)//2, (v-1)//2-1) Check the database:: @@ -3323,14 +3337,14 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch ....: assert designs.difference_family(v,k,l,existence=True) is True ....: df = designs.difference_family(v,k,l,check=True) - sage: for k in EDS: + sage: for k in EDS: # needs sage.libs.pari ....: for v in EDS[k]: ....: assert designs.difference_family(v,k,1,existence=True) is True ....: df = designs.difference_family(v,k,1,check=True) Check the known Hadamard parameters:: - sage: for N in range(2,21): + sage: for N in range(2,21): # needs sage.libs.pari ....: v = 4*N^2; k = 2*N^2-N; l = N^2-N ....: status = designs.difference_family(v,k,l,existence=True) ....: print("{:2} {}".format(N,designs.difference_family(v,k,l,explain_construction=True) if status is True else status)) @@ -3369,7 +3383,7 @@ def difference_family(v, k, l=1, existence=False, explain_construction=False, ch sage: designs.difference_family(3, 2, 1, explain_construction=True) 'Trivial difference family' - sage: for _ in range(100): + sage: for _ in range(100): # needs sage.libs.pari ....: v = randint(1, 30) ....: k = randint(2, 30) ....: l = randint(1, 30) diff --git a/src/sage/combinat/designs/difference_matrices.py b/src/sage/combinat/designs/difference_matrices.py index 6d609fc94f4..8ad1f7bf088 100644 --- a/src/sage/combinat/designs/difference_matrices.py +++ b/src/sage/combinat/designs/difference_matrices.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.finite_rings r""" Difference Matrices @@ -146,12 +147,12 @@ def difference_matrix(g,k,lmbda=1,existence=False,check=True): - ``existence`` (boolean) -- instead of building the design, return: - - ``True`` -- meaning that Sage knows how to build the design + - ``True`` -- meaning that Sage knows how to build the design - - ``Unknown`` -- meaning that Sage does not know how to build the - design, but that the design may exist (see :mod:`sage.misc.unknown`). + - ``Unknown`` -- meaning that Sage does not know how to build the + design, but that the design may exist (see :mod:`sage.misc.unknown`). - - ``False`` -- meaning that the design does not exist. + - ``False`` -- meaning that the design does not exist. .. NOTE:: diff --git a/src/sage/combinat/designs/evenly_distributed_sets.pyx b/src/sage/combinat/designs/evenly_distributed_sets.pyx index a19267a4bdb..36d48d50b30 100644 --- a/src/sage/combinat/designs/evenly_distributed_sets.pyx +++ b/src/sage/combinat/designs/evenly_distributed_sets.pyx @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- +# sage.doctest: needs sage.rings.finite_rings r""" Evenly distributed sets in finite fields @@ -111,7 +111,7 @@ cdef class EvenlyDistributedSetsBacktracker: Or only count them:: - sage: for k in range(13, 200, 12): # optional - sage.rings.finite_rings + sage: for k in range(13, 200, 12): ....: if is_prime_power(k): ....: K = GF(k,'a') ....: E1 = EvenlyDistributedSetsBacktracker(K, 4, False) @@ -357,9 +357,9 @@ cdef class EvenlyDistributedSetsBacktracker: sage: from sage.combinat.designs.evenly_distributed_sets import EvenlyDistributedSetsBacktracker - sage: EvenlyDistributedSetsBacktracker(GF(25,'a'), 4) # optional - sage.rings.finite_rings + sage: EvenlyDistributedSetsBacktracker(GF(25,'a'), 4) 4-evenly distributed sets (up to isomorphism) in Finite Field in a of size 5^2 - sage: EvenlyDistributedSetsBacktracker(GF(25,'a'), 4, # optional - sage.rings.finite_rings + sage: EvenlyDistributedSetsBacktracker(GF(25,'a'), 4, ....: up_to_isomorphism=False) 4-evenly distributed sets in Finite Field in a of size 5^2 """ @@ -379,15 +379,15 @@ cdef class EvenlyDistributedSetsBacktracker: sage: from sage.combinat.designs.evenly_distributed_sets import EvenlyDistributedSetsBacktracker - sage: E = EvenlyDistributedSetsBacktracker(GF(25,'a'), 4); E # optional - sage.rings.finite_rings + sage: E = EvenlyDistributedSetsBacktracker(GF(25,'a'), 4); E 4-evenly distributed sets (up to isomorphism) in Finite Field in a of size 5^2 - sage: E.cardinality() # optional - sage.rings.finite_rings + sage: E.cardinality() 4 - sage: E = EvenlyDistributedSetsBacktracker(GF(25,'a'), 4, # optional - sage.rings.finite_rings + sage: E = EvenlyDistributedSetsBacktracker(GF(25,'a'), 4, ....: up_to_isomorphism=False) - sage: E.cardinality() # optional - sage.rings.finite_rings + sage: E.cardinality() 40 """ cdef n = 0 diff --git a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx index cdc5cc7c472..26da661f3cd 100644 --- a/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx +++ b/src/sage/combinat/designs/gen_quadrangles_with_spread.pyx @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.libs.gap r""" Database of generalised quadrangles with spread diff --git a/src/sage/combinat/designs/group_divisible_designs.py b/src/sage/combinat/designs/group_divisible_designs.py index 738cec0222c..0bc9c38ef00 100644 --- a/src/sage/combinat/designs/group_divisible_designs.py +++ b/src/sage/combinat/designs/group_divisible_designs.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.finite_rings r""" Group-Divisible Designs (GDD) diff --git a/src/sage/combinat/designs/incidence_structures.py b/src/sage/combinat/designs/incidence_structures.py index 3be5a2d8866..83927348b05 100644 --- a/src/sage/combinat/designs/incidence_structures.py +++ b/src/sage/combinat/designs/incidence_structures.py @@ -105,8 +105,8 @@ class IncidenceStructure(): Or by its adjacency matrix (a `\{0,1\}`-matrix in which rows are indexed by points and columns by blocks):: - sage: m = matrix([[0,1,0],[0,0,1],[1,0,1],[1,1,1]]) - sage: IncidenceStructure(m) + sage: m = matrix([[0,1,0],[0,0,1],[1,0,1],[1,1,1]]) # needs sage.modules + sage: IncidenceStructure(m) # needs sage.modules Incidence structure with 4 points and 3 blocks The points can be any (hashable) object:: @@ -160,9 +160,10 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, We avoid to convert to integers when the points are not (but compare equal to integers because of coercion):: + sage: # needs sage.rings.finite_rings sage: V = GF(5) sage: e0,e1,e2,e3,e4 = V - sage: [e0,e1,e2,e3,e4] == list(range(5)) # coercion makes them equal + sage: [e0,e1,e2,e3,e4] == list(range(5)) # coercion makes them equal True sage: blocks = [[e0,e1,e2],[e0,e1],[e2,e4]] sage: I = IncidenceStructure(V, blocks) @@ -282,9 +283,9 @@ def __eq__(self, other): sage: blocks = [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]] sage: BD1 = IncidenceStructure(7, blocks) - sage: M = BD1.incidence_matrix() - sage: BD2 = IncidenceStructure(incidence_matrix=M) - sage: BD1 == BD2 + sage: M = BD1.incidence_matrix() # needs sage.modules + sage: BD2 = IncidenceStructure(incidence_matrix=M) # needs sage.modules + sage: BD1 == BD2 # needs sage.modules True sage: e1 = frozenset([0,1]) @@ -326,9 +327,9 @@ def __ne__(self, other): EXAMPLES:: sage: BD1 = IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: M = BD1.incidence_matrix() - sage: BD2 = IncidenceStructure(incidence_matrix=M) - sage: BD1 != BD2 + sage: M = BD1.incidence_matrix() # needs sage.modules + sage: BD2 = IncidenceStructure(incidence_matrix=M) # needs sage.modules + sage: BD1 != BD2 # needs sage.modules False """ return not self == other @@ -363,8 +364,9 @@ def __contains__(self, block): True sage: ["Am", "I", "finally", "done ?"] in IS False - sage: IS = designs.ProjectiveGeometryDesign(3, 1, GF(2), point_coordinates=False) - sage: [3,8,7] in IS + sage: IS = designs.ProjectiveGeometryDesign(3, 1, GF(2), # needs sage.rings.finite_rings + ....: point_coordinates=False) + sage: [3,8,7] in IS # needs sage.rings.finite_rings True sage: [3,8,9] in IS False @@ -393,6 +395,7 @@ def canonical_label(self): EXAMPLES:: + sage: # needs sage.schemes sage: fano1 = designs.balanced_incomplete_block_design(7,3) sage: fano2 = designs.projective_plane(2) sage: fano1 == fano2 @@ -427,6 +430,7 @@ def is_isomorphic(self, other, certificate=False): EXAMPLES:: + sage: # needs sage.schemes sage: fano1 = designs.balanced_incomplete_block_design(7,3) sage: fano2 = designs.projective_plane(2) sage: fano1.is_isomorphic(fano2) @@ -436,29 +440,32 @@ def is_isomorphic(self, other, certificate=False): TESTS:: - sage: IS = IncidenceStructure([["A",5,pi],["A",5,"Wouhou"], # optional - sage.symbolic + sage: # needs sage.symbolic + sage: IS = IncidenceStructure([["A",5,pi],["A",5,"Wouhou"], ....: ["A","Wouhou",(9,9)],[pi,12]]) - sage: IS2 = IS.copy() # optional - sage.symbolic - sage: IS2.relabel(IS2.canonical_label()) # optional - sage.symbolic - sage: IS.is_isomorphic(IS2) # optional - sage.symbolic + sage: IS2 = IS.copy() + sage: IS2.relabel(IS2.canonical_label()) + sage: IS.is_isomorphic(IS2) True - sage: canon = IS.is_isomorphic(IS2, certificate=True) # optional - sage.symbolic - sage: IS.relabel(canon) # optional - sage.symbolic - sage: IS==IS2 # optional - sage.symbolic + sage: canon = IS.is_isomorphic(IS2, certificate=True) + sage: IS.relabel(canon) + sage: IS==IS2 True sage: IS2 = IncidenceStructure([[1,2]]) - sage: IS2.is_isomorphic(IS) # optional - sage.symbolic + sage: IS2.is_isomorphic(IS) # needs sage.symbolic False - sage: IS2.is_isomorphic(IS, certificate=True) # optional - sage.symbolic + sage: IS2.is_isomorphic(IS, certificate=True) # needs sage.symbolic {} Checking whether two :class:`IncidenceStructure` are isomorphic incidentally computes their canonical label (if necessary). Thus, subsequent calls to :meth:`is_isomorphic` will be faster:: + sage: # needs sage.schemes sage: IS1 = designs.projective_plane(3) - sage: IS2 = IS1.relabel(Permutations(IS1.ground_set()).random_element(), inplace=False) + sage: IS2 = IS1.relabel(Permutations(IS1.ground_set()).random_element(), + ....: inplace=False) sage: IS2 = IncidenceStructure(IS2.blocks()) sage: IS1._canonical_label is None and IS2._canonical_label is None True @@ -548,10 +555,10 @@ def isomorphic_substructures_iterator(self, H2,induced=False): The number of copies of `H` in itself is the size of its automorphism group:: - sage: H = designs.projective_plane(3) - sage: sum(1 for _ in H.isomorphic_substructures_iterator(H)) + sage: H = designs.projective_plane(3) # needs sage.schemes + sage: sum(1 for _ in H.isomorphic_substructures_iterator(H)) # needs sage.schemes 5616 - sage: H.automorphism_group().cardinality() + sage: H.automorphism_group().cardinality() # needs sage.groups sage.schemes 5616 """ from sage.combinat.designs.subhypergraph_search import SubHypergraphSearch @@ -673,6 +680,7 @@ def trace(self, points, min_size=1, multiset=True): A Baer subplane of order 2 (i.e. a Fano plane) in a projective plane of order 4:: + sage: # needs sage.schemes sage: P4 = designs.projective_plane(4) sage: F = designs.projective_plane(2) sage: for x in Subsets(P4.ground_set(),7): @@ -685,6 +693,7 @@ def trace(self, points, min_size=1, multiset=True): TESTS:: + sage: # needs sage.schemes sage: F.trace([0..50]) Traceback (most recent call last): ... @@ -929,11 +938,11 @@ def is_regular(self, r=None) -> bool | int: EXAMPLES:: - sage: designs.balanced_incomplete_block_design(7,3).is_regular() + sage: designs.balanced_incomplete_block_design(7,3).is_regular() # needs sage.schemes 3 - sage: designs.balanced_incomplete_block_design(7,3).is_regular(r=3) + sage: designs.balanced_incomplete_block_design(7,3).is_regular(r=3) # needs sage.schemes True - sage: designs.balanced_incomplete_block_design(7,3).is_regular(r=4) + sage: designs.balanced_incomplete_block_design(7,3).is_regular(r=4) # needs sage.schemes False TESTS:: @@ -980,11 +989,11 @@ def is_uniform(self, k=None) -> bool | int: EXAMPLES:: - sage: designs.balanced_incomplete_block_design(7,3).is_uniform() + sage: designs.balanced_incomplete_block_design(7,3).is_uniform() # needs sage.schemes 3 - sage: designs.balanced_incomplete_block_design(7,3).is_uniform(k=3) + sage: designs.balanced_incomplete_block_design(7,3).is_uniform(k=3) # needs sage.schemes True - sage: designs.balanced_incomplete_block_design(7,3).is_uniform(k=4) + sage: designs.balanced_incomplete_block_design(7,3).is_uniform(k=4) # needs sage.schemes False TESTS:: @@ -1119,10 +1128,11 @@ def incidence_matrix(self): EXAMPLES:: - sage: BD = IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) + sage: BD = IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5], + ....: [1,4,6],[2,3,6],[2,4,5]]) sage: BD.block_sizes() [3, 3, 3, 3, 3, 3, 3] - sage: BD.incidence_matrix() + sage: BD.incidence_matrix() # needs sage.modules [1 1 1 0 0 0 0] [1 0 0 1 1 0 0] [1 0 0 0 0 1 1] @@ -1132,7 +1142,7 @@ def incidence_matrix(self): [0 0 1 0 1 1 0] sage: I = IncidenceStructure('abc', ('ab','abc','ac','c')) - sage: I.incidence_matrix() + sage: I.incidence_matrix() # needs sage.modules [1 1 1 0] [1 1 0 0] [0 1 1 1] @@ -1157,26 +1167,28 @@ def incidence_graph(self,labels=False): - ``labels`` (boolean) -- whether to return a graph whose vertices are integers, or labelled elements. - - ``labels is False`` (default) -- in this case the first vertices - of the graphs are the elements of :meth:`ground_set`, and appear - in the same order. Similarly, the following vertices represent the - elements of :meth:`blocks`, and appear in the same order. + - ``labels is False`` (default) -- in this case the first vertices + of the graphs are the elements of :meth:`ground_set`, and appear + in the same order. Similarly, the following vertices represent the + elements of :meth:`blocks`, and appear in the same order. - - ``labels is True``, the points keep their original labels, and the - blocks are :func:`Set ` objects. + - ``labels is True``, the points keep their original labels, and the + blocks are :func:`Set ` objects. - Note that the labelled incidence graph can be incorrect when - blocks are repeated, and on some (rare) occasions when the - elements of :meth:`ground_set` mix :func:`Set` and non-:func:`Set - ` objects. + Note that the labelled incidence graph can be incorrect when + blocks are repeated, and on some (rare) occasions when the + elements of :meth:`ground_set` mix :func:`Set` and non-:func:`Set + ` objects. EXAMPLES:: - sage: BD = IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]]) - sage: BD.incidence_graph() + sage: BD = IncidenceStructure(7, [[0,1,2],[0,3,4],[0,5,6],[1,3,5], + ....: [1,4,6],[2,3,6],[2,4,5]]) + sage: BD.incidence_graph() # needs sage.modules Bipartite graph on 14 vertices - sage: A = BD.incidence_matrix() - sage: Graph(block_matrix([[A*0,A],[A.transpose(),A*0]])) == BD.incidence_graph() + sage: A = BD.incidence_matrix() # needs sage.modules + sage: Graph(block_matrix([[A*0, A], # needs sage.modules + ....: [A.transpose(),A*0]])) == BD.incidence_graph() True TESTS: @@ -1219,9 +1231,9 @@ def is_berge_cyclic(self): EXAMPLES:: - sage: Hypergraph(5, [[1, 2, 3], [2, 3 ,4]]).is_berge_cyclic() + sage: Hypergraph(5, [[1, 2, 3], [2, 3, 4]]).is_berge_cyclic() # needs sage.modules True - sage: Hypergraph(6, [[1, 2, 3], [3 ,4, 5]]).is_berge_cyclic() + sage: Hypergraph(6, [[1, 2, 3], [3, 4, 5]]).is_berge_cyclic() # needs sage.modules False TESTS:: @@ -1260,10 +1272,10 @@ def complement(self,uniform=False): :class:`~sage.combinat.designs.bibd.BalancedIncompleteBlockDesign` is also a `2`-design:: - sage: bibd = designs.balanced_incomplete_block_design(13,4) - sage: bibd.is_t_design(return_parameters=True) + sage: bibd = designs.balanced_incomplete_block_design(13,4) # needs sage.schemes + sage: bibd.is_t_design(return_parameters=True) # needs sage.schemes (True, (2, 13, 4, 1)) - sage: bibd.complement().is_t_design(return_parameters=True) + sage: bibd.complement().is_t_design(return_parameters=True) # needs sage.schemes (True, (2, 13, 9, 6)) The "uniform" complement of a graph is a graph:: @@ -1279,8 +1291,8 @@ def complement(self,uniform=False): TESTS:: - sage: bibd.relabel({i:str(i) for i in bibd.ground_set()}) - sage: bibd.complement().ground_set() + sage: bibd.relabel({i:str(i) for i in bibd.ground_set()}) # needs sage.schemes + sage: bibd.complement().ground_set() # needs sage.schemes ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'] sage: I = IncidenceStructure('abc', ['ab','ac','bc']) @@ -1336,17 +1348,19 @@ def relabel(self, perm=None, inplace=True): EXAMPLES:: - sage: TD=designs.transversal_design(5,5) - sage: TD.relabel({i:chr(97+i) for i in range(25)}) + sage: # needs sage.schemes + sage: TD = designs.transversal_design(5,5) + sage: TD.relabel({i: chr(97+i) for i in range(25)}) sage: TD.ground_set() - ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y'] + ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y'] sage: TD.blocks()[:3] [['a', 'f', 'k', 'p', 'u'], ['a', 'g', 'm', 's', 'y'], ['a', 'h', 'o', 'q', 'x']] Relabel to integer points:: - sage: TD.relabel() - sage: TD.blocks()[:3] + sage: TD.relabel() # needs sage.schemes + sage: TD.blocks()[:3] # needs sage.schemes [[0, 5, 10, 15, 20], [0, 6, 12, 18, 24], [0, 7, 14, 16, 23]] TESTS: @@ -1365,7 +1379,7 @@ def relabel(self, perm=None, inplace=True): And one can also verify that we have exactly two automorphisms:: - sage: I.automorphism_group() + sage: I.automorphism_group() # needs sage.groups Permutation Group with generators [(2,4)] """ if not inplace: @@ -1431,10 +1445,10 @@ def packing(self, solver=None, verbose=0, *, integrality_tolerance=1e-3): EXAMPLES:: - sage: P = IncidenceStructure([[1,2],[3,4],[2,3]]).packing() - sage: sorted(sorted(b) for b in P) + sage: P = IncidenceStructure([[1,2],[3,4],[2,3]]).packing() # needs sage.numerical.mip + sage: sorted(sorted(b) for b in P) # needs sage.numerical.mip [[1, 2], [3, 4]] - sage: len(designs.steiner_triple_system(9).packing()) + sage: len(designs.steiner_triple_system(9).packing()) # needs sage.numerical.mip 3 """ from sage.numerical.mip import MixedIntegerLinearProgram @@ -1499,7 +1513,7 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): sage: BD.is_t_design(0,6,3,7) or BD.is_t_design(0,7,4,7) or BD.is_t_design(0,7,3,8) False - sage: BD = designs.AffineGeometryDesign(3, 1, GF(2)) + sage: BD = designs.AffineGeometryDesign(3, 1, GF(2)) # needs sage.rings.finite_rings sage: BD.is_t_design(1) True sage: BD.is_t_design(2) @@ -1568,12 +1582,15 @@ def is_t_design(self, t=None, v=None, k=None, l=None, return_parameters=False): [(8, 4, 7)] sage: [(v,k,l) for v in R for k in R for l in R if S4_8.is_t_design(0,v,k,l)] [(8, 4, 14)] + + sage: # needs sage.rings.finite_rings sage: A = designs.AffineGeometryDesign(3, 1, GF(2)) sage: A.is_t_design(return_parameters=True) (True, (2, 8, 2, 1)) sage: A = designs.AffineGeometryDesign(4, 2, GF(2)) sage: A.is_t_design(return_parameters=True) (True, (3, 16, 4, 1)) + sage: I = IncidenceStructure(2, []) sage: I.is_t_design(return_parameters=True) (True, (0, 2, 0, 0)) @@ -1695,30 +1712,31 @@ def is_generalized_quadrangle(self, verbose=False, parameters=False): EXAMPLES:: - sage: h = designs.CremonaRichmondConfiguration() - sage: h.is_generalized_quadrangle() + sage: h = designs.CremonaRichmondConfiguration() # needs networkx + sage: h.is_generalized_quadrangle() # needs networkx True This is actually a *regular* generalized quadrangle:: - sage: h.is_generalized_quadrangle(parameters=True) + sage: h.is_generalized_quadrangle(parameters=True) # needs networkx (2, 2) TESTS:: sage: H = IncidenceStructure((2*graphs.CompleteGraph(3)).edges(sort=True, labels=False)) - sage: H.is_generalized_quadrangle(verbose=True) + sage: H.is_generalized_quadrangle(verbose=True) # needs sage.modules Some point is at distance >3 from some block. False sage: G = graphs.CycleGraph(5) - sage: B = list(G.subgraph_search_iterator(graphs.PathGraph(3), return_graphs=False)) - sage: H = IncidenceStructure(B) - sage: H.is_generalized_quadrangle(verbose=True) + sage: B = list(G.subgraph_search_iterator(graphs.PathGraph(3), # needs sage.modules + ....: return_graphs=False)) + sage: H = IncidenceStructure(B) # needs sage.modules + sage: H.is_generalized_quadrangle(verbose=True) # needs sage.modules Two blocks intersect on >1 points. False - sage: hypergraphs.CompleteUniform(4,2).is_generalized_quadrangle(verbose=1) + sage: hypergraphs.CompleteUniform(4,2).is_generalized_quadrangle(verbose=1) # needs sage.modules Some point has two projections on some line. False """ @@ -1769,26 +1787,24 @@ def dual(self, algorithm=None): The dual of a projective plane is a projective plane:: - sage: PP = designs.DesarguesianProjectivePlaneDesign(4) - sage: PP.dual().is_t_design(return_parameters=True) + sage: PP = designs.DesarguesianProjectivePlaneDesign(4) # needs sage.rings.finite_rings + sage: PP.dual().is_t_design(return_parameters=True) # needs sage.modules sage.rings.finite_rings (True, (2, 21, 5, 1)) TESTS:: - sage: D = IncidenceStructure(4, [[0,2],[1,2,3],[2,3]]) - sage: D + sage: D = IncidenceStructure(4, [[0,2],[1,2,3],[2,3]]); D Incidence structure with 4 points and 3 blocks - sage: D.dual() + sage: D.dual() # needs sage.modules Incidence structure with 3 points and 4 blocks - sage: print(D.dual(algorithm="gap")) # optional - gap_packages + sage: print(D.dual(algorithm="gap")) # optional - gap_packages Incidence structure with 3 points and 4 blocks sage: blocks = [[0,1,2],[0,3,4],[0,5,6],[1,3,5],[1,4,6],[2,3,6],[2,4,5]] - sage: BD = IncidenceStructure(7, blocks, name="FanoPlane") - sage: BD + sage: BD = IncidenceStructure(7, blocks, name="FanoPlane"); BD Incidence structure with 7 points and 7 blocks - sage: print(BD.dual(algorithm="gap")) # optional - gap_packages + sage: print(BD.dual(algorithm="gap")) # optional - gap_packages Incidence structure with 7 points and 7 blocks - sage: BD.dual() + sage: BD.dual() # needs sage.modules Incidence structure with 7 points and 7 blocks REFERENCE: @@ -1815,6 +1831,7 @@ def automorphism_group(self): EXAMPLES:: + sage: # needs sage.groups sage.rings.finite_rings sage: P = designs.DesarguesianProjectivePlaneDesign(2); P (7,3,1)-Balanced Incomplete Block Design sage: G = P.automorphism_group() @@ -1828,17 +1845,17 @@ def automorphism_group(self): A non self-dual example:: sage: IS = IncidenceStructure(list(range(4)), [[0,1,2,3],[1,2,3]]) - sage: IS.automorphism_group().cardinality() + sage: IS.automorphism_group().cardinality() # needs sage.groups 6 - sage: IS.dual().automorphism_group().cardinality() + sage: IS.dual().automorphism_group().cardinality() # needs sage.groups sage.modules 1 Examples with non-integer points:: sage: I = IncidenceStructure('abc', ('ab','ac','bc')) - sage: I.automorphism_group() + sage: I.automorphism_group() # needs sage.groups Permutation Group with generators [('b','c'), ('a','b')] - sage: IncidenceStructure([[(1,2),(3,4)]]).automorphism_group() + sage: IncidenceStructure([[(1,2),(3,4)]]).automorphism_group() # needs sage.groups Permutation Group with generators [((1,2),(3,4))] """ from sage.graphs.graph import Graph @@ -1907,19 +1924,19 @@ def is_resolvable(self, certificate=False, solver=None, verbose=0, check=True, sage: TD.is_resolvable() True - sage: AG = designs.AffineGeometryDesign(3,1,GF(2)) - sage: AG.is_resolvable() + sage: AG = designs.AffineGeometryDesign(3,1,GF(2)) # needs sage.rings.finite_rings + sage: AG.is_resolvable() # needs sage.rings.finite_rings True Their classes:: - sage: b,cls = TD.is_resolvable(True) + sage: b, cls = TD.is_resolvable(True) sage: b True sage: cls # random [[[0, 3], [1, 2]], [[1, 3], [0, 2]]] - sage: b,cls = AG.is_resolvable(True) + sage: b, cls = AG.is_resolvable(True) # needs sage.rings.finite_rings sage: b True sage: cls # random @@ -1933,17 +1950,17 @@ def is_resolvable(self, certificate=False, solver=None, verbose=0, check=True, A non-resolvable design:: - sage: Fano = designs.balanced_incomplete_block_design(7,3) - sage: Fano.is_resolvable() + sage: Fano = designs.balanced_incomplete_block_design(7,3) # needs sage.schemes + sage: Fano.is_resolvable() # needs sage.schemes False - sage: Fano.is_resolvable(True) + sage: Fano.is_resolvable(True) # needs sage.schemes (False, []) TESTS:: - sage: _,cls1 = AG.is_resolvable(certificate=True) - sage: _,cls2 = AG.is_resolvable(certificate=True) - sage: cls1 is cls2 + sage: _, cls1 = AG.is_resolvable(certificate=True) # needs sage.rings.finite_rings + sage: _, cls2 = AG.is_resolvable(certificate=True) # needs sage.rings.finite_rings + sage: cls1 is cls2 # needs sage.rings.finite_rings False """ if self._classes is None: @@ -2040,12 +2057,12 @@ def coloring(self, k=None, solver=None, verbose=0, The Fano plane has chromatic number 3:: - sage: len(designs.steiner_triple_system(7).coloring()) + sage: len(designs.steiner_triple_system(7).coloring()) # needs sage.numerical.mip 3 One admissible 3-coloring:: - sage: designs.steiner_triple_system(7).coloring() # not tested - architecture-dependent + sage: designs.steiner_triple_system(7).coloring() # not tested # needs sage.numerical.mip [[0, 2, 5, 1], [4, 3], [6]] The chromatic number of a graph is equal to the chromatic number of its @@ -2055,7 +2072,7 @@ def coloring(self, k=None, solver=None, verbose=0, sage: H = IncidenceStructure(g.edges(sort=True, labels=False)) sage: len(g.coloring()) 3 - sage: len(H.coloring()) + sage: len(H.coloring()) # needs sage.numerical.mip 3 """ if k is None: @@ -2150,10 +2167,11 @@ def _spring_layout(self): EXAMPLES:: + sage: # needs sage.plot sage: H = Hypergraph([{1,2,3},{2,3,4},{3,4,5},{4,5,6}]); H Incidence structure with 6 points and 4 blocks sage: L = H._spring_layout() - sage: L # random + sage: L # random {1: (0.238, -0.926), 2: (0.672, -0.518), 3: (0.449, -0.225), @@ -2166,7 +2184,7 @@ def _spring_layout(self): {1, 2, 3}: (0.393, -0.617)} sage: all(v in L for v in H.ground_set()) True - sage: all(v in L for v in map(Set,H.blocks())) + sage: all(v in L for v in map(Set, H.blocks())) True """ from sage.graphs.graph import Graph @@ -2196,9 +2214,10 @@ def _latex_(self): sage: g = graphs.Grid2dGraph(5,5) sage: C4 = graphs.CycleGraph(4) - sage: sets = Set(map(Set,list(g.subgraph_search_iterator(C4, return_graphs=False)))) - sage: H = Hypergraph(sets) - sage: view(H) # not tested + sage: sets = Set(map(Set, g.subgraph_search_iterator(C4, # needs sage.modules + ....: return_graphs=False))) + sage: H = Hypergraph(sets) # needs sage.modules + sage: view(H) # not tested # needs sage.modules sage.plot TESTS:: diff --git a/src/sage/combinat/designs/latin_squares.py b/src/sage/combinat/designs/latin_squares.py index 4dea7cd63d7..23e2f09bfac 100644 --- a/src/sage/combinat/designs/latin_squares.py +++ b/src/sage/combinat/designs/latin_squares.py @@ -1,11 +1,11 @@ -# -*- coding: utf-8 -*- +# sage.doctest: needs sage.modules r""" Mutually Orthogonal Latin Squares (MOLS) The main function of this module is :func:`mutually_orthogonal_latin_squares` and can be can be used to generate MOLS (or check that they exist):: - sage: MOLS = designs.mutually_orthogonal_latin_squares(4,8) + sage: MOLS = designs.mutually_orthogonal_latin_squares(4,8) # needs sage.schemes For more information on MOLS, see the :wikipedia:`Wikipedia entry on MOLS `. If you are only @@ -155,8 +155,8 @@ def are_mutually_orthogonal_latin_squares(l, verbose=False): Squares 0 and 2 are not orthogonal False - sage: m = designs.mutually_orthogonal_latin_squares(7,8) - sage: are_mutually_orthogonal_latin_squares(m) + sage: m = designs.mutually_orthogonal_latin_squares(7,8) # needs sage.schemes + sage: are_mutually_orthogonal_latin_squares(m) # needs sage.schemes True TESTS: @@ -239,7 +239,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): EXAMPLES:: - sage: designs.mutually_orthogonal_latin_squares(4,5) + sage: designs.mutually_orthogonal_latin_squares(4,5) # needs sage.schemes [ [0 2 4 1 3] [0 3 1 4 2] [0 4 3 2 1] [0 1 2 3 4] [4 1 3 0 2] [3 1 4 2 0] [2 1 0 4 3] [4 0 1 2 3] @@ -248,7 +248,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): [1 3 0 2 4], [2 0 3 1 4], [3 2 1 0 4], [1 2 3 4 0] ] - sage: designs.mutually_orthogonal_latin_squares(3,7) + sage: designs.mutually_orthogonal_latin_squares(3,7) # needs sage.schemes [ [0 2 4 6 1 3 5] [0 3 6 2 5 1 4] [0 4 1 5 2 6 3] [6 1 3 5 0 2 4] [5 1 4 0 3 6 2] [4 1 5 2 6 3 0] @@ -259,7 +259,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): [1 3 5 0 2 4 6], [2 5 1 4 0 3 6], [3 0 4 1 5 2 6] ] - sage: designs.mutually_orthogonal_latin_squares(2,5,partitions=True) + sage: designs.mutually_orthogonal_latin_squares(2,5,partitions=True) # needs sage.schemes [[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], @@ -283,7 +283,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): What is the maximum number of MOLS of size 8 that Sage knows how to build?:: - sage: designs.orthogonal_arrays.largest_available_k(8)-2 + sage: designs.orthogonal_arrays.largest_available_k(8)-2 # needs sage.schemes 7 If you only want to know if Sage is able to build a given set of @@ -291,12 +291,12 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): sage: designs.orthogonal_arrays.is_available(5+2, 5) # 5 MOLS of order 5 False - sage: designs.orthogonal_arrays.is_available(4+2,6) # 4 MOLS of order 6 + sage: designs.orthogonal_arrays.is_available(4+2,6) # 4 MOLS of order 6 # needs sage.schemes False Sage, however, is not able to prove that the second MOLS do not exist:: - sage: designs.orthogonal_arrays.exists(4+2,6) # 4 MOLS of order 6 + sage: designs.orthogonal_arrays.exists(4+2,6) # 4 MOLS of order 6 # needs sage.schemes Unknown If you ask for such a MOLS then you will respectively get an informative @@ -306,7 +306,7 @@ def mutually_orthogonal_latin_squares(k, n, partitions=False, check=True): Traceback (most recent call last): ... EmptySetError: there exist at most n-1 MOLS of size n if n>=2 - sage: designs.mutually_orthogonal_latin_squares(4,6) + sage: designs.mutually_orthogonal_latin_squares(4,6) # needs sage.schemes Traceback (most recent call last): ... NotImplementedError: I don't know how to build 4 MOLS of order 6 @@ -431,8 +431,8 @@ def latin_square_product(M, N, *others): EXAMPLES:: sage: from sage.combinat.designs.latin_squares import latin_square_product - sage: m=designs.mutually_orthogonal_latin_squares(3,4)[0] - sage: latin_square_product(m,m,m) + sage: m=designs.mutually_orthogonal_latin_squares(3,4)[0] # needs sage.schemes + sage: latin_square_product(m,m,m) # needs sage.schemes 64 x 64 sparse matrix over Integer Ring (use the '.str()' method to see the entries) """ from sage.matrix.constructor import Matrix @@ -476,6 +476,7 @@ def MOLS_table(start,stop=None,compare=False,width=None): EXAMPLES:: + sage: # needs sage.schemes sage: from sage.combinat.designs.latin_squares import MOLS_table sage: MOLS_table(100) 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 diff --git a/src/sage/combinat/designs/orthogonal_arrays.py b/src/sage/combinat/designs/orthogonal_arrays.py index 094a8118245..b37cf2be2b3 100644 --- a/src/sage/combinat/designs/orthogonal_arrays.py +++ b/src/sage/combinat/designs/orthogonal_arrays.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.finite_rings r""" Orthogonal arrays (OA) @@ -1180,11 +1181,12 @@ def incomplete_orthogonal_array(k,n,holes,resolvable=False, existence=False): 10 holes of size 9 through the product construction:: - sage: iOA = designs.incomplete_orthogonal_array(10,153,[9]*10) # long time - sage: OA9 = designs.orthogonal_arrays.build(10,9) # long time - sage: for i in range(10): # long time + sage: # long time + sage: iOA = designs.incomplete_orthogonal_array(10,153,[9]*10) + sage: OA9 = designs.orthogonal_arrays.build(10,9) + sage: for i in range(10): ....: iOA.extend([[153-9*(i+1)+x for x in B] for B in OA9]) - sage: is_orthogonal_array(iOA,10,153) # long time + sage: is_orthogonal_array(iOA,10,153) True An `OA(9,82)-OA(9,9)-OA(9,1)`:: @@ -1954,7 +1956,8 @@ def OA_from_PBD(k,n,PBD, check=True): sage: OA_from_PBD(4,10,pbd) Traceback (most recent call last): ... - EmptySetError: There is no OA(n+1,n) - 3.OA(n+1,1) as all blocks intersect in a projective plane. + EmptySetError: There is no OA(n+1,n) - 3.OA(n+1,1) + as all blocks intersect in a projective plane. Or an `OA(3,6)` (as the PBD has 10 points):: diff --git a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py index ff62bae1b8e..c30041a9c9b 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py +++ b/src/sage/combinat/designs/orthogonal_arrays_build_recursive.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.finite_rings r""" Orthogonal arrays (build recursive constructions) diff --git a/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx b/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx index 8499f338ade..1843cbe00e8 100644 --- a/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx +++ b/src/sage/combinat/designs/orthogonal_arrays_find_recursive.pyx @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.finite_rings # cython: cdivision=True r""" Orthogonal arrays (find recursive constructions) diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 623b10be3d2..ed098ce746c 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.finite_rings r""" Resolvable Balanced Incomplete Block Design (RBIBD) diff --git a/src/sage/combinat/designs/subhypergraph_search.pyx b/src/sage/combinat/designs/subhypergraph_search.pyx index 39fb62300ad..ace387a4bf2 100644 --- a/src/sage/combinat/designs/subhypergraph_search.pyx +++ b/src/sage/combinat/designs/subhypergraph_search.pyx @@ -444,8 +444,8 @@ cdef class SubHypergraphSearch: EXAMPLES:: - sage: d = designs.projective_plane(3) - sage: d.isomorphic_substructures_iterator(d).relabel_heuristic() + sage: d = designs.projective_plane(3) # needs sage.schemes + sage: d.isomorphic_substructures_iterator(d).relabel_heuristic() # needs sage.schemes """ cdef hypergraph h2 = self.h2 cdef int x,y,i diff --git a/src/sage/combinat/designs/twographs.py b/src/sage/combinat/designs/twographs.py index 32a844083b9..b36d23f0162 100644 --- a/src/sage/combinat/designs/twographs.py +++ b/src/sage/combinat/designs/twographs.py @@ -84,8 +84,8 @@ def __init__(self, points=None, blocks=None, incidence_matrix=None, Traceback (most recent call last): ... AssertionError: the structure is not a 2-graph! - sage: p = graphs.PetersenGraph().twograph() # optional - sage.modules - sage: TwoGraph(p, check=True) # optional - sage.modules + sage: p = graphs.PetersenGraph().twograph() # needs sage.modules + sage: TwoGraph(p, check=True) # needs sage.modules Incidence structure with 10 points and 60 blocks """ IncidenceStructure.__init__(self, points=points, blocks=blocks, @@ -108,15 +108,16 @@ def is_regular_twograph(self, alpha=False): EXAMPLES:: - sage: p = graphs.PetersenGraph().twograph() # optional - sage.modules - sage: p.is_regular_twograph(alpha=True) # optional - sage.modules + sage: # needs sage.modules + sage: p = graphs.PetersenGraph().twograph() + sage: p.is_regular_twograph(alpha=True) 4 - sage: p.is_regular_twograph() # optional - sage.modules + sage: p.is_regular_twograph() True - sage: p = graphs.PathGraph(5).twograph() # optional - sage.modules - sage: p.is_regular_twograph(alpha=True) # optional - sage.modules + sage: p = graphs.PathGraph(5).twograph() + sage: p.is_regular_twograph(alpha=True) False - sage: p.is_regular_twograph() # optional - sage.modules + sage: p.is_regular_twograph() False """ r, (_, _, _, a) = self.is_t_design(t=2, k=3, return_parameters=True) @@ -139,8 +140,8 @@ def descendant(self, v): EXAMPLES:: - sage: p = graphs.PetersenGraph().twograph().descendant(0) # optional - sage.modules - sage: p.is_strongly_regular(parameters=True) # optional - sage.modules + sage: p = graphs.PetersenGraph().twograph().descendant(0) # needs sage.modules + sage: p.is_strongly_regular(parameters=True) # needs sage.modules (9, 4, 1, 2) """ from sage.graphs.graph import Graph @@ -159,14 +160,14 @@ def complement(self): EXAMPLES:: - sage: p = graphs.CompleteGraph(8).line_graph().twograph() # optional - sage.modules - sage: pc = p.complement(); pc # optional - sage.modules + sage: p = graphs.CompleteGraph(8).line_graph().twograph() # needs sage.modules + sage: pc = p.complement(); pc # needs sage.modules Incidence structure with 28 points and 1260 blocks TESTS:: sage: from sage.combinat.designs.twographs import is_twograph - sage: is_twograph(pc) # optional - sage.modules + sage: is_twograph(pc) # needs sage.modules True """ return super().complement(uniform=True) @@ -192,7 +193,7 @@ def taylor_twograph(q): EXAMPLES:: sage: from sage.combinat.designs.twographs import taylor_twograph - sage: T = taylor_twograph(3); T # optional - sage.rings.finite_rings + sage: T = taylor_twograph(3); T # needs sage.rings.finite_rings Incidence structure with 28 points and 1260 blocks """ from sage.graphs.generators.classical_geometries import TaylorTwographSRG @@ -211,25 +212,25 @@ def is_twograph(T): a two-graph from a graph:: sage: from sage.combinat.designs.twographs import (is_twograph, TwoGraph) - sage: p = graphs.PetersenGraph().twograph() # optional - sage.modules - sage: is_twograph(p) # optional - sage.modules + sage: p = graphs.PetersenGraph().twograph() # needs sage.modules + sage: is_twograph(p) # needs sage.modules True a non-regular 2-uniform hypergraph which is a two-graph:: - sage: is_twograph(TwoGraph([[1,2,3],[1,2,4]])) # optional - sage.modules + sage: is_twograph(TwoGraph([[1,2,3],[1,2,4]])) True TESTS: wrong size of blocks:: - sage: is_twograph(designs.projective_plane(3)) + sage: is_twograph(designs.projective_plane(3)) # needs sage.schemes False a triple system which is not a two-graph:: - sage: is_twograph(designs.projective_plane(2)) + sage: is_twograph(designs.projective_plane(2)) # needs sage.schemes False """ if not T.is_uniform(3): @@ -285,14 +286,14 @@ def twograph_descendant(G, v, name=None): sage: T8 = graphs.CompleteGraph(8).line_graph() sage: v = T8.vertices(sort=True)[0] - sage: twograph_descendant(T8, v) == T8.twograph().descendant(v) # optional - sage.modules + sage: twograph_descendant(T8, v) == T8.twograph().descendant(v) # needs sage.modules True sage: twograph_descendant(T8, v).is_strongly_regular(parameters=True) (27, 16, 10, 8) sage: p = graphs.PetersenGraph() - sage: twograph_descendant(p,5) + sage: twograph_descendant(p, 5) Graph on 9 vertices - sage: twograph_descendant(p,5,name=True) + sage: twograph_descendant(p, 5, name=True) descendant of Petersen graph at 5: Graph on 9 vertices """ G = G.seidel_switching(G.neighbors(v),inplace=False) diff --git a/src/sage/combinat/diagram.py b/src/sage/combinat/diagram.py index 54f56904719..fdbb6912ee1 100644 --- a/src/sage/combinat/diagram.py +++ b/src/sage/combinat/diagram.py @@ -502,9 +502,9 @@ def specht_module(self, base_ring=None): sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,0), (1,1), (2,2), (2,3)]) - sage: SM = D.specht_module(QQ) - sage: s = SymmetricFunctions(QQ).s() - sage: s(SM.frobenius_image()) + sage: SM = D.specht_module(QQ) # needs sage.modules + sage: s = SymmetricFunctions(QQ).s() # needs sage.modules + sage: s(SM.frobenius_image()) # needs sage.modules s[2, 1, 1] + s[2, 2] + 2*s[3, 1] + s[4] """ from sage.combinat.specht_module import SpechtModule @@ -527,9 +527,9 @@ def specht_module_dimension(self, base_ring=None): sage: from sage.combinat.diagram import Diagram sage: D = Diagram([(0,0), (1,1), (2,2), (2,3)]) - sage: D.specht_module_dimension() + sage: D.specht_module_dimension() # needs sage.modules 12 - sage: D.specht_module(QQ).dimension() + sage: D.specht_module(QQ).dimension() # needs sage.modules 12 """ from sage.combinat.specht_module import specht_module_rank @@ -661,9 +661,9 @@ def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): . . O - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) - sage: Dgms(p).pp() + sage: from sage.combinat.tiling import Polyomino # needs sage.modules + sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) # needs sage.modules + sage: Dgms(p).pp() # needs sage.modules O . . O O O @@ -676,8 +676,8 @@ def _element_constructor_(self, cells, n_rows=None, n_cols=None, check=True): O O . . O O O O - sage: M = Matrix([[1,1,1,1],[1,1,0,0],[0,0,0,0],[1,1,0,0],[1,1,1,1]]) - sage: Dgms(M).pp() + sage: M = Matrix([[1,1,1,1],[1,1,0,0],[0,0,0,0],[1,1,0,0],[1,1,1,1]]) # needs sage.modules + sage: Dgms(M).pp() # needs sage.modules O O O O O O . . . . . . @@ -721,23 +721,23 @@ def from_polyomino(self, p): EXAMPLES:: - sage: from sage.combinat.tiling import Polyomino - sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) + sage: from sage.combinat.tiling import Polyomino # needs sage.modules + sage: p = Polyomino([(0,0),(1,0),(1,1),(1,2)]) # needs sage.modules sage: from sage.combinat.diagram import Diagrams - sage: Diagrams()(p).pp() + sage: Diagrams()(p).pp() # needs sage.modules O . . O O O We can also call this method directly:: - sage: Diagrams().from_polyomino(p).pp() + sage: Diagrams().from_polyomino(p).pp() # needs sage.modules O . . O O O This only works for a 2d :class:`~sage.combinat.tiling.Polyomino`:: - sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') - sage: Diagrams().from_polyomino(p) + sage: p = Polyomino([(0,0,0), (0,1,0), (1,1,0), (1,1,1)], color='blue') # needs sage.modules + sage: Diagrams().from_polyomino(p) # needs sage.modules Traceback (most recent call last): ... ValueError: the polyomino must be 2 dimensional @@ -782,17 +782,17 @@ def from_zero_one_matrix(self, M, check=True): EXAMPLES:: - sage: M = matrix([[1,0,1,1],[0,1,1,0]]) # optional - sage.modules + sage: M = matrix([[1,0,1,1],[0,1,1,0]]) # needs sage.modules sage: from sage.combinat.diagram import Diagrams - sage: Diagrams()(M).pp() # optional - sage.modules + sage: Diagrams()(M).pp() # needs sage.modules O . O O . O O . - sage: Diagrams().from_zero_one_matrix(M).pp() # optional - sage.modules + sage: Diagrams().from_zero_one_matrix(M).pp() # needs sage.modules O . O O . O O . - sage: M = matrix([[1, 0, 0], [1, 0, 0], [0, 0, 0]]) # optional - sage.modules - sage: Diagrams()(M).pp() # optional - sage.modules + sage: M = matrix([[1, 0, 0], [1, 0, 0], [0, 0, 0]]) # needs sage.modules + sage: Diagrams()(M).pp() # needs sage.modules O . . O . . . . . @@ -1440,10 +1440,10 @@ def from_parallelogram_polyomino(self, p): EXAMPLES:: - sage: p = ParallelogramPolyomino([[0, 0, 1, 0, 0, 0, 1, 1], # optional - sage.modules + sage: p = ParallelogramPolyomino([[0, 0, 1, 0, 0, 0, 1, 1], # needs sage.modules ....: [1, 1, 0, 1, 0, 0, 0, 0]]) sage: from sage.combinat.diagram import NorthwestDiagrams - sage: NorthwestDiagrams().from_parallelogram_polyomino(p).pp() + sage: NorthwestDiagrams().from_parallelogram_polyomino(p).pp() # needs sage.modules O O . O O O . O O @@ -1499,8 +1499,8 @@ def RotheDiagram(w): :class:`sage.combinat.permutations.Permutations` are supported. In particular, elements of permutation groups are not supported:: - sage: w = SymmetricGroup(9).an_element() - sage: RotheDiagram(w) + sage: w = SymmetricGroup(9).an_element() # needs sage.groups + sage: RotheDiagram(w) # needs sage.groups Traceback (most recent call last): ... ValueError: w must be a permutation diff --git a/src/sage/combinat/finite_state_machine.py b/src/sage/combinat/finite_state_machine.py index decef1d4479..35de4581bd0 100644 --- a/src/sage/combinat/finite_state_machine.py +++ b/src/sage/combinat/finite_state_machine.py @@ -944,7 +944,6 @@ from sage.misc.latex import latex from sage.misc.verbose import verbose from sage.misc.sageinspect import sage_getargspec -from sage.rings.qqbar import QQbar from sage.rings.integer_ring import ZZ from sage.rings.real_mpfr import RR from sage.structure.sage_object import SageObject @@ -9885,7 +9884,7 @@ def number_of_words(self, variable=None, from sage.arith.misc import binomial from sage.symbolic.ring import SR if base_ring is None: - base_ring = QQbar + from sage.rings.qqbar import QQbar as base_ring if variable is None: variable = SR.symbol('n') diff --git a/src/sage/combinat/k_regular_sequence.py b/src/sage/combinat/k_regular_sequence.py index 707f03879ef..6a1a2dabf71 100644 --- a/src/sage/combinat/k_regular_sequence.py +++ b/src/sage/combinat/k_regular_sequence.py @@ -21,6 +21,10 @@ might change without a formal deprecation. See https://github.com/sagemath/sage/issues/21202 for details. +:: + + sage: import logging + sage: logging.basicConfig() Examples ======== @@ -53,10 +57,10 @@ sage: tuple(u(n) for n in srange(10)) (0, 1, 3, 5, 9, 11, 15, 19, 27, 29) -There is a `2`-recursive sequence describing the numbers above as well:: +There is a `2`-regular sequence describing the numbers above as well:: - sage: U = Seq2((Matrix([[3, 2], [0, 1]]), Matrix([[2, 0], [1, 3]])), - ....: left=vector([0, 1]), right=vector([1, 0])).transposed() + sage: U = Seq2((Matrix([[3, 0], [2, 1]]), Matrix([[2, 1], [0, 3]])), + ....: left=vector([1, 0]), right=vector([0, 1])) sage: all(U[n] == u(n) for n in srange(30)) True @@ -165,6 +169,25 @@ def value(D, k): return sum(d * k**j for j, d in enumerate(D)) +class DegeneratedSequenceError(RuntimeError): + r""" + Exception raised if a degenerated sequence + (see :meth:`~kRegularSequence.is_degenerated`) is detected. + + EXAMPLES:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1])) + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + """ + pass + + class kRegularSequence(RecognizableSeries): def __init__(self, parent, mu, left=None, right=None): r""" @@ -190,11 +213,20 @@ def __init__(self, parent, mu, left=None, right=None): from the right to the matrix product. If ``None``, then this multiplication is skipped. + When created via the parent :class:`kRegularSequenceSpace`, then + the following option is available. + + - ``allow_degenerated_sequence`` -- (default: ``False``) a boolean. If set, then + there will be no check if the input is a degenerated sequence + (see :meth:`is_degenerated`). + Otherwise the input is checked and a :class:`DegeneratedSequenceError` + is raised if such a sequence is detected. + EXAMPLES:: sage: Seq2 = kRegularSequenceSpace(2, ZZ) - sage: S = Seq2((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), - ....: vector([0, 1]), vector([1, 0])).transposed(); S + sage: S = Seq2((Matrix([[3, 0], [6, 1]]), Matrix([[0, 1], [-6, 5]])), + ....: vector([1, 0]), vector([0, 1])); S 2-regular sequence 0, 1, 3, 5, 9, 11, 15, 19, 27, 29, ... We can access the coefficients of a sequence by @@ -231,8 +263,8 @@ def _repr_(self): TESTS:: sage: Seq2 = kRegularSequenceSpace(2, ZZ) - sage: s = Seq2((Matrix([[3, 6], [0, 1]]), Matrix([[0, -6], [1, 5]])), - ....: vector([0, 1]), vector([1, 0])).transposed() + sage: s = Seq2((Matrix([[3, 0], [6, 1]]), Matrix([[0, 1], [-6, 5]])), + ....: vector([1, 0]), vector([0, 1])) sage: repr(s) # indirect doctest '2-regular sequence 0, 1, 3, 5, 9, 11, 15, 19, 27, 29, ...' """ @@ -316,6 +348,241 @@ def __iter__(self): from itertools import count return iter(self[n] for n in count()) + @cached_method + def is_degenerated(self): + r""" + Return whether this `k`-regular sequence is degenerated, + i.e., whether this `k`-regular sequence does not satisfiy + `\mu[0] \mathit{right} = \mathit{right}`. + + EXAMPLES:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1])) # indirect doctest + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: S.is_degenerated() + True + + :: + + sage: C = Seq2((Matrix([[2, 0], [2, 1]]), Matrix([[0, 1], [-2, 3]])), + ....: vector([1, 0]), vector([0, 1])) + sage: C.is_degenerated() + False + """ + from sage.rings.integer_ring import ZZ + return (self.mu[ZZ(0)] * self.right) != self.right + + def _error_if_degenerated_(self): + r""" + Raise an error if this `k`-regular sequence is degenerated, + i.e., if this `k`-regular sequence does not satisfiy + `\mu[0] \mathit{right} = \mathit{right}`. + + TESTS:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([[3, 2], [0, 1]]), Matrix([[2, 0], [1, 3]])), # indirect doctest + ....: left=vector([0, 1]), right=vector([1, 0])) + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + """ + if self.is_degenerated(): + raise DegeneratedSequenceError( + "degenerated sequence: mu[0]*right != right. " + "Using such a sequence might lead to wrong results. " + "You can use 'allow_degenerated_sequence=True' followed by " + "a call of method .regenerated() " + "for correcting this.") + + @cached_method + @minimize_result + def regenerated(self): + r""" + Return a `k`-regular sequence that satisfies + `\mu[0] \mathit{right} = \mathit{right}` with the same values as + this sequence. + + INPUT: + + - ``minimize`` -- (default: ``None``) a boolean or ``None``. + If ``True``, then :meth:`~RecognizableSeries.minimized` is called after the operation, + if ``False``, then not. If this argument is ``None``, then + the default specified by the parent's ``minimize_results`` is used. + + OUTPUT: + + A :class:`kRegularSequence` + + ALGORITHM: + + Theorem B of [HKL2022]_ with `n_0 = 1`. + + EXAMPLES:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + + The following linear representation of `S` is chosen badly (is + degenerated, see :meth:`is_degenerated`), as `\mu(0)` applied on + `\mathit{right}` does not equal `\mathit{right}`:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: S.is_degenerated() + True + + However, we can regenerate the sequence `S`:: + + sage: H = S.regenerated() + sage: H + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: H.linear_representation() + ((1, 0), + Finite family {0: [ 0 1] + [-2 3], + 1: [3 0] + [6 0]}, + (1, 1)) + sage: H.is_degenerated() + False + + TESTS:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: H = S.regenerated(minimize=False) + sage: H.linear_representation() + ((1, 0), + Finite family {0: [ 2|-1] + [--+--] + [ 0| 1], + 1: [3|0] + [-+-] + [0|0]}, + (1, 1)) + sage: H.is_degenerated() + False + """ + if not self.is_degenerated(): + return self + + from sage.matrix.constructor import Matrix + from sage.matrix.special import zero_matrix, identity_matrix + from sage.modules.free_module_element import vector + + P = self.parent() + dim = self.dimension() + Zc = zero_matrix(dim, 1) + Zr = zero_matrix(1, dim) + I = identity_matrix(dim) + + itA = iter(P.alphabet()) + z = next(itA) + W0 = Matrix(dim, 1, (I - self.mu[z]) * self.right) + mu = {z: Matrix.block([[self.mu[z], W0], [Zr, 1]])} + mu.update((r, Matrix.block([[self.mu[r], Zc], [Zr, 0]])) + for r in itA) + + return P.element_class( + P, mu, + vector(tuple(self.left) + (0,)), + vector(tuple(self.right) + (1,))) + + def transposed(self, allow_degenerated_sequence=False): + r""" + Return the transposed sequence. + + INPUT: + + - ``allow_degenerated_sequence`` -- (default: ``False``) a boolean. If set, then + there will be no check if the transposed sequence is a degenerated sequence + (see :meth:`is_degenerated`). + Otherwise the transposed sequence is checked and a :class:`DegeneratedSequenceError` + is raised if such a sequence is detected. + + OUTPUT: + + A :class:`kRegularSequence` + + Each of the matrices in :meth:`mu ` is transposed. Additionally + the vectors :meth:`left ` and :meth:`right ` are switched. + + EXAMPLES:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: U = Seq2((Matrix([[3, 2], [0, 1]]), Matrix([[2, 0], [1, 3]])), + ....: left=vector([0, 1]), right=vector([1, 0]), + ....: allow_degenerated_sequence=True) + sage: U.is_degenerated() + True + sage: Ut = U.transposed() + sage: Ut.linear_representation() + ((1, 0), + Finite family {0: [3 0] + [2 1], + 1: [2 1] + [0 3]}, + (0, 1)) + sage: Ut.is_degenerated() + False + + sage: Ut.transposed() + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + sage: Utt = Ut.transposed(allow_degenerated_sequence=True) + sage: Utt.is_degenerated() + True + + .. SEEALSO:: + + :meth:`RecognizableSeries.transposed ` + """ + element = super().transposed() + if not allow_degenerated_sequence: + element._error_if_degenerated_() + return element + + def _minimized_right_(self): + r""" + Return a regular sequence equivalent to this series, but + with a right minimized linear representation. + + OUTPUT: + + A :class:`kRegularSequence` + + .. SEEALSO:: + + :meth:`RecognizableSeries._minimized_right_ ` + + TESTS:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([[3, 0], [2, 1]]), Matrix([[2, 1], [0, 3]])), # indirect doctest + ....: left=vector([1, 0]), right=vector([0, 1])).minimized() + 2-regular sequence 0, 1, 3, 5, 9, 11, 15, 19, 27, 29, ... + """ + return self.transposed(allow_degenerated_sequence=True)._minimized_left_().transposed(allow_degenerated_sequence=True) + @minimize_result def subsequence(self, a, b): r""" @@ -489,6 +756,27 @@ def subsequence(self, a, b): Traceback (most recent call last): ... ValueError: a=-1 is not nonnegative. + + The following linear representation of `S` is chosen badly (is + degenerated, see :meth:`is_degenerated`), as `\mu(0)` applied on + `\mathit{right}` does not equal `\mathit{right}`:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + + This leads to the wrong result + :: + + sage: S.subsequence(1, -4) + 2-regular sequence 0, 0, 0, 0, 8, 12, 12, 18, 24, 36, ... + + We get the correct result by + :: + + sage: S.regenerated().subsequence(1, -4) + 2-regular sequence 0, 0, 0, 0, 1, 3, 6, 9, 12, 18, ... """ from itertools import chain from sage.rings.integer_ring import ZZ @@ -753,7 +1041,7 @@ def forward_differences(self, **kwds): def partial_sums(self, include_n=False): r""" Return the sequence of partial sums of this - `k`-regular sequence. That is, the `n`th entry of the result + `k`-regular sequence. That is, the `n`-th entry of the result is the sum of the first `n` entries in the original sequence. INPUT: @@ -795,6 +1083,55 @@ def partial_sums(self, include_n=False): sage: C.partial_sums(include_n=True) 2-regular sequence 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, ... + The following linear representation of `S` is chosen badly (is + degenerated, see :meth:`is_degenerated`), as `\mu(0)` applied on + `\mathit{right}` does not equal `\mathit{right}`:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + + Therefore, building partial sums produces a wrong result:: + + sage: H = S.partial_sums(include_n=True, minimize=False) + sage: H + 2-regular sequence 1, 5, 16, 25, 62, 80, 98, 125, 274, 310, ... + sage: H = S.partial_sums(minimize=False) + sage: H + 2-regular sequence 0, 2, 10, 16, 50, 62, 80, 98, 250, 274, ... + + We can :meth:`~kRegularSequenceSpace.guess` the correct representation:: + + sage: from itertools import islice + sage: L = []; ps = 0 + sage: for s in islice(S, 110): + ....: ps += s + ....: L.append(ps) + sage: G = Seq2.guess(lambda n: L[n]) + sage: G + 2-regular sequence 1, 4, 10, 19, 31, 49, 67, 94, 118, 154, ... + sage: G.linear_representation() + ((1, 0, 0, 0), + Finite family {0: [ 0 1 0 0] + [ 0 0 0 1] + [ -5 5 1 0] + [ 10 -17 0 8], + 1: [ 0 0 1 0] + [ -5 3 3 0] + [ -5 0 6 0] + [-30 21 10 0]}, + (1, 1, 4, 1)) + sage: G.minimized().dimension() == G.dimension() + True + + Or we regenerate the sequence `S` first:: + + sage: S.regenerated().partial_sums(include_n=True, minimize=False) + 2-regular sequence 1, 4, 10, 19, 31, 49, 67, 94, 118, 154, ... + sage: S.regenerated().partial_sums(minimize=False) + 2-regular sequence 0, 1, 4, 10, 19, 31, 49, 67, 94, 118, ... + TESTS:: sage: E.linear_representation() @@ -994,6 +1331,63 @@ def _n_to_index_(self, n): except OverflowError: raise ValueError('value {} of index is negative'.format(n)) from None + def some_elements(self): + r""" + Return some elements of this `k`-regular sequence. + + See :class:`TestSuite` for a typical use case. + + OUTPUT: + + An iterator + + EXAMPLES:: + + sage: tuple(kRegularSequenceSpace(2, ZZ).some_elements()) + (2-regular sequence 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, ..., + 2-regular sequence 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, ..., + 2-regular sequence 1, 1, 0, 1, -1, 0, 0, 1, -2, -1, ..., + 2-regular sequence 2, -1, 0, 0, 0, -1, 0, 0, 0, 0, ..., + 2-regular sequence 1, 1, 0, 1, 5, 0, 0, 1, -33, 5, ..., + 2-regular sequence -5, 0, 0, 0, 0, 0, 0, 0, 0, 0, ..., + 2-regular sequence -59, -20, 0, -20, 0, 0, 0, -20, 0, 0, ..., + ... + 2-regular sequence 2210, 170, 0, 0, 0, 0, 0, 0, 0, 0, ...) + """ + return iter(element.regenerated() + for element + in super(kRegularSequenceSpace, self).some_elements( + allow_degenerated_sequence=True)) + + def _element_constructor_(self, *args, **kwds): + r""" + Return a `k`-regular sequence. + + See :class:`kRegularSequenceSpace` for details. + + TESTS:: + + sage: Seq2 = kRegularSequenceSpace(2, ZZ) + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1])) + Traceback (most recent call last): + ... + DegeneratedSequenceError: degenerated sequence: mu[0]*right != right. + Using such a sequence might lead to wrong results. + You can use 'allow_degenerated_sequence=True' followed + by a call of method .regenerated() for correcting this. + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True).regenerated() + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + """ + allow_degenerated_sequence = kwds.pop('allow_degenerated_sequence', False) + element = super(kRegularSequenceSpace, self)._element_constructor_(*args, **kwds) + if not allow_degenerated_sequence: + element._error_if_degenerated_() + return element + def guess(self, f, n_verify=100, max_exponent=10, sequence=None): r""" Guess a `k`-regular sequence whose first terms coincide with `(f(n))_{n\geq0}`. @@ -1057,7 +1451,7 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): sage: Seq2 = kRegularSequenceSpace(2, ZZ) sage: import logging - sage: logging.basicConfig(level=logging.INFO) + sage: logging.getLogger().setLevel(logging.INFO) sage: S1 = Seq2.guess(s); S1 INFO:...:including f_{1*m+0} INFO:...:including f_{2*m+1} @@ -1132,6 +1526,34 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): 1: [0 1] [0 1]}, (0, 1)) + sage: logging.getLogger().setLevel(logging.WARN) + + The following linear representation of `S` is chosen badly (is + degenerated, see :meth:`is_degenerated`), as `\mu(0)` applied on + `\mathit{right}` does not equal `\mathit{right}`:: + + sage: S = Seq2((Matrix([2]), Matrix([3])), vector([1]), vector([1]), + ....: allow_degenerated_sequence=True) + sage: S + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: S.is_degenerated() + True + + However, we can :meth:`~kRegularSequenceSpace.guess` a `2`-regular sequence of dimension `2`:: + + sage: G = Seq2.guess(lambda n: S[n]) + sage: G + 2-regular sequence 1, 3, 6, 9, 12, 18, 18, 27, 24, 36, ... + sage: G.linear_representation() + ((1, 0), + Finite family {0: [ 0 1] + [-2 3], + 1: [3 0] + [6 0]}, + (1, 1)) + + sage: G == S.regenerated() + True TESTS:: @@ -1160,7 +1582,7 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): 1: [1]}, (1)) - We guess some partial sums sequences:: + We :meth:`~kRegularSequenceSpace.guess` some partial sums sequences:: sage: S = Seq2((Matrix([1]), Matrix([2])), vector([1]), vector([1])) sage: S @@ -1257,7 +1679,8 @@ def guess(self, f, n_verify=100, max_exponent=10, sequence=None): sage: A = Seq2( ....: (Matrix([[1, 1], [1, 1]]), Matrix([[1, 1], [1, 1]])), - ....: left=(1, 1), right=(1, 1)) + ....: left=(1, 1), right=(1, 1), + ....: allow_degenerated_sequence=True) sage: Seq2.guess(lambda n: n, sequence=A, n_verify=5) Traceback (most recent call last): ... @@ -1416,7 +1839,7 @@ def from_recurrence(self, *args, **kwds): r""" Construct the unique `k`-regular sequence which fulfills the given recurrence relations and initial values. The recurrence relations have to - have the specific shape of `k`-recursive sequences as described in [HKL2021]_, + have the specific shape of `k`-recursive sequences as described in [HKL2022]_, and are either given as symbolic equations, e.g., :: @@ -1465,7 +1888,7 @@ def from_recurrence(self, *args, **kwds): ``coefficient_ring``. The recurrence relations above uniquely determine a `k`-regular sequence; - see [HKL2021]_ for further information. + see [HKL2022]_ for further information. - ``function`` -- symbolic function ``f`` occurring in the equations @@ -1478,11 +1901,11 @@ def from_recurrence(self, *args, **kwds): following arguments are required: - ``M`` -- parameter of the recursive sequences, - see [HKL2021]_, Definition 3.1, as well as in the description of + see [HKL2022]_, Definition 3.1, as well as in the description of ``equations`` above - ``m`` -- parameter of the recursive sequences, - see [HKL2021]_, Definition 3.1, as well as in the description of + see [HKL2022]_, Definition 3.1, as well as in the description of ``equations`` above - ``coeffs`` -- a dictionary where ``coeffs[(r, j)]`` is the @@ -1500,7 +1923,7 @@ def from_recurrence(self, *args, **kwds): - ``inhomogeneities`` -- (default: ``{}``) a dictionary mapping integers ``r`` to the inhomogeneity `g_r` as given - in [HKL2021]_, Corollary D. All inhomogeneities have to be + in [HKL2022]_, Corollary D. All inhomogeneities have to be regular sequences from ``self`` or elements of ``coefficient_ring``. OUTPUT: a :class:`kRegularSequence` @@ -2568,13 +2991,13 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) OUTPUT: a namedtuple ``recurrence_rules`` consisting of - ``M``, ``m``, ``l``, ``u``, ``offset`` -- parameters of the recursive - sequences, see [HKL2021]_, Definition 3.1 + sequences, see [HKL2022]_, Definition 3.1 - ``ll``, ``uu``, ``n1``, ``dim`` -- parameters and dimension of the - resulting linear representation, see [HKL2021]_, Theorem A + resulting linear representation, see [HKL2022]_, Theorem A - ``coeffs`` -- a dictionary mapping ``(r, j)`` to the coefficients - `c_{r, j}` as given in [HKL2021]_, Equation (3.1). + `c_{r, j}` as given in [HKL2022]_, Equation (3.1). If ``coeffs[(r, j)]`` is not given for some ``r`` and ``j``, then it is assumed to be zero. @@ -2582,7 +3005,7 @@ def parameters(self, M, m, coeffs, initial_values, offset=0, inhomogeneities={}) ``n``-th value of the sequence - ``inhomogeneities`` -- a dictionary mapping integers ``r`` - to the inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. + to the inhomogeneity `g_r` as given in [HKL2022]_, Corollary D. EXAMPLES:: @@ -2760,10 +3183,10 @@ def values(self, *, M, m, l, u, ll, coeffs, INPUT: - ``M``, ``m``, ``l``, ``u``, ``offset`` -- parameters of the - recursive sequences, see [HKL2021]_, Definition 3.1 + recursive sequences, see [HKL2022]_, Definition 3.1 - ``ll`` -- parameter of the resulting linear representation, - see [HKL2021]_, Theorem A + see [HKL2022]_, Theorem A - ``coeffs`` -- a dictionary where ``coeffs[(r, j)]`` is the coefficient `c_{r,j}` as given in :meth:`kRegularSequenceSpace.from_recurrence`. @@ -2777,7 +3200,7 @@ def values(self, *, M, m, l, u, ll, coeffs, determine the linear representation - ``inhomogeneities`` -- a dictionary mapping integers ``r`` - to the inhomogeneity `g_r` as given in [HKL2021]_, Corollary D. + to the inhomogeneity `g_r` as given in [HKL2022]_, Corollary D. OUTPUT: @@ -2951,15 +3374,15 @@ def f(n): def ind(self, M, m, ll, uu): r""" Determine the index operator corresponding to the recursive - sequence as defined in [HKL2021]_. + sequence as defined in [HKL2022]_. INPUT: - ``M``, ``m`` -- parameters of the recursive sequences, - see [HKL2021]_, Definition 3.1 + see [HKL2022]_, Definition 3.1 - ``ll``, ``uu`` -- parameters of the resulting linear representation, - see [HKL2021]_, Theorem A + see [HKL2022]_, Theorem A OUTPUT: @@ -3015,7 +3438,7 @@ def ind(self, M, m, ll, uu): def shifted_inhomogeneities(self, recurrence_rules): r""" Return a dictionary of all needed shifted inhomogeneities as described - in the proof of Coroallary D in [HKL2021]_. + in the proof of Corollary D in [HKL2022]_. INPUT: @@ -3025,9 +3448,9 @@ def shifted_inhomogeneities(self, recurrence_rules): OUTPUT: A dictionary mapping `r` to the regular sequence - `\sum_i g_r(n + i)` for `g_r` as given in [HKL2021]_, Corollary D, + `\sum_i g_r(n + i)` for `g_r` as given in [HKL2022]_, Corollary D, and `i` between `\lfloor\ell'/k^{M}\rfloor` and - `\lfloor (k^{M-1} - k^{m} + u')/k^{M}\rfloor + 1`; see [HKL2021]_, + `\lfloor (k^{M-1} - k^{m} + u')/k^{M}\rfloor + 1`; see [HKL2022]_, proof of Corollary D. The first blocks of the corresponding vector-valued sequence (obtained from its linear representation) correspond to the sequences `g_r(n + i)` where @@ -3126,7 +3549,7 @@ def shifted_inhomogeneities(self, recurrence_rules): def v_eval_n(self, recurrence_rules, n): r""" - Return the vector `v(n)` as given in [HKL2021]_, Theorem A. + Return the vector `v(n)` as given in [HKL2022]_, Theorem A. INPUT: @@ -3195,7 +3618,7 @@ def matrix(self, recurrence_rules, rem, correct_offset=True): - ``correct_offset`` -- (default: ``True``) a boolean. If ``True``, then the resulting linear representation has no - offset. See [HKL2021]_ for more information. + offset. See [HKL2022]_ for more information. OUTPUT: a matrix diff --git a/src/sage/combinat/partition_algebra.py b/src/sage/combinat/partition_algebra.py index 9136b682c59..0b7eb06c3a5 100644 --- a/src/sage/combinat/partition_algebra.py +++ b/src/sage/combinat/partition_algebra.py @@ -1974,7 +1974,7 @@ def set_partition_composition(sp1, sp2): True """ g = pair_to_graph(sp1, sp2) - connected_components = g.connected_components() + connected_components = g.connected_components(sort=False) res = [] total_removed = 0 diff --git a/src/sage/combinat/posets/mobile.py b/src/sage/combinat/posets/mobile.py index d09eb370fa7..f291400ce5f 100644 --- a/src/sage/combinat/posets/mobile.py +++ b/src/sage/combinat/posets/mobile.py @@ -123,7 +123,7 @@ def _is_valid_ribbon(self, ribbon): continue G_un.delete_edge(lc, r) - P = Poset(G.subgraph(G_un.connected_component_containing_vertex(lc))) + P = Poset(G.subgraph(G_un.connected_component_containing_vertex(lc, sort=False))) if P.top() != lc or not P.is_d_complete(): return False G_un.add_edge(lc, r) diff --git a/src/sage/combinat/posets/posets.py b/src/sage/combinat/posets/posets.py index 65ad12be6bf..d37e9676d7c 100644 --- a/src/sage/combinat/posets/posets.py +++ b/src/sage/combinat/posets/posets.py @@ -1368,11 +1368,11 @@ def _latex_(self): EXAMPLES:: sage: P = Poset(([1,2], [[1,2]]), cover_relations = True) - sage: print(P._latex_()) #optional - dot2tex graphviz + sage: print(P._latex_()) # optional - dot2tex graphviz \begin{tikzpicture}[>=latex,line join=bevel,] %% - \node (node_...) at (6.0...bp,...bp) [draw,draw=none] {$...$}; - \node (node_...) at (6.0...bp,...bp) [draw,draw=none] {$...$}; + \node (node_...) at (5...bp,...bp) [draw,draw=none] {$...$}; + \node (node_...) at (5...bp,...bp) [draw,draw=none] {$...$}; \draw [black,->] (node_...) ..controls (...bp,...bp) and (...bp,...bp) .. (node_...); % \end{tikzpicture} @@ -5330,7 +5330,7 @@ def edge_color(va, vb): fusion = fusion.transitive_closure() resu = [] - for s in fusion.connected_components(): + for s in fusion.connected_components(sort=False): subg = [x for x in prod_dg if all(x[i] == v0[i] for i in factors_range if i not in s)] resu.append(Poset(prod_dg.subgraph(subg))) diff --git a/src/sage/combinat/recognizable_series.py b/src/sage/combinat/recognizable_series.py index 5596216845a..e5856aacef9 100644 --- a/src/sage/combinat/recognizable_series.py +++ b/src/sage/combinat/recognizable_series.py @@ -318,6 +318,12 @@ def minimize_result(operation): if ``False``, then not. If this argument is ``None``, then the default specified by the parent's ``minimize_results`` is used. + .. NOTE:: + + If the result of ``operation`` is ``self``, then minimization is + not applied unless ``minimize=True`` is explicitly set, + in particular, independent of the parent's ``minimize_results``. + TESTS:: sage: from sage.combinat.recognizable_series import minimize_result @@ -352,14 +358,39 @@ def minimize_result(operation): some result minimized sage: S('some').operation(minimize=False) some result + + :: + + sage: class T(S): + ....: @minimize_result + ....: def nooperation(self): + ....: return self + sage: t = T('some') + sage: p.minimize_results = True + sage: t.nooperation() is t + True + sage: t.nooperation(minimize=True) is t + False + sage: t.nooperation(minimize=False) is t + True + sage: p.minimize_results = False + sage: t.nooperation() is t + True + sage: t.nooperation(minimize=True) is t + False + sage: t.nooperation(minimize=False) is t + True """ @wraps(operation) def minimized(self, *args, **kwds): minimize = kwds.pop('minimize', None) - if minimize is None: - minimize = self.parent().minimize_results result = operation(self, *args, **kwds) + if minimize is not True and result is self: + return result + + if minimize is None: + minimize = self.parent().minimize_results if minimize: result = result.minimized() @@ -1045,9 +1076,11 @@ def tr(M): T = M.transpose() T.set_immutable() return T - return self.parent()(self.mu.map(tr), - left=self.right, - right=self.left) + + P = self.parent() + return P.element_class(P, self.mu.map(tr), + left=self.right, + right=self.left) @cached_method def minimized(self): @@ -1871,12 +1904,16 @@ def _an_element_(self): for i, _ in enumerate(self.alphabet())), vector([z, e]), right=vector([e, z])) - def some_elements(self): + def some_elements(self, **kwds): r""" Return some elements of this recognizable series space. See :class:`TestSuite` for a typical use case. + INPUT: + + - ``kwds`` are passed on to the element constructor + OUTPUT: An iterator @@ -1913,7 +1950,7 @@ def some_elements(self): LR = list(islice(elements_V, 2)) if len(mu) != k or len(LR) != 2: break - yield self(mu, *LR) + yield self(mu, *LR, **kwds) @cached_method def one_hadamard(self): diff --git a/src/sage/combinat/root_system/ambient_space.py b/src/sage/combinat/root_system/ambient_space.py index b5e4a90e6ad..7c7159a444c 100644 --- a/src/sage/combinat/root_system/ambient_space.py +++ b/src/sage/combinat/root_system/ambient_space.py @@ -119,7 +119,7 @@ def _test_norm_of_simple_roots(self, **options): T = self.cartan_type() D = T.symmetrizer() alpha = self.simple_roots() - for C in T.dynkin_diagram().connected_components(): + for C in T.dynkin_diagram().connected_components(sort=False): tester.assertEqual(len( set( alpha[i].scalar(alpha[i]) / D[i] for i in C ) ), 1) # FIXME: attribute or method? diff --git a/src/sage/combinat/root_system/cartan_type.py b/src/sage/combinat/root_system/cartan_type.py index d8a37f350ce..013ceca8a02 100644 --- a/src/sage/combinat/root_system/cartan_type.py +++ b/src/sage/combinat/root_system/cartan_type.py @@ -1739,7 +1739,7 @@ def symmetrizer(self): M[i, n * i + j] = m[i,j] M[j, n * i + j] -= m[j,i] kern = M.integer_kernel() - c = len(self.dynkin_diagram().connected_components()) + c = len(self.dynkin_diagram().connected_components(sort=False)) if kern.dimension() < c: # the Cartan matrix is not symmetrizable return None diff --git a/src/sage/combinat/tutorial.py b/src/sage/combinat/tutorial.py index 51ade61b8d1..a5edc7146ea 100644 --- a/src/sage/combinat/tutorial.py +++ b/src/sage/combinat/tutorial.py @@ -275,37 +275,37 @@ introduce two variables, `C` and `z`, and we define the equation:: - sage: C, z = var('C,z') # optional - sage.symbolic - sage: sys = [ C == z + C*C ] # optional - sage.symbolic + sage: C, z = var('C,z') # needs sage.symbolic + sage: sys = [ C == z + C*C ] # needs sage.symbolic There are two solutions, which happen to have closed forms:: - sage: sol = solve(sys, C, solution_dict=True); sol # optional - sage.symbolic + sage: sol = solve(sys, C, solution_dict=True); sol # needs sage.symbolic [{C: -1/2*sqrt(-4*z + 1) + 1/2}, {C: 1/2*sqrt(-4*z + 1) + 1/2}] - sage: s0 = sol[0][C]; s1 = sol[1][C] # optional - sage.symbolic + sage: s0 = sol[0][C]; s1 = sol[1][C] # needs sage.symbolic and whose Taylor series begin as follows:: - sage: s0.series(z, 6) # optional - sage.symbolic + sage: s0.series(z, 6) # needs sage.symbolic 1*z + 1*z^2 + 2*z^3 + 5*z^4 + 14*z^5 + Order(z^6) - sage: s1.series(z, 6) # optional - sage.symbolic + sage: s1.series(z, 6) # needs sage.symbolic 1 + (-1)*z + (-1)*z^2 + (-2)*z^3 + (-5)*z^4 + (-14)*z^5 + Order(z^6) The second solution is clearly aberrant, while the first one gives the expected coefficients. Therefore, we set:: - sage: C = s0 # optional - sage.symbolic + sage: C = s0 # needs sage.symbolic We can now calculate the next terms:: - sage: C.series(z, 11) # optional - sage.symbolic + sage: C.series(z, 11) # needs sage.symbolic 1*z + 1*z^2 + 2*z^3 + 5*z^4 + 14*z^5 + 42*z^6 + 132*z^7 + 429*z^8 + 1430*z^9 + 4862*z^10 + Order(z^11) or calculate, more or less instantaneously, the 100-th coefficient:: - sage: C.series(z, 101).coefficient(z,100) # optional - sage.symbolic + sage: C.series(z, 101).coefficient(z,100) # needs sage.symbolic 227508830794229349661819540395688853956041682601541047340 It is unfortunate to have to recalculate everything if at some point we @@ -338,19 +338,19 @@ We now return to the closed form of `C(z)`:: - sage: z = var('z') # optional - sage.symbolic - sage: C = s0; C # optional - sage.symbolic + sage: z = var('z') # needs sage.symbolic + sage: C = s0; C # needs sage.symbolic -1/2*sqrt(-4*z + 1) + 1/2 The `n`-th coefficient in the Taylor series for `C(z)` being given by `\frac{1}{n!} C(z)^{(n)}(0)`, we look at the successive derivatives `C(z)^{(n)}(z)`:: - sage: derivative(C, z, 1) # optional - sage.symbolic + sage: derivative(C, z, 1) # needs sage.symbolic 1/sqrt(-4*z + 1) - sage: derivative(C, z, 2) # optional - sage.symbolic + sage: derivative(C, z, 2) # needs sage.symbolic 2/(-4*z + 1)^(3/2) - sage: derivative(C, z, 3) # optional - sage.symbolic + sage: derivative(C, z, 3) # needs sage.symbolic 12/(-4*z + 1)^(5/2) This suggests the existence of a simple explicit formula, which we will @@ -360,7 +360,7 @@ Taking successive quotients:: - sage: [ (d(n+1) / d(n)) for n in range(1,17) ] # optional - sage.symbolic + sage: [ (d(n+1) / d(n)) for n in range(1,17) ] # needs sage.symbolic [2, 6, 10, 14, 18, 22, 26, 30, 34, 38, 42, 46, 50, 54, 58, 62] we observe that `d_n` satisfies the recurrence relation @@ -373,9 +373,9 @@ We check this:: - sage: n = var('n') # optional - sage.symbolic - sage: c = 1/n*binomial(2*(n-1),n-1) # optional - sage.symbolic - sage: [c.subs(n=k) for k in range(1, 11)] # optional - sage.symbolic + sage: n = var('n') # needs sage.symbolic + sage: c = 1/n*binomial(2*(n-1),n-1) # needs sage.symbolic + sage: [c.subs(n=k) for k in range(1, 11)] # needs sage.symbolic [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862] sage: [catalan_number(k-1) for k in range(1, 11)] [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862] @@ -383,14 +383,14 @@ We can now calculate coefficients much further; here we calculate `c_{100000}` which has more than `60000` digits:: - sage: cc = c(n=100000) # optional - sage.symbolic + sage: cc = c(n=100000) # needs sage.symbolic This takes a couple of seconds:: - sage: %time cc = c(100000) # not tested # optional - sage.symbolic + sage: %time cc = c(100000) # not tested # needs sage.symbolic CPU times: user 2.34 s, sys: 0.00 s, total: 2.34 s Wall time: 2.34 s - sage: ZZ(cc).ndigits() # optional - sage.symbolic + sage: ZZ(cc).ndigits() # needs sage.symbolic 60198 The methods which we have used generalize to all recursively defined @@ -417,11 +417,12 @@ In the present case, `P=y^2-y+x`. We formally differentiate this equation with respect to `z`:: - sage: x, y, z = var('x, y, z') # optional - sage.symbolic - sage: P = function('P')(x, y) # optional - sage.symbolic - sage: C = function('C')(z) # optional - sage.symbolic - sage: equation = P(x=z, y=C) == 0 # optional - sage.symbolic - sage: diff(equation, z) # optional - sage.symbolic + sage: # needs sage.symbolic + sage: x, y, z = var('x, y, z') + sage: P = function('P')(x, y) + sage: C = function('C')(z) + sage: equation = P(x=z, y=C) == 0 + sage: diff(equation, z) diff(C(z), z)*D[1](P)(z, C(z)) + D[0](P)(z, C(z)) == 0 or, in a more readable format, @@ -434,9 +435,9 @@ In the case of complete binary trees, this gives:: - sage: P = y^2 - y + x # optional - sage.symbolic - sage: Px = diff(P, x); Py = diff(P, y) # optional - sage.symbolic - sage: - Px / Py # optional - sage.symbolic + sage: P = y^2 - y + x # needs sage.symbolic + sage: Px = diff(P, x); Py = diff(P, y) # needs sage.symbolic + sage: - Px / Py # needs sage.symbolic -1/(2*y - 1) Recall that `P(z, C(z))=0`. Thus, we can calculate this fraction @@ -447,7 +448,7 @@ sage: Qx = QQ['x'].fraction_field() sage: Qxy = Qx['y'] - sage: R = Qxy.quo(P); R # optional - sage.symbolic + sage: R = Qxy.quo(P); R # needs sage.symbolic Univariate Quotient Polynomial Ring in ybar over Fraction Field of Univariate Polynomial Ring in x over Rational Field with modulus y^2 - y + x @@ -458,7 +459,7 @@ We continue the calculation of this fraction in `R`:: - sage: fraction = - R(Px) / R(Py); fraction # optional - sage.symbolic + sage: fraction = - R(Px) / R(Py); fraction # needs sage.symbolic (1/2/(x - 1/4))*ybar - 1/4/(x - 1/4) .. note:: @@ -474,9 +475,9 @@ `z` and `C(z)` to obtain an expression for `\frac{d}{dz}C(z)`:: - sage: fraction = fraction.lift(); fraction # optional - sage.symbolic + sage: fraction = fraction.lift(); fraction # needs sage.symbolic (1/2/(x - 1/4))*y - 1/4/(x - 1/4) - sage: fraction(x=z, y=C) # optional - sage.symbolic + sage: fraction(x=z, y=C) # needs sage.symbolic 2*C(z)/(4*z - 1) - 1/(4*z - 1) or, more legibly, @@ -486,13 +487,14 @@ In this simple case, we can directly deduce from this expression a linear differential equation with coefficients in `\QQ[z]`:: - sage: equadiff = diff(C,z) == fraction(x=z, y=C) # optional - sage.symbolic - sage: equadiff # optional - sage.symbolic + sage: # needs sage.symbolic + sage: equadiff = diff(C,z) == fraction(x=z, y=C) + sage: equadiff diff(C(z), z) == 2*C(z)/(4*z - 1) - 1/(4*z - 1) - sage: equadiff = equadiff.simplify_rational() # optional - sage.symbolic - sage: equadiff = equadiff * equadiff.rhs().denominator() # optional - sage.symbolic - sage: equadiff = equadiff - equadiff.rhs() # optional - sage.symbolic - sage: equadiff # optional - sage.symbolic + sage: equadiff = equadiff.simplify_rational() + sage: equadiff = equadiff * equadiff.rhs().denominator() + sage: equadiff = equadiff - equadiff.rhs() + sage: equadiff (4*z - 1)*diff(C(z), z) - 2*C(z) + 1 == 0 or, more legibly, @@ -501,10 +503,10 @@ It is trivial to verify this equation on the closed form:: - sage: Cf = sage.symbolic.function_factory.function('C') # optional - sage.symbolic - sage: equadiff.substitute_function(Cf, s0.function(z)) # optional - sage.symbolic + sage: Cf = sage.symbolic.function_factory.function('C') # needs sage.symbolic + sage: equadiff.substitute_function(Cf, s0.function(z)) # needs sage.symbolic (4*z - 1)/sqrt(-4*z + 1) + sqrt(-4*z + 1) == 0 - sage: bool(equadiff.substitute_function(Cf, s0.function(z))) # optional - sage.symbolic + sage: bool(equadiff.substitute_function(Cf, s0.function(z))) # needs sage.symbolic True .. On veut non seulement remplacer les occurrences de C(z), mais @@ -780,8 +782,8 @@ Similarly, if we consider the number of compositions of `5` by length, we find a line of Pascal’s triangle:: - sage: x = var('x') # optional - sage.symbolic - sage: sum(x^len(c) for c in C5) # optional - sage.symbolic + sage: x = var('x') # needs sage.symbolic + sage: sum(x^len(c) for c in C5) # needs sage.symbolic x^5 + 4*x^4 + 6*x^3 + 4*x^2 + x The above example uses a functionality which we have not seen yet: @@ -872,12 +874,13 @@ ``Sage``; as a result, the following commands are not yet implemented:: - sage: C = Graphs(5) # todo: not implemented - sage: C.cardinality() # todo: not implemented + sage: # not implemented + sage: C = Graphs(5) + sage: C.cardinality() 34 - sage: Graphs(19).cardinality() # todo: not implemented + sage: Graphs(19).cardinality() 24637809253125004524383007491432768 - sage: Graphs(19).random_element() # todo: not implemented + sage: Graphs(19).random_element() Graph on 19 vertices What we have seen so far also applies, in principle, to finite algebraic @@ -893,8 +896,8 @@ or the algebra of `2\times 2` matrices over the finite field `\ZZ/2\ZZ`:: - sage: C = MatrixSpace(GF(2), 2) # optional - sage.modules sage.rings.finite_rings - sage: C.list() # optional - sage.modules sage.rings.finite_rings + sage: C = MatrixSpace(GF(2), 2) # needs sage.modules sage.rings.finite_rings + sage: C.list() # needs sage.modules sage.rings.finite_rings [ [0 0] [1 0] [0 1] [0 0] [0 0] [1 1] [1 0] [1 0] [0 1] [0 1] [0 0], [0 0], [0 0], [1 0], [0 1], [0 0], [1 0], [0 1], [1 0], [0 1], @@ -902,7 +905,7 @@ [0 0] [1 1] [1 1] [1 0] [0 1] [1 1] [1 1], [1 0], [0 1], [1 1], [1 1], [1 1] ] - sage: C.cardinality() # optional - sage.modules sage.rings.finite_rings + sage: C.cardinality() # needs sage.modules sage.rings.finite_rings 16 .. topic:: Exercise @@ -1146,18 +1149,18 @@ :: - sage: x = var('x') # optional - sage.symbolic - sage: sum(x^len(s) for s in Subsets(8)) # optional - sage.symbolic + sage: x = var('x') # needs sage.symbolic + sage: sum(x^len(s) for s in Subsets(8)) # needs sage.symbolic x^8 + 8*x^7 + 28*x^6 + 56*x^5 + 70*x^4 + 56*x^3 + 28*x^2 + 8*x + 1 :: - sage: sum(x^p.length() for p in Permutations(3)) # optional - sage.symbolic + sage: sum(x^p.length() for p in Permutations(3)) # needs sage.symbolic x^3 + 2*x^2 + 2*x + 1 :: - sage: factor(sum(x^p.length() for p in Permutations(3))) # optional - sage.symbolic + sage: factor(sum(x^p.length() for p in Permutations(3))) # needs sage.symbolic (x^2 + x + 1)*(x + 1) :: @@ -1375,15 +1378,15 @@ to define a formal variable ``Leaf`` for the leaves and a formal 2-ary function ``Node``:: - sage: var('Leaf') # optional - sage.symbolic + sage: var('Leaf') # needs sage.symbolic Leaf - sage: function('Node', nargs=2) # optional - sage.symbolic + sage: function('Node', nargs=2) # needs sage.symbolic Node The second tree in :ref:`figure-examples-catalan-trees` can be represented by the expression:: - sage: tr = Node(Node(Leaf, Node(Leaf, Leaf)), Leaf) # optional - sage.symbolic + sage: tr = Node(Node(Leaf, Node(Leaf, Leaf)), Leaf) # needs sage.symbolic .. _section-constructions: @@ -1666,7 +1669,7 @@ combinatorial species:: sage: from sage.combinat.species.library import * - sage: o = var('o') # optional - sage.symbolic + sage: o = var('o') # needs sage.symbolic We begin by redefining the complete binary trees; to do so, we stipulate the recurrence relation directly on the sets:: @@ -1678,10 +1681,10 @@ Now we can construct the set of trees with five nodes, list them, count them...:: - sage: BT5 = BT.isotypes([o]*5) # optional - sage.symbolic - sage: BT5.cardinality() # optional - sage.symbolic + sage: BT5 = BT.isotypes([o]*5) # needs sage.symbolic + sage: BT5.cardinality() # needs sage.symbolic 14 - sage: BT5.list() # optional - sage.symbolic + sage: BT5.list() # needs sage.symbolic [o*(o*(o*(o*o))), o*(o*((o*o)*o)), o*((o*o)*(o*o)), o*((o*(o*o))*o), o*(((o*o)*o)*o), (o*o)*(o*(o*o)), (o*o)*((o*o)*o), (o*(o*o))*(o*o), ((o*o)*o)*(o*o), @@ -1730,8 +1733,8 @@ :: - sage: FW3 = FW.isotypes([o]*3) # optional - sage.symbolic - sage: FW3.list() # optional - sage.symbolic + sage: FW3 = FW.isotypes([o]*3) # needs sage.symbolic + sage: FW3.list() # needs sage.symbolic [o*(o*(o*{})), o*(o*(({}*o)*{})), o*((({}*o)*o)*{}), (({}*o)*o)*(o*{}), (({}*o)*o)*(({}*o)*{})] diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 5aae3f020c6..4844662073d 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -8,10 +8,20 @@ - David Roe (2012-03-27) -- initial version, based on Robert Bradshaw's code. """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein -# Copyright (C) 2016 Jeroen Demeyer +# Copyright (C) 2012-2013 David Roe +# 2012-2013 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013-2014 Volker Braun +# 2013-2018 Jeroen Demeyer +# 2013-2021 John H. Palmieri +# 2017 Erik M. Bray +# 2017-2021 Frédéric Chapoton +# 2018 Sébastien Labbé +# 2019 François Bissey +# 2020-2023 Matthias Koeppe +# 2022 Michael Orlitzky +# 2022 Sebastian Oehms # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -20,6 +30,7 @@ # https://www.gnu.org/licenses/ # **************************************************************************** +import importlib import random import os import sys @@ -35,16 +46,14 @@ from sage.misc.temporary_file import tmp_dir from cysignals.signals import AlarmInterrupt, init_cysignals -from .sources import FileDocTestSource, DictAsObject +from .sources import FileDocTestSource, DictAsObject, get_basename from .forker import DocTestDispatcher from .reporting import DocTestReporter from .util import Timer, count_noun, dict_difference from .external import available_software -from .parsing import parse_optional_tags +from .parsing import parse_optional_tags, parse_file_optional_tags, unparse_optional_tags, \ + nodoctest_regex, optionaltag_regex, optionalfiledirective_regex -nodoctest_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest') -optionaltag_regex = re.compile(r'^(\w|[.])+$') -optionalfiledirective_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*sage\.doctest: (.*)') # Optional tags which are always automatically added @@ -114,6 +123,7 @@ def __init__(self, **kwds): self.initial = False self.exitfirst = False self.force_lib = False + self.if_installed = False self.abspath = True # sage-runtests default is False self.verbose = False self.debug = False @@ -137,6 +147,7 @@ def __init__(self, **kwds): # the auto_optional_tags there. self.optional = set(['sage']) | auto_optional_tags self.hide = '' + self.probe = '' # > 0: always run GC before every test # < 0: disable GC @@ -213,18 +224,23 @@ def skipdir(dirname): return True return False -def skipfile(filename, tested_optional_tags=False): +def skipfile(filename, tested_optional_tags=False, *, + if_installed=False, log=None): """ - Return True if and only if the file ``filename`` should not be - doctested. + Return ``True`` if and only if the file ``filename`` should not be doctested. INPUT: - - ``filename`` - name of a file + - ``filename`` -- name of a file - - ``tested_optional_tags`` - a list or tuple or set of optional tags to test, + - ``tested_optional_tags`` -- a list or tuple or set of optional tags to test, or ``False`` (no optional test) or ``True`` (all optional tests) + - ``if_installed`` -- (boolean, default ``False``) whether to skip Python/Cython files + that are not installed as modules + + - ``log`` -- function to call with log messages, or ``None`` + If ``filename`` contains a line of the form ``"# sage.doctest: optional - xyz")``, then this will return ``False`` if "xyz" is in ``tested_optional_tags``. Otherwise, it returns the matching tag @@ -262,28 +278,49 @@ def skipfile(filename, tested_optional_tags=False): base, ext = os.path.splitext(filename) # .rst.txt appear in the installed documentation in subdirectories named "_sources" if ext not in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx', '.rst', '.tex', '.rst.txt'): + if log: + log(f"Skipping '{filename}' because it does not have one of the recognized file name extensions") return True - # These files are created by the jupyter-sphinx extension for internal use and should not be tested if "jupyter_execute" in filename: + if log: + log(f"Skipping '{filename}' because it is created by the jupyter-sphinx extension for internal use and should not be tested") return True - with open(filename) as F: - line_count = 0 - for line in F: - if nodoctest_regex.match(line): + if if_installed and ext in ('.py', '.pyx', '.pxd'): + module_name = get_basename(filename) + try: + if not importlib.util.find_spec(module_name): # tries to import the containing package + if log: + log(f"Skipping '{filename}' because module {module_name} is not present in the venv") return True - if tested_optional_tags is not True: - # Adapted from code in SageDocTestParser.parse - m = optionalfiledirective_regex.match(line) - if m: - if tested_optional_tags is False: - return m.group(2) - optional_tags = parse_optional_tags('#' + m.group(2)) - extra = optional_tags - set(tested_optional_tags) - if extra: - return m.group(2) - line_count += 1 - if line_count >= 10: - break + except ModuleNotFoundError as e: + if log: + log(f"Skipping '{filename}' because module {e.name} cannot be imported") + return True + + with open(filename) as F: + file_optional_tags = parse_file_optional_tags(enumerate(F)) + + if 'not tested' in file_optional_tags: + if log: + log(f"Skipping '{filename}' because it is marked 'nodoctest'") + return True + + if tested_optional_tags is False: + if file_optional_tags: + file_tag_string = unparse_optional_tags(file_optional_tags, prefix='') + if log: + log(f"Skipping '{filename}' because it is marked '# {file_tag_string}'") + return file_tag_string + + elif tested_optional_tags is not True: + extra = set(tag for tag in file_optional_tags + if tag not in tested_optional_tags) + if extra: + file_tag_string = unparse_optional_tags(file_optional_tags, prefix='') + if log: + log(f"Skipping '{filename}' because it is marked '{file_tag_string}'") + return file_tag_string + return False @@ -458,6 +495,23 @@ def __init__(self, options, args): options.optional |= auto_optional_tags options.optional -= options.disabled_optional + if isinstance(options.probe, str): + if options.probe == 'none': + options.probe = '' + s = options.probe.lower() + if not s: + options.probe = set() + else: + options.probe = set(s.split(',')) + if "all" in options.probe: + # Special case to probe all features that are not present + options.probe = True + else: + # Check that all tags are valid + for o in options.probe: + if not optionaltag_regex.search(o): + raise ValueError('invalid optional tag {!r}'.format(o)) + self.options = options self.files = args @@ -893,7 +947,9 @@ def all_doc_sources(): and (filename.endswith(".py") or filename.endswith(".pyx") or filename.endswith(".rst")) - and not skipfile(opj(SAGE_ROOT, filename), self.options.optional)): + and not skipfile(opj(SAGE_ROOT, filename), + True if self.options.optional else False, + if_installed=self.options.if_installed)): self.files.append(os.path.relpath(opj(SAGE_ROOT, filename))) def expand_files_into_sources(self): @@ -953,11 +1009,14 @@ def expand(): if dir[0] == "." or skipdir(os.path.join(root,dir)): dirs.remove(dir) for file in files: - if not skipfile(os.path.join(root, file), self.options.optional): + if not skipfile(os.path.join(root, file), + True if self.options.optional else False, + if_installed=self.options.if_installed): yield os.path.join(root, file) else: - # the user input this file explicitly, so we don't skip it - yield path + if not skipfile(path, True if self.options.optional else False, + if_installed=self.options.if_installed, log=self.log): # log when directly specified filenames are skipped + yield path self.sources = [FileDocTestSource(path, self.options) for path in expand()] def filter_sources(self): @@ -1462,6 +1521,9 @@ def run(self): self.log("Features to be detected: " + ','.join(available_software.detectable())) if self.options.hidden_features: self.log("Hidden features: " + ','.join([f.name for f in self.options.hidden_features])) + if self.options.probe: + self.log("Features to be probed: " + ('all' if self.options.probe is True + else ','.join(self.options.probe))) self.add_files() self.expand_files_into_sources() self.filter_sources() diff --git a/src/sage/doctest/external.py b/src/sage/doctest/external.py index 60299862755..8744bd93e72 100644 --- a/src/sage/doctest/external.py +++ b/src/sage/doctest/external.py @@ -16,7 +16,12 @@ """ #***************************************************************************** -# Copyright (C) 2016 KWANKYU LEE +# Copyright (C) 2016 Kwankyu Lee +# 2018 Thierry Monteil +# 2018-2021 Sébastien Labbé +# 2019 Markus Wageringel +# 2020 John H. Palmieri +# 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/doctest/fixtures.py b/src/sage/doctest/fixtures.py index e6f8fc45f8e..a3b5e9edda7 100644 --- a/src/sage/doctest/fixtures.py +++ b/src/sage/doctest/fixtures.py @@ -33,7 +33,7 @@ """ #***************************************************************************** -# Copyright (C) 2014 Martin von Gagern +# Copyright (C) 2014-2015 Martin von Gagern # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index 24ede7eeb9c..b5521868dd5 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -22,10 +22,22 @@ """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein -# Copyright (C) 2013-2015 Jeroen Demeyer +# Copyright (C) 2012-2013 David Roe +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013-2018 Jeroen Demeyer +# 2013-2020 John H. Palmieri +# 2013-2017 Volker Braun +# 2014 André Apitzsch +# 2014 Darij Grinberg +# 2016-2021 Frédéric Chapoton +# 2017-2019 Erik M. Bray +# 2018 Julian Rüth +# 2020 Jonathan Kliem +# 2020-2023 Matthias Koeppe +# 2022 Markus Wageringel +# 2022 Michael Orlitzky # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -57,9 +69,9 @@ from sage.misc.timing import walltime from .util import Timer, RecordingDict, count_noun from .sources import DictAsObject -from .parsing import OriginalSource, reduce_hex, unparse_optional_tags +from .parsing import OriginalSource, reduce_hex from sage.structure.sage_object import SageObject -from .parsing import SageOutputChecker, pre_hash, get_source +from .parsing import SageOutputChecker, pre_hash, get_source, unparse_optional_tags from sage.repl.user_globals import set_globals from sage.cpython.atexit import restore_atexit from sage.cpython.string import bytes_to_str, str_to_bytes @@ -132,10 +144,11 @@ def init_sage(controller=None): Check that SymPy equation pretty printer is limited in doctest mode to default width (80 chars):: - sage: from sympy import sympify # optional - sage.symbolic - sage: from sympy.printing.pretty.pretty import PrettyPrinter # optional - sage.symbolic - sage: s = sympify('+x^'.join(str(i) for i in range(30))) # optional - sage.symbolic - sage: print(PrettyPrinter(settings={'wrap_line': True}).doprint(s)) # optional - sage.symbolic + sage: # needs sympy + sage: from sympy import sympify + sage: from sympy.printing.pretty.pretty import PrettyPrinter + sage: s = sympify('+x^'.join(str(i) for i in range(30))) + sage: print(PrettyPrinter(settings={'wrap_line': True}).doprint(s)) 29 28 27 26 25 24 23 22 21 20 19 18 17 x + x + x + x + x + x + x + x + x + x + x + x + x + @@ -716,10 +729,18 @@ def compiler(example): outcome = FAILURE # guilty until proved innocent or insane + probed_tags = getattr(example, 'probed_tags', False) + # If the example executed without raising any exceptions, # verify its output. if exception is None: if check(example.want, got, self.optionflags): + if probed_tags and probed_tags is not True: + example.warnings.append( + f"The tag '{unparse_optional_tags(probed_tags)}' " + f"may no longer be needed; these features are not present, " + f"but we ran the doctest anyway as requested by --probe, " + f"and it succeeded.") outcome = SUCCESS # The example raised an exception: check if it was expected. @@ -755,6 +776,12 @@ def compiler(example): # We expected an exception: see whether it matches. elif check(example.exc_msg, exc_msg, self.optionflags): + if probed_tags and probed_tags is not True: + example.warnings.append( + f"The tag '{unparse_optional_tags(example.probed_tags)}' " + f"may no longer be needed; these features are not present, " + f"but we ran the doctest anyway as requested by --probe, " + f"and it succeeded (raised the expected exception).") outcome = SUCCESS # Another chance if they didn't care about the detail. @@ -763,22 +790,32 @@ def compiler(example): m2 = re.match(r'(?:[^:]*\.)?([^:]*:)', exc_msg) if m1 and m2 and check(m1.group(1), m2.group(1), self.optionflags): + if probed_tags and probed_tags is not True: + example.warnings.append( + f"The tag '{unparse_optional_tags(example.probed_tags)}' " + f"may no longer be needed; these features are not present, " + f"but we ran the doctest anyway as requested by --probe, " + f"and it succeeded (raised an exception as expected).") outcome = SUCCESS check_duration = walltime(check_starttime) self.total_walltime += example.walltime + check_duration # Report the outcome. + if example.warnings: + for warning in example.warnings: + out(self._failure_header(test, example, f'Warning: {warning}')) if outcome is SUCCESS: if self.options.warn_long > 0 and example.walltime + check_duration > self.options.warn_long: self.report_overtime(out, test, example, got, check_duration=check_duration) elif example.warnings: - for warning in example.warnings: - out(self._failure_header(test, example, f'Warning: {warning}')) + pass elif not quiet: self.report_success(out, test, example, got, check_duration=check_duration) + elif probed_tags: + pass elif outcome is FAILURE: if not quiet: self.report_failure(out, test, example, got, test.globs) @@ -1097,7 +1134,8 @@ def compile_and_execute(self, example, compiler, globs): if isinstance(globs, RecordingDict): globs.start() example.sequence_number = len(self.history) - example.warnings = [] + if not hasattr(example, 'warnings'): + example.warnings = [] self.history.append(example) timer = Timer().start() try: @@ -1111,16 +1149,24 @@ def compile_and_execute(self, example, compiler, globs): for name in globs.got: setters_dict = self.setters.get(name) # setter_optional_tags -> setter if setters_dict: + was_set = False for setter_optional_tags, setter in setters_dict.items(): - if setter_optional_tags.issubset(example.optional_tags): + if setter_optional_tags.issubset(example.optional_tags): # was set in a less constrained doctest + was_set = True example.predecessors.append(setter) - if not example.predecessors: - f_setter_optional_tags = "; ".join("'" - + unparse_optional_tags(setter_optional_tags) - + "'" - for setter_optional_tags in setters_dict) - example.warnings.append(f"Variable '{name}' referenced here " - f"was set only in doctest marked {f_setter_optional_tags}") + if not was_set: + if example.probed_tags: + # Probing confusion. + # Do not issue the "was set only in doctest marked" warning; + # and also do not issue the "may no longer be needed" notice + example.probed_tags = True + else: + f_setter_optional_tags = "; ".join("'" + + unparse_optional_tags(setter_optional_tags) + + "'" + for setter_optional_tags in setters_dict) + example.warnings.append(f"Variable '{name}' referenced here " + f"was set only in doctest marked {f_setter_optional_tags}") for name in globs.set: self.setters[name][example.optional_tags] = example else: @@ -2134,7 +2180,7 @@ def run(self): TESTS:: - sage: run_doctests(sage.symbolic.units) # indirect doctest # optional - sage.symbolic + sage: run_doctests(sage.symbolic.units) # indirect doctest # needs sage.symbolic Running doctests with ID ... Doctesting 1 file. sage -t .../sage/symbolic/units.py diff --git a/src/sage/doctest/parsing.py b/src/sage/doctest/parsing.py index 579768d6a06..86d7aa71174 100644 --- a/src/sage/doctest/parsing.py +++ b/src/sage/doctest/parsing.py @@ -13,9 +13,19 @@ """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein +# Copyright (C) 2012-2018 David Roe +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013 Volker Braun +# 2013-2018 Jeroen Demeyer +# 2016-2021 Frédéric Chapoton +# 2017-2018 Erik M. Bray +# 2020 Marc Mezzarobba +# 2020-2023 Matthias Koeppe +# 2022 John H. Palmieri +# 2022 Sébastien Labbé +# 2023 Kwankyu Lee # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -23,13 +33,17 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import re +import collections.abc import doctest +import re + from collections import defaultdict -from sage.repl.preparse import preparse, strip_string_literals from functools import reduce -from .external import available_software +from sage.misc.cachefunc import cached_function +from sage.repl.preparse import preparse, strip_string_literals + +from .external import available_software, external_software _RIFtol = None @@ -80,111 +94,455 @@ def fake_RIFtol(*args): ansi_escape_sequence = re.compile(r'(\x1b[@-Z\\-~]|\x1b\[.*?[@-~]|\x9b.*?[@-~])') special_optional_regex = 'arb216|arb218|py2|long time|not implemented|not tested|known bug' -optional_regex = re.compile(fr'({special_optional_regex})|([^ a-z]\s*optional\s*[:-]*((\s|\w|[.])*))') -special_optional_regex = re.compile(special_optional_regex) +tag_with_explanation_regex = fr'((?:\w|[.])+)\s*(?:\((.*?)\))?' +optional_regex = re.compile(fr'(?P{special_optional_regex})\s*(?:\((?P.*?)\))?|' + fr'[^ a-z]\s*(optional|needs)(?:\s|[:-])*(?P(?:(?:{tag_with_explanation_regex})\s*)*)', + re.IGNORECASE) +special_optional_regex = re.compile(special_optional_regex, re.IGNORECASE) +tag_with_explanation_regex = re.compile(tag_with_explanation_regex, re.IGNORECASE) +nodoctest_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*nodoctest') +optionaltag_regex = re.compile(r'^(\w|[.])+$') +optionalfiledirective_regex = re.compile(r'\s*(#+|%+|r"+|"+|\.\.)\s*sage\.doctest: (.*)') -def parse_optional_tags(string): - """ - Return a set consisting of the optional tags from the following + +def parse_optional_tags(string, *, return_string_sans_tags=False): + r""" + Return a dictionary whose keys are optional tags from the following set that occur in a comment on the first line of the input string. - - 'long time' - - 'not implemented' - - 'not tested' - - 'known bug' - - 'py2' - - 'arb216' - - 'arb218' - - 'optional: PKG_NAME' -- the set will just contain 'PKG_NAME' + - ``'long time'`` + - ``'not implemented'`` + - ``'not tested'`` + - ``'known bug'`` + - ``'py2'`` + - ``'arb216'`` + - ``'arb218'`` + - ``'optional - FEATURE...'`` or ``'needs FEATURE...'`` -- + the dictionary will just have the key ``'FEATURE'`` + + The values, if non-``None``, are strings with optional explanations + for a tag, which may appear in parentheses after the tag in ``string``. + + INPUT: + + - ``string`` -- a string + + - ``return_string_sans_tags`` -- (boolean, default ``False``); whether to + additionally return ``string`` with the optional tags removed but other + comments kept and a boolean ``is_persistent`` EXAMPLES:: sage: from sage.doctest.parsing import parse_optional_tags sage: parse_optional_tags("sage: magma('2 + 2')# optional: magma") - {'magma'} + {'magma': None} sage: parse_optional_tags("sage: #optional -- mypkg") - {'mypkg'} + {'mypkg': None} sage: parse_optional_tags("sage: print(1) # parentheses are optional here") - set() + {} sage: parse_optional_tags("sage: print(1) # optional") - {''} + {} sage: sorted(list(parse_optional_tags("sage: #optional -- foo bar, baz"))) ['bar', 'foo'] sage: parse_optional_tags("sage: #optional -- foo.bar, baz") - {'foo.bar'} + {'foo.bar': None} + sage: parse_optional_tags("sage: #needs foo.bar, baz") + {'foo.bar': None} sage: sorted(list(parse_optional_tags(" sage: factor(10^(10^10) + 1) # LoNg TiME, NoT TeSTED; OptioNAL -- P4cka9e"))) ['long time', 'not tested', 'p4cka9e'] sage: parse_optional_tags(" sage: raise RuntimeError # known bug") - {'bug'} + {'bug': None} sage: sorted(list(parse_optional_tags(" sage: determine_meaning_of_life() # long time, not implemented"))) ['long time', 'not implemented'] We don't parse inside strings:: sage: parse_optional_tags(" sage: print(' # long time')") - set() + {} sage: parse_optional_tags(" sage: print(' # long time') # not tested") - {'not tested'} + {'not tested': None} UTF-8 works:: - sage: parse_optional_tags("'ěščřžýáíéďĎ'") - set() + sage: parse_optional_tags("'ěščřžýáíéďĎ'") + {} + + Tags with parenthesized explanations:: + + sage: parse_optional_tags(" sage: 1 + 1 # long time (1 year, 2 months??), optional - bliss (because)") + {'bliss': 'because', 'long time': '1 year, 2 months??'} + + With ``return_string_sans_tags=True``:: + + sage: parse_optional_tags("sage: print(1) # very important 1 # optional - foo", + ....: return_string_sans_tags=True) + ({'foo': None}, 'sage: print(1) # very important 1 ', False) + sage: parse_optional_tags("sage: print( # very important too # optional - foo\n....: 2)", + ....: return_string_sans_tags=True) + ({'foo': None}, 'sage: print( # very important too \n....: 2)', False) + sage: parse_optional_tags("sage: #this is persistent #needs scipy", + ....: return_string_sans_tags=True) + ({'scipy': None}, 'sage: #this is persistent ', True) + sage: parse_optional_tags("sage: #this is not #needs scipy\n....: import scipy", + ....: return_string_sans_tags=True) + ({'scipy': None}, 'sage: #this is not \n....: import scipy', False) + """ safe, literals, state = strip_string_literals(string) - first_line = safe.split('\n', 1)[0] - if '#' not in first_line: - return set() - comment = first_line[first_line.find('#') + 1:] - comment = comment[comment.index('(') + 1: comment.rindex(')')] - # strip_string_literals replaces comments - comment = "#" + (literals[comment]).lower() + split = safe.split('\n', 1) + if len(split) > 1: + first_line, rest = split + else: + first_line, rest = split[0], None + + sharp_index = first_line.find('#') + if sharp_index < 0: # no comment + if return_string_sans_tags: + return {}, string, False + else: + return {} + + first_line_sans_comments, comment = first_line[:sharp_index] % literals, first_line[sharp_index:] % literals + + if return_string_sans_tags: + # skip non-tag comments that precede the first tag comment + if m := optional_regex.search(comment): + sharp_index = comment[:m.start(0) + 1].rfind('#') + if sharp_index >= 0: + first_line = first_line_sans_comments + comment[:sharp_index] + comment = comment[sharp_index:] + else: + # no tag comment + return {}, string, False - tags = [] + tags = {} for m in optional_regex.finditer(comment): - cmd = m.group(1) - if cmd == 'known bug': - tags.append('bug') # so that such tests will be run by sage -t ... -only-optional=bug + cmd = m.group('cmd') + if cmd and cmd.lower() == 'known bug': + tags['bug'] = None # so that such tests will be run by sage -t ... -only-optional=bug elif cmd: - tags.append(cmd) + tags[cmd.lower()] = m.group('cmd_explanation') or None else: - tags.extend(m.group(3).split() or [""]) - return set(tags) + # optional/needs + tags.update({m.group(1).lower(): m.group(2) or None + for m in tag_with_explanation_regex.finditer(m.group('tags'))}) + + if return_string_sans_tags: + is_persistent = tags and first_line_sans_comments.strip() == 'sage:' and not rest # persistent (block-scoped) tag + return tags, (first_line + '\n' + rest%literals if rest is not None + else first_line), is_persistent + else: + return tags + + +def parse_file_optional_tags(lines): + r""" + Scan the first few lines for file-level doctest directives. + + INPUT: + + - ``lines`` -- iterable of pairs ``(lineno, line)``. + + OUTPUT: + + a dictionary whose keys are strings (tags); see :func:`parse_optional_tags` + + EXAMPLES:: + + sage: from sage.doctest.parsing import parse_file_optional_tags + sage: filename = tmp_filename(ext=".pyx") + sage: with open(filename, "r") as f: + ....: parse_file_optional_tags(enumerate(f)) + {} + sage: with open(filename, "w") as f: + ....: _ = f.write("# nodoctest") + sage: with open(filename, "r") as f: + ....: parse_file_optional_tags(enumerate(f)) + {'not tested': None} + sage: with open(filename, "w") as f: + ....: _ = f.write("# sage.doctest: " # broken in two source lines to avoid the pattern + ....: "optional - xyz") # of relint (multiline_doctest_comment) + sage: with open(filename, "r") as f: + ....: parse_file_optional_tags(enumerate(f)) + {'xyz': None} + """ + tags = {} + for line_count, line in lines: + if nodoctest_regex.match(line): + tags['not tested'] = None + if m := optionalfiledirective_regex.match(line): + file_tag_string = m.group(2) + tags.update(parse_optional_tags('#' + file_tag_string)) + if line_count >= 10: + break + return tags + + +@cached_function +def _standard_tags(): + r""" + Return the set of the names of all standard features. + + EXAMPLES:: + + sage: from sage.doctest.parsing import _standard_tags + sage: sorted(_standard_tags()) + [..., 'numpy', ..., 'sage.rings.finite_rings', ...] + """ + from sage.features.all import all_features + return frozenset(feature.name for feature in all_features() + if feature._spkg_type() == 'standard') + + +def _tag_group(tag): + r""" + Classify a doctest tag as belonging to one of 4 groups. + INPUT: + + - ``tag`` -- string + + OUTPUT: + + a string; one of ``'special'``, ``'optional'``, ``'standard'``, ``'sage'`` + + EXAMPLES:: -def unparse_optional_tags(tags): + sage: from sage.doctest.parsing import _tag_group + sage: _tag_group('scipy') + 'standard' + sage: _tag_group('sage.numerical.mip') + 'sage' + sage: _tag_group('bliss') + 'optional' + sage: _tag_group('not tested') + 'special' + """ + if tag.startswith('sage.'): + return 'sage' + elif tag in _standard_tags(): + return 'standard' + elif not special_optional_regex.fullmatch(tag): + return 'optional' + else: + return 'special' + + +def unparse_optional_tags(tags, prefix='# '): r""" Return a comment string that sets ``tags``. INPUT: - - ``tags`` -- iterable of tags, as output by :func:`parse_optional_tags` + - ``tags`` -- dict or iterable of tags, as output by :func:`parse_optional_tags` + + - ``prefix`` -- to be put before a nonempty string EXAMPLES:: sage: from sage.doctest.parsing import unparse_optional_tags - sage: unparse_optional_tags(set()) + sage: unparse_optional_tags({}) '' - sage: unparse_optional_tags({'magma'}) + sage: unparse_optional_tags({'magma': None}) '# optional - magma' - sage: unparse_optional_tags(['zipp', 'sage.rings.number_field', 'foo']) - '# optional - foo zipp sage.rings.number_field' - sage: unparse_optional_tags(['long time', 'not tested', 'p4cka9e']) - '# long time, not tested, optional - p4cka9e' + sage: unparse_optional_tags({'fictional_optional': None, + ....: 'sage.rings.number_field': None, + ....: 'scipy': 'just because', + ....: 'bliss': None}) + '# optional - bliss fictional_optional, needs scipy (just because) sage.rings.number_field' + sage: unparse_optional_tags(['long time', 'not tested', 'p4cka9e'], prefix='') + 'long time, not tested, optional - p4cka9e' """ - tags = set(tags) - special_tags = set(tag for tag in tags if special_optional_regex.fullmatch(tag)) - optional_tags = sorted(tags - special_tags, - key=lambda tag: (tag.startswith('sage.'), tag)) - tags = sorted(special_tags) - if optional_tags: - tags.append('optional - ' + " ".join(optional_tags)) + group = defaultdict(set) + if isinstance(tags, collections.abc.Mapping): + for tag, explanation in tags.items(): + if tag == 'bug': + tag = 'known bug' + group[_tag_group(tag)].add(f'{tag} ({explanation})' if explanation else tag) + else: + for tag in tags: + if tag == 'bug': + tag = 'known bug' + group[_tag_group(tag)].add(tag) + + tags = sorted(group.pop('special', [])) + if 'optional' in group: + tags.append('optional - ' + " ".join(sorted(group.pop('optional')))) + if 'standard' in group or 'sage' in group: + tags.append('needs ' + " ".join(sorted(group.pop('standard', [])) + + sorted(group.pop('sage', [])))) + assert not group if tags: - return '# ' + ', '.join(tags) + return prefix + ', '.join(tags) return '' +optional_tag_columns = [48, 56, 64, 72, 80, 84] +standard_tag_columns = [88, 100, 120, 160] + + +def update_optional_tags(line, tags=None, *, add_tags=None, remove_tags=None, force_rewrite=False): + r""" + Return the doctest ``line`` with tags changed. + + EXAMPLES:: + + sage: from sage.doctest.parsing import update_optional_tags, optional_tag_columns, standard_tag_columns + sage: ruler = '' + sage: for column in optional_tag_columns: + ....: ruler += ' ' * (column - len(ruler)) + 'V' + sage: for column in standard_tag_columns: + ....: ruler += ' ' * (column - len(ruler)) + 'v' + sage: def print_with_ruler(lines): + ....: print('|' + ruler) + ....: for line in lines: + ....: print('|' + line) + sage: print_with_ruler([ # the tags are obscured in the source file to avoid relint warnings + ....: update_optional_tags(' sage: something() # opt' 'ional - latte_int', + ....: remove_tags=['latte_int', 'wasnt_even_there']), + ....: update_optional_tags(' sage: nothing_to_be_seen_here()', + ....: tags=['scipy', 'long time']), + ....: update_optional_tags(' sage: nothing_to_be_seen_here(honestly=True)', + ....: add_tags=['scipy', 'long time']), + ....: update_optional_tags(' sage: nothing_to_be_seen_here(honestly=True, very=True)', + ....: add_tags=['scipy', 'long time']), + ....: update_optional_tags(' sage: no_there_is_absolutely_nothing_to_be_seen_here_i_am_serious()#opt' 'ional:bliss', + ....: add_tags=['scipy', 'long time']), + ....: update_optional_tags(' sage: ntbsh() # abbrv for above#opt' 'ional:bliss', + ....: add_tags={'scipy': None, 'long time': '30s on the highest setting'}), + ....: update_optional_tags(' sage: no_there_is_absolutely_nothing_to_be_seen_here_i_am_serious() # really, you can trust me here', + ....: add_tags=['scipy']), + ....: ]) + | V V V V V V v v v v + | sage: something() + | sage: nothing_to_be_seen_here() # long time # needs scipy + | sage: nothing_to_be_seen_here(honestly=True) # long time # needs scipy + | sage: nothing_to_be_seen_here(honestly=True, very=True) # long time # needs scipy + | sage: no_there_is_absolutely_nothing_to_be_seen_here_i_am_serious() # long time, optional - bliss, needs scipy + | sage: ntbsh() # abbrv for above # long time (30s on the highest setting), optional - bliss, needs scipy + | sage: no_there_is_absolutely_nothing_to_be_seen_here_i_am_serious() # really, you can trust me here # needs scipy + + When no tags are changed, by default, the unchanged input is returned. + We can force a rewrite; unconditionally or whenever standard tags are involved. + But even when forced, if comments are already aligned at one of the standard alignment columns, + this alignment is kept even if we would normally realign farther to the left:: + + sage: print_with_ruler([ + ....: update_optional_tags(' sage: unforced() # opt' 'ional - latte_int'), + ....: update_optional_tags(' sage: unforced() # opt' 'ional - latte_int', + ....: add_tags=['latte_int']), + ....: update_optional_tags(' sage: forced()#opt' 'ional- latte_int', + ....: force_rewrite=True), + ....: update_optional_tags(' sage: forced() # opt' 'ional - scipy', + ....: force_rewrite='standard'), + ....: update_optional_tags(' sage: aligned_with_below() # opt' 'ional - 4ti2', + ....: force_rewrite=True), + ....: update_optional_tags(' sage: aligned_with_above() # opt' 'ional - 4ti2', + ....: force_rewrite=True), + ....: update_optional_tags(' sage: also_already_aligned() # ne' 'eds scipy', + ....: force_rewrite='standard'), + ....: update_optional_tags(' sage: two_columns_first_preserved() # lo' 'ng time # ne' 'eds scipy', + ....: force_rewrite='standard'), + ....: update_optional_tags(' sage: two_columns_first_preserved() # lo' 'ng time # ne' 'eds scipy', + ....: force_rewrite='standard'), + ....: ]) + | V V V V V V v v v v + | sage: unforced() # optional - latte_int + | sage: unforced() # optional - latte_int + | sage: forced() # optional - latte_int + | sage: forced() # needs scipy + | sage: aligned_with_below() # optional - 4ti2 + | sage: aligned_with_above() # optional - 4ti2 + | sage: also_already_aligned() # needs scipy + | sage: two_columns_first_preserved() # long time # needs scipy + | sage: two_columns_first_preserved() # long time # needs scipy + + Rewriting a persistent (block-scoped) tag:: + + sage: print_with_ruler([ + ....: update_optional_tags(' sage: #opt' 'ional:magma sage.symbolic', + ....: force_rewrite=True), + ....: ]) + | V V V V V V v v v v + | sage: # optional - magma, needs sage.symbolic + """ + if not (m := re.match('( *sage: *)(.*)', line)): + raise ValueError(f'line must start with a sage: prompt, got: {line}') + + current_tags, line_sans_tags, is_persistent = parse_optional_tags(line.rstrip(), return_string_sans_tags=True) + + if isinstance(tags, collections.abc.Mapping): + new_tags = dict(tags) + elif tags is not None: + new_tags = {tag: None for tag in tags} + else: + new_tags = dict(current_tags) + + if add_tags is not None: + if isinstance(add_tags, collections.abc.Mapping): + new_tags.update(add_tags) + else: + new_tags.update({tag: None for tag in add_tags}) + + if remove_tags is not None: + for tag in remove_tags: + new_tags.pop(tag, None) + + if not force_rewrite and new_tags == current_tags: + return line + + if not new_tags: + return line_sans_tags.rstrip() + + if (force_rewrite == 'standard' + and new_tags == current_tags + and not any(_tag_group(tag) in ['standard', 'sage'] + for tag in new_tags)): + return line + + if is_persistent: + line = line_sans_tags.rstrip() + ' ' + else: + group = defaultdict(set) + for tag in new_tags: + group[_tag_group(tag)].add(tag) + tag_columns = (optional_tag_columns if group['optional'] or group['special'] + else standard_tag_columns) + + if len(line_sans_tags) in tag_columns and line_sans_tags[-2:] == ' ': + # keep alignment + line = line_sans_tags + pass + else: + # realign + line = line_sans_tags.rstrip() + for column in tag_columns: + if len(line) <= column - 2: + line += ' ' * (column - 2 - len(line)) + break + line += ' ' + + if (group['optional'] or group['special']) and (group['standard'] or group['sage']): + # Try if two-column mode works better + first_part = unparse_optional_tags({tag: explanation + for tag, explanation in new_tags.items() + if (tag in group['optional'] + or tag in group['special'])}) + column = standard_tag_columns[0] + if len(line + first_part) + 8 <= column: + line += first_part + line += ' ' * (column - len(line)) + line += unparse_optional_tags({tag: explanation + for tag, explanation in new_tags.items() + if not (tag in group['optional'] + or tag in group['special'])}) + return line.rstrip() + + line += unparse_optional_tags(new_tags) + return line + + def parse_tolerance(source, want): r""" Return a version of ``want`` marked up with the tolerance tags @@ -476,13 +834,15 @@ class SageDocTestParser(doctest.DocTestParser): A version of the standard doctest parser which handles Sage's custom options and tolerances in floating point arithmetic. """ - def __init__(self, optional_tags=(), long=False): + def __init__(self, optional_tags=(), long=False, *, probed_tags=(), file_optional_tags=()): r""" INPUT: - ``optional_tags`` -- a list or tuple of strings. - ``long`` -- boolean, whether to run doctests marked as taking a long time. + - ``probed_tags`` -- a list or tuple of strings. + - ``file_optional_tags`` -- an iterable of strings. EXAMPLES:: @@ -511,6 +871,8 @@ def __init__(self, optional_tags=(), long=False): self.optional_tags.remove('sage') else: self.optional_only = True + self.probed_tags = probed_tags + self.file_optional_tags = set(file_optional_tags) def __eq__(self, other): """ @@ -556,7 +918,7 @@ def parse(self, string, *args): - A list consisting of strings and :class:`doctest.Example` instances. There will be at least one string between - successive examples (exactly one unless or long or optional + successive examples (exactly one unless long or optional tests are removed), and it will begin and end with a string. EXAMPLES:: @@ -605,7 +967,7 @@ def parse(self, string, *args): of the current line should be joined to the next line. This feature allows for breaking large integers over multiple lines but is not standard for Python doctesting. It's not - guaranteed to persist, but works in Sage 5.5:: + guaranteed to persist:: sage: n = 1234\ ....: 5678 @@ -621,6 +983,23 @@ def parse(self, string, *args): sage: print(m) 87654321 + Optional tags at the start of an example block persist to the end of + the block (delimited by a blank line):: + + sage: # long time, needs sage.rings.number_field + sage: QQbar(I)^10000 + 1 + sage: QQbar(I)^10000 # not tested + I + + sage: # needs sage.rings.finite_rings + sage: GF(7) + Finite Field of size 7 + sage: GF(10) + Traceback (most recent call last): + ... + ValueError: the order of a finite field must be a prime power + Test that :trac:`26575` is resolved:: sage: example3 = 'sage: Zp(5,4,print_mode="digits")(5)\n...00010' @@ -630,6 +1009,68 @@ def parse(self, string, *args): 'Zp(5,4,print_mode="digits")(5)\n' sage: dte.want '...00010\n' + + Style warnings:: + + sage: def parse(test_string): + ....: return [x if isinstance(x, str) + ....: else (getattr(x, 'warnings', None), x.sage_source, x.source) + ....: for x in DTP.parse(test_string)] + + sage: parse('sage: 1 # optional guava mango\nsage: 2 # optional guava\nsage: 3 # optional guava\nsage: 4 # optional guava\nsage: 5 # optional guava\n\nsage: 11 # optional guava') + ['', + (["Consider using a block-scoped tag by inserting the line 'sage: # optional - guava' just before this line to avoid repeating the tag 5 times"], + '1 # optional guava mango\n', + 'None # virtual doctest'), + '', + (None, '2 # optional guava\n', 'Integer(2) # optional guava\n'), + '', + (None, '3 # optional guava\n', 'Integer(3) # optional guava\n'), + '', + (None, '4 # optional guava\n', 'Integer(4) # optional guava\n'), + '', + (None, '5 # optional guava\n', 'Integer(5) # optional guava\n'), + '\n', + (None, '11 # optional guava\n', 'Integer(11) # optional guava\n'), + ''] + + sage: parse('sage: 1 # optional guava\nsage: 2 # optional guava mango\nsage: 3 # optional guava\nsage: 4 # optional guava\nsage: 5 # optional guava\n') + ['', + (["Consider using a block-scoped tag by inserting the line 'sage: # optional - guava' just before this line to avoid repeating the tag 5 times"], + '1 # optional guava\n', + 'Integer(1) # optional guava\n'), + '', + '', + (None, '3 # optional guava\n', 'Integer(3) # optional guava\n'), + '', + (None, '4 # optional guava\n', 'Integer(4) # optional guava\n'), + '', + (None, '5 # optional guava\n', 'Integer(5) # optional guava\n'), + ''] + + sage: parse('sage: # optional mango\nsage: 1 # optional guava\nsage: 2 # optional guava mango\nsage: 3 # optional guava\nsage: 4 # optional guava\n sage: 5 # optional guava\n') # optional - guava mango + ['', + (["Consider updating this block-scoped tag to 'sage: # optional - guava mango' to avoid repeating the tag 5 times"], + '# optional mango\n', + 'None # virtual doctest'), + '', + '', + '', + '', + '', + ''] + + sage: parse('::\n\n sage: 1 # optional guava\n sage: 2 # optional guava mango\n sage: 3 # optional guava\n\n::\n\n sage: 4 # optional guava\n sage: 5 # optional guava\n') + ['::\n\n', + (None, '1 # optional guava\n', 'Integer(1) # optional guava\n'), + '', + '', + (None, '3 # optional guava\n', 'Integer(3) # optional guava\n'), + '\n::\n\n', + (None, '4 # optional guava\n', 'Integer(4) # optional guava\n'), + '', + (None, '5 # optional guava\n', 'Integer(5) # optional guava\n'), + ''] """ # Regular expressions find_sage_prompt = re.compile(r"^(\s*)sage: ", re.M) @@ -664,10 +1105,77 @@ def parse(self, string, *args): string = find_sage_continuation.sub(r"\1...", string) res = doctest.DocTestParser.parse(self, string, *args) filtered = [] + persistent_optional_tags = self.file_optional_tags + persistent_optional_tag_setter = None + persistent_optional_tag_setter_index = None + first_example_in_block = None + first_example_in_block_index = None + tag_count_within_block = defaultdict(lambda: 0) + + def update_tag_counts(optional_tags): + for tag in optional_tags: + if tag not in persistent_optional_tags: + tag_count_within_block[tag] += 1 + tag_count_within_block[''] += 1 + + def check_and_clear_tag_counts(): + if (num_examples := tag_count_within_block['']) >= 4: + if overused_tags := set(tag for tag, count in tag_count_within_block.items() + if tag and count >= num_examples): + overused_tags.update(persistent_optional_tags) + overused_tags.difference_update(self.file_optional_tags) + suggested = unparse_optional_tags(overused_tags, prefix='sage: # ') + + if persistent_optional_tag_setter: + warning_example = persistent_optional_tag_setter + index = persistent_optional_tag_setter_index + warning = (f"Consider updating this block-scoped tag to '{suggested}' " + f"to avoid repeating the tag {num_examples} times") + else: + warning_example = first_example_in_block + index = first_example_in_block_index + warning = (f"Consider using a block-scoped tag by " + f"inserting the line '{suggested}' just before this line " + f"to avoid repeating the tag {num_examples} times") + + if not (index < len(filtered) and filtered[index] == warning_example): + # The example to which we want to attach our warning is + # not in ``filtered``. It is either the persistent tag line, + # or the first example of the block and not run because of unmet tags, + # or just a comment. Either way, we transform this example + # to a virtual example and attach the warning to it. + warning_example.sage_source = warning_example.source + if warning_example.sage_source.startswith("sage: "): + warning_example.sage_source = warning_example.source[6:] + warning_example.source = 'None # virtual doctest' + warning_example.want = '' + filtered.insert(index, warning_example) + + if not hasattr(warning_example, 'warnings'): + warning_example.warnings = [] + warning_example.warnings.append(warning) + tag_count_within_block.clear() + for item in res: if isinstance(item, doctest.Example): - optional_tags = parse_optional_tags(item.source) + optional_tags, source_sans_tags, is_persistent = parse_optional_tags(item.source, return_string_sans_tags=True) + optional_tags = set(optional_tags) + if is_persistent: + check_and_clear_tag_counts() + persistent_optional_tags = optional_tags + persistent_optional_tags.update(self.file_optional_tags) + persistent_optional_tag_setter = first_example_in_block = item + persistent_optional_tag_setter_index = len(filtered) + first_example_in_block_index = None + continue + + if not first_example_in_block: + first_example_in_block = item + first_example_in_block_index = len(filtered) + update_tag_counts(optional_tags) + optional_tags.update(persistent_optional_tags) item.optional_tags = frozenset(optional_tags) + item.probed_tags = set() if optional_tags: for tag in optional_tags: self.optionals[tag] += 1 @@ -682,13 +1190,28 @@ def parse(self, string, *args): continue if self.optional_tags is not True: - extra = optional_tags - self.optional_tags # set difference + extra = set(tag + for tag in optional_tags + if (tag not in self.optional_tags + and tag not in available_software)) if extra: - if not available_software.issuperset(extra): + if any(tag in external_software for tag in extra): + # never probe "external" software + continue + if all(tag in persistent_optional_tags for tag in extra): + # don't probe if test is only conditional + # on file-level or block-level tags + continue + if self.probed_tags is True: + item.probed_tags = extra + elif all(tag in self.probed_tags for tag in extra): + item.probed_tags = extra + else: continue elif self.optional_only: self.optionals['sage'] += 1 continue + if replace_ellipsis: item.want = item.want.replace(ellipsis_tag, "...") if item.exc_msg is not None: @@ -699,7 +1222,16 @@ def parse(self, string, *args): if item.sage_source.lstrip().startswith('#'): continue item.source = preparse(item.sage_source) + else: + if '\n' in item: + check_and_clear_tag_counts() + persistent_optional_tags = self.file_optional_tags + persistent_optional_tag_setter = first_example_in_block = None + persistent_optional_tag_setter_index = first_example_in_block_index = None filtered.append(item) + + check_and_clear_tag_counts() + return filtered @@ -783,11 +1315,11 @@ def add_tolerance(self, wantval, want): sage: want_tol = MarkedOutput().update(tol=0.0001) sage: want_abs = MarkedOutput().update(abs_tol=0.0001) sage: want_rel = MarkedOutput().update(rel_tol=0.0001) - sage: OC.add_tolerance(RIF(pi.n(64)), want_tol).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_tol).endpoints() # needs sage.symbolic (3.14127849432443, 3.14190681285516) - sage: OC.add_tolerance(RIF(pi.n(64)), want_abs).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_abs).endpoints() # needs sage.symbolic (3.14149265358979, 3.14169265358980) - sage: OC.add_tolerance(RIF(pi.n(64)), want_rel).endpoints() + sage: OC.add_tolerance(RIF(pi.n(64)), want_rel).endpoints() # needs sage.symbolic (3.14127849432443, 3.14190681285516) sage: OC.add_tolerance(RIF(1e1000), want_tol) 1.000?e1000 @@ -897,7 +1429,7 @@ def check_output(self, want, got, optionflags): More explicit tolerance checks:: - sage: _ = x # rel tol 1e10 + sage: _ = x # rel tol 1e10 # needs sage.symbolic sage: raise RuntimeError # rel tol 1e10 Traceback (most recent call last): ... diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py index 84ef0cf45cc..c2b4537d596 100644 --- a/src/sage/doctest/reporting.py +++ b/src/sage/doctest/reporting.py @@ -23,10 +23,16 @@ """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein -# Copyright (C) 2013 Jeroen Demeyer +# Copyright (C) 2012-2013 David Roe +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013 Jeroen Demeyer +# 2013-2017 Volker Braun +# 2018 Julian Rüth +# 2018-2021 Sébastien Labbé +# 2020 Samuel Lelièvre +# 2022 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/doctest/sources.py b/src/sage/doctest/sources.py index 15115a245b3..466ccf12181 100644 --- a/src/sage/doctest/sources.py +++ b/src/sage/doctest/sources.py @@ -10,9 +10,17 @@ """ # **************************************************************************** -# Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein +# Copyright (C) 2012-2013 David Roe +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 R. Andrew Ohana +# 2013-2017 Jeroen Demeyer +# 2013-2019 John H. Palmieri +# 2014 Volker Braun +# 2014-2022 Frédéric Chapoton +# 2017 Erik M. Bray +# 2021 Sébastien Labbé +# 2021-2023 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # @@ -225,9 +233,30 @@ def _process_doc(self, doctests, doc, namespace, start): sigon = doctest.Example(sig_on_count_doc_doctest, "0\n", lineno=docstring.count("\n")) sigon.sage_source = sig_on_count_doc_doctest sigon.optional_tags = frozenset() + sigon.probed_tags = frozenset() dt.examples.append(sigon) doctests.append(dt) + @lazy_attribute + def file_optional_tags(self): + r""" + Return the set of tags that should apply to all doctests in this source. + + This default implementation just returns the empty set. + + EXAMPLES:: + + sage: from sage.doctest.control import DocTestDefaults + sage: from sage.doctest.sources import StringDocTestSource, PythonSource + sage: from sage.structure.dynamic_class import dynamic_class + sage: s = "'''\n sage: 2 + 2\n 4\n'''" + sage: PythonStringSource = dynamic_class('PythonStringSource', (StringDocTestSource, PythonSource)) + sage: PSS = PythonStringSource('', s, DocTestDefaults(), 'runtime') + sage: PSS.file_optional_tags + set() + """ + return set() + def _create_doctests(self, namespace, tab_okay=None): """ Creates a list of doctests defined in this source. @@ -263,7 +292,7 @@ def _create_doctests(self, namespace, tab_okay=None): sage: FDS.qualified_name = NestedName('sage.doctest.sources') sage: doctests, extras = FDS._create_doctests({}) sage: len(doctests) - 41 + 43 sage: extras['tab'] False sage: extras['line_number'] @@ -274,7 +303,9 @@ def _create_doctests(self, namespace, tab_okay=None): self._init() self.line_shift = 0 self.parser = SageDocTestParser(self.options.optional, - self.options.long) + self.options.long, + probed_tags=self.options.probe, + file_optional_tags=self.file_optional_tags) self.linking = False doctests = [] in_docstring = False @@ -686,6 +717,25 @@ def in_lib(self): return (self.options.force_lib or is_package_or_sage_namespace_package_dir(os.path.dirname(self.path))) + @lazy_attribute + def file_optional_tags(self): + """ + Return the set of tags that should apply to all doctests in this source. + + EXAMPLES:: + + sage: from sage.doctest.control import DocTestDefaults + sage: from sage.doctest.sources import FileDocTestSource + sage: from sage.env import SAGE_SRC + sage: import os + sage: filename = os.path.join(SAGE_SRC, 'sage', 'repl', 'user_globals.py') + sage: FDS = FileDocTestSource(filename, DocTestDefaults()) + sage: FDS.file_optional_tags + {'sage.modules': None} + """ + from .parsing import parse_file_optional_tags + return parse_file_optional_tags(self) + def create_doctests(self, namespace): r""" Return a list of doctests for this file. @@ -710,16 +760,16 @@ def create_doctests(self, namespace): sage: FDS = FileDocTestSource(filename,DocTestDefaults()) sage: doctests, extras = FDS.create_doctests(globals()) sage: len(doctests) - 41 + 43 sage: extras['tab'] False We give a self referential example:: - sage: doctests[18].name + sage: doctests[20].name 'sage.doctest.sources.FileDocTestSource.create_doctests' - sage: doctests[18].examples[10].source - 'doctests[Integer(18)].examples[Integer(10)].source\n' + sage: doctests[20].examples[10].source + 'doctests[Integer(20)].examples[Integer(10)].source\n' TESTS: @@ -728,11 +778,12 @@ def create_doctests(self, namespace): sage: import sys sage: bitness = '64' if sys.maxsize > (1 << 32) else '32' - sage: gp.get_precision() == 38 + sage: gp.get_precision() == 38 # needs sage.libs.pari False # 32-bit True # 64-bit - sage: ex = doctests[18].examples[13] - sage: (bitness == '64' and ex.want == 'True \n') or (bitness == '32' and ex.want == 'False \n') + sage: ex = doctests[20].examples[13] + sage: ((bitness == '64' and ex.want == 'True \n') # needs sage.libs.pari + ....: or (bitness == '32' and ex.want == 'False \n')) True We check that lines starting with a # aren't doctested:: @@ -758,9 +809,11 @@ def create_doctests(self, namespace): def _test_enough_doctests(self, check_extras=True, verbose=True): r""" This function checks to see that the doctests are not getting - unexpectedly skipped. It uses a different (and simpler) code - path than the doctest creation functions, so there are a few - files in Sage that it counts incorrectly. + unexpectedly skipped. + + It uses a different (and simpler) code path than the doctest + creation functions. In particular, it does not understand + file-level and block-level # optional / needs tags. INPUT: @@ -773,13 +826,14 @@ def _test_enough_doctests(self, check_extras=True, verbose=True): TESTS:: + sage: # not tested (because the output will change when source files are changed) sage: from sage.doctest.control import DocTestDefaults sage: from sage.doctest.sources import FileDocTestSource sage: from sage.env import SAGE_SRC sage: cwd = os.getcwd() sage: os.chdir(SAGE_SRC) sage: import itertools - sage: for path, dirs, files in itertools.chain(os.walk('sage'), os.walk('doc')): # long time + sage: for path, dirs, files in itertools.chain(os.walk('sage'), os.walk('doc')): ....: path = os.path.relpath(path) ....: dirs.sort(); files.sort() ....: for F in files: diff --git a/src/sage/doctest/test.py b/src/sage/doctest/test.py index 60075a53578..aa64974fd72 100644 --- a/src/sage/doctest/test.py +++ b/src/sage/doctest/test.py @@ -253,11 +253,12 @@ is still alive, but it should be killed automatically after the ``die_timeout`` given above (10 seconds):: - sage: pid = int(open(F).read()) # long time - sage: time.sleep(2) # long time - sage: os.kill(pid, signal.SIGQUIT) # long time; 2 seconds passed => still alive - sage: time.sleep(8) # long time - sage: os.kill(pid, signal.SIGQUIT) # long time; 10 seconds passed => dead # random + sage: # long time + sage: pid = int(open(F).read()) + sage: time.sleep(2) + sage: os.kill(pid, signal.SIGQUIT) # 2 seconds passed => still alive + sage: time.sleep(8) + sage: os.kill(pid, signal.SIGQUIT) # 10 seconds passed => dead # random Traceback (most recent call last): ... ProcessLookupError: ... @@ -464,13 +465,12 @@ Running doctests ... Doctesting 1 file. sage -t --warn-long 0.0 --random-seed=0 show_skipped.rst - 1 unlabeled test not run 2 tests not run due to known bugs 1 gap test not run 1 long test not run 1 not tested test not run 0 tests not run because we ran out of time - [1 test, ... s] + [2 tests, ... s] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -484,11 +484,10 @@ Running doctests ... Doctesting 1 file. sage -t --long --warn-long 0.0 --random-seed=0 show_skipped.rst - 1 unlabeled test not run 2 tests not run due to known bugs 1 not tested test not run 0 tests not run because we ran out of time - [3 tests, ... s] + [4 tests, ... s] ---------------------------------------------------------------------- All tests passed! ---------------------------------------------------------------------- @@ -500,10 +499,9 @@ Running doctests ... Doctesting 1 file. sage -t --long --warn-long 0.0 --random-seed=0 show_skipped.rst - 1 unlabeled test not run 2 tests not run due to known bugs 1 not tested test not run - 1 sage test not run + 2 sage tests not run 0 tests not run because we ran out of time [2 tests, ... s] ---------------------------------------------------------------------- diff --git a/src/sage/doctest/util.py b/src/sage/doctest/util.py index 029611497a3..7446373eae0 100644 --- a/src/sage/doctest/util.py +++ b/src/sage/doctest/util.py @@ -10,8 +10,11 @@ # **************************************************************************** # Copyright (C) 2012 David Roe -# Robert Bradshaw -# William Stein +# 2012 Robert Bradshaw +# 2012 William Stein +# 2013 Jeroen Demeyer +# 2014 Volker Braun +# 2017 Frédéric Chapoton # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py index 2fdde406202..97cc1c1df3e 100644 --- a/src/sage/dynamics/arithmetic_dynamics/projective_ds.py +++ b/src/sage/dynamics/arithmetic_dynamics/projective_ds.py @@ -6008,7 +6008,7 @@ def reduced_form(self, **kwds): sage: f = DynamicalSystem_projective([x^3 + x*y^2, y^3]) sage: m = matrix(QQ, 2, 2, [-201221, -1, 1, 0]) sage: f = f.conjugate(m) - sage: f.reduced_form(prec=50, smallest_coeffs=False) # needs 2 periodic + sage: f.reduced_form(prec=50, smallest_coeffs=False) # this needs 2 periodic Traceback (most recent call last): ... ValueError: accuracy of Newton's root not within tolerance(0.000066... > 1e-06), @@ -6026,7 +6026,7 @@ def reduced_form(self, **kwds): :: sage: PS. = ProjectiveSpace(ZZ, 1) - sage: f = DynamicalSystem_projective([x^2 + x*y, y^2]) # needs 3 periodic + sage: f = DynamicalSystem_projective([x^2 + x*y, y^2]) # this needs 3 periodic sage: m = matrix(QQ, 2, 2, [-221, -1, 1, 0]) sage: f = f.conjugate(m) sage: f.reduced_form(prec=200, smallest_coeffs=False) diff --git a/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx b/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx index 987308e5cb1..62788943b09 100644 --- a/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +++ b/src/sage/dynamics/complex_dynamics/mandel_julia_helper.pyx @@ -680,6 +680,15 @@ cpdef polynomial_mandelbrot(f, parameter=None, double x_center=0, sage: f = z^4 - z + c sage: polynomial_mandelbrot(f, pixel_count=100) 100x100px 24-bit RGB image + + :: + + sage: from sage.dynamics.complex_dynamics.mandel_julia_helper import polynomial_mandelbrot + sage: B. = CC[] + sage: R. = B[] + sage: f = z^2*(z-c) + c + sage: polynomial_mandelbrot(f, pixel_count=100) + 100x100px 24-bit RGB image """ cdef: @@ -763,7 +772,7 @@ cpdef polynomial_mandelbrot(f, parameter=None, double x_center=0, df = f.derivative(z).univariate_polynomial() critical_pts = df.roots(multiplicities=False) constant_c = True - except PariError: + except (PariError, TypeError): constant_c = False # If c is in the constant term of the polynomial, then the critical points diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index 5deb0085a63..89a7b184a4c 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -232,7 +232,7 @@ def require(self): EXAMPLES:: sage: from sage.features.gap import GapPackage - sage: GapPackage("ve1EeThu").require() + sage: GapPackage("ve1EeThu").require() # needs sage.libs.gap Traceback (most recent call last): ... FeatureNotPresentError: gap_package_ve1EeThu is not available. @@ -379,7 +379,7 @@ def hide(self): sage: from sage.features.graph_generators import Benzene sage: Benzene().hide() - sage: len(list(graphs.fusenes(2))) + sage: len(list(graphs.fusenes(2))) # needs sage.graphs Traceback (most recent call last): ... FeatureNotPresentError: benzene is not available. @@ -387,7 +387,7 @@ def hide(self): Use method `unhide` to make it available again. sage: Benzene().unhide() - sage: len(list(graphs.fusenes(2))) # optional benzene + sage: len(list(graphs.fusenes(2))) # optional - benzene, needs sage.graphs 1 """ self._hidden = True @@ -405,7 +405,7 @@ def unhide(self): sage: from sage.features.gap import GapPackage sage: Polycyclic = GapPackage("polycyclic", spkg="gap_packages") sage: Polycyclic.hide() - sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) + sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap Traceback (most recent call last): ... FeatureNotPresentError: gap_package_polycyclic is not available. @@ -413,7 +413,7 @@ def unhide(self): Use method `unhide` to make it available again. sage: Polycyclic.unhide() - sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) + sage: libgap(AbelianGroup(3, [0,3,4], names="abc")) # needs sage.libs.gap Pcp-group with orders [ 0, 3, 4 ] """ self._hidden = False @@ -452,7 +452,7 @@ def __str__(self): EXAMPLES:: sage: from sage.features.gap import GapPackage - sage: GapPackage("gapZuHoh8Uu").require() # indirect doctest + sage: GapPackage("gapZuHoh8Uu").require() # indirect doctest # needs sage.libs.gap Traceback (most recent call last): ... FeatureNotPresentError: gap_package_gapZuHoh8Uu is not available. @@ -485,7 +485,7 @@ class FeatureTestResult(): Explanatory messages might be available as ``reason`` and ``resolution``:: - sage: presence.reason + sage: presence.reason # needs sage.libs.gap '`TestPackageAvailability("NOT_A_PACKAGE")` evaluated to `fail` in GAP.' sage: bool(presence.resolution) False @@ -870,8 +870,10 @@ class CythonFeature(Feature): ....: ....: assert fabs(-1) == 1 ....: ''' - sage: fabs = CythonFeature("fabs", test_code=fabs_test_code, spkg="gcc", url="https://gnu.org", type="standard") - sage: fabs.is_present() + sage: fabs = CythonFeature("fabs", test_code=fabs_test_code, # needs sage.misc.cython + ....: spkg="gcc", url="https://gnu.org", + ....: type="standard") + sage: fabs.is_present() # needs sage.misc.cython FeatureTestResult('fabs', True) Test various failures:: @@ -922,7 +924,7 @@ def _is_present(self): sage: from sage.features import CythonFeature sage: empty = CythonFeature("empty", test_code="") - sage: empty.is_present() + sage: empty.is_present() # needs sage.misc.cython FeatureTestResult('empty', True) """ from sage.misc.temporary_file import tmp_filename @@ -935,6 +937,9 @@ def _is_present(self): pyx.write(self.test_code) try: from sage.misc.cython import cython_import + except ImportError: + return FeatureTestResult(self, False, reason="sage.misc.cython is not available") + try: cython_import(pyx.name, verbose=-1) except CCompilerError: return FeatureTestResult(self, False, reason="Failed to compile test code.") diff --git a/src/sage/features/all.py b/src/sage/features/all.py index 19eabe60126..599bc575dd7 100644 --- a/src/sage/features/all.py +++ b/src/sage/features/all.py @@ -3,7 +3,7 @@ """ # ***************************************************************************** -# Copyright (C) 2021 Matthias Koeppe +# Copyright (C) 2021-2023 Matthias Koeppe # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -11,6 +11,9 @@ # https://www.gnu.org/licenses/ # ***************************************************************************** +import itertools + + def all_features(): r""" Return an iterable of all features. @@ -34,3 +37,89 @@ def all_features(): else: if af != all_features: yield from af() + + +def module_feature(module_name): + r""" + Find a top-level :class:`Feature` that provides the Python module of the given ``module_name``. + + Only features known to :func:`all_features` are considered. + + INPUT: + + - ``module_name`` -- string + + OUTPUT: a :class:`Feature` or ``None``. + + EXAMPLES:: + + sage: from sage.features.all import module_feature + sage: module_feature('sage.combinat.tableau') # needs sage.combinat + Feature('sage.combinat') + sage: module_feature('sage.combinat.posets.poset') # needs sage.graphs + Feature('sage.graphs') + sage: module_feature('sage.schemes.toric.variety') # needs sage.geometry.polyhedron + Feature('sage.geometry.polyhedron') + sage: module_feature('scipy') # needs scipy + Feature('scipy') + sage: print(module_feature('sage.structure.element')) + None + sage: print(module_feature('sage.does_not_exist')) + None + """ + longest_prefix = '' + longest_prefix_feature = None + for feature in all_features(): + for joined in itertools.chain([feature], feature.joined_features()): + if joined.name == module_name: + return feature + if (joined.name + '.').startswith(longest_prefix): + if (module_name + '.').startswith(joined.name + '.'): + longest_prefix = feature.name + '.' + longest_prefix_feature = feature + return longest_prefix_feature + + +def name_feature(name, toplevel=None): + r""" + Find a top-level :class:`Feature` that provides the top-level ``name``. + + Only features known to :func:`all_features` are considered. + + INPUT: + + - ``name`` -- string + + - ``toplevel`` -- a module or other namespace + + OUTPUT: a :class:`Feature` or ``None``. + + EXAMPLES:: + + sage: from sage.features.all import name_feature + sage: name_feature('QuadraticField') # needs sage.rings.number_field + Feature('sage.rings.number_field') + sage: name_feature('line') # needs sage.plot + Feature('sage.plot') + sage: print(name_feature('ZZ')) + None + sage: print(name_feature('does_not_exist')) + None + """ + if toplevel is None: + try: + import sage.all as toplevel + except ImportError: + return None + try: + obj = getattr(toplevel, name) + except AttributeError: + return None + + from sage.misc.dev_tools import find_object_modules + + for module, names in find_object_modules(obj).items(): + if name in names and (feature := module_feature(module)): + return feature + + return None diff --git a/src/sage/features/gap.py b/src/sage/features/gap.py index dad0e975804..17aabcdca54 100644 --- a/src/sage/features/gap.py +++ b/src/sage/features/gap.py @@ -13,12 +13,16 @@ from . import Feature, FeatureTestResult, PythonModule from .join_feature import JoinFeature - +from .sagemath import sage__libs__gap class GapPackage(Feature): r""" A :class:`~sage.features.Feature` describing the presence of a GAP package. + .. SEEALSO:: + + :class:`Feature sage.libs.gap <~sage.features.sagemath.sage__libs__gap>` + EXAMPLES:: sage: from sage.features.gap import GapPackage @@ -48,7 +52,11 @@ def _is_present(self): sage: GapPackage("grape", spkg="gap_packages")._is_present() # optional - gap_packages FeatureTestResult('gap_package_grape', True) """ - from sage.libs.gap.libgap import libgap + try: + from sage.libs.gap.libgap import libgap + except ImportError: + return FeatureTestResult(self, False, + reason="sage.libs.gap is not available") command = 'TestPackageAvailability("{package}")'.format(package=self.package) presence = libgap.eval(command) if presence: @@ -59,32 +67,5 @@ def _is_present(self): reason="`{command}` evaluated to `{presence}` in GAP.".format(command=command, presence=presence)) -class sage__libs__gap(JoinFeature): - r""" - A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.gap` - (the library interface to :ref:`GAP `) and :mod:`sage.interfaces.gap` (the pexpect - interface to GAP). By design, we do not distinguish between these two, in order - to facilitate the conversion of code from the pexpect interface to the library - interface. - - EXAMPLES:: - - sage: from sage.features.gap import sage__libs__gap - sage: sage__libs__gap().is_present() # optional - sage.libs.gap - FeatureTestResult('sage.libs.gap', True) - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.gap import sage__libs__gap - sage: isinstance(sage__libs__gap(), sage__libs__gap) - True - """ - JoinFeature.__init__(self, 'sage.libs.gap', - [PythonModule('sage.libs.gap.libgap'), - PythonModule('sage.interfaces.gap')]) - - def all_features(): - return [sage__libs__gap()] + return [] diff --git a/src/sage/features/kenzo.py b/src/sage/features/kenzo.py index 0060e6deb03..72f703da15f 100644 --- a/src/sage/features/kenzo.py +++ b/src/sage/features/kenzo.py @@ -48,7 +48,10 @@ def _is_present(self): sage: Kenzo()._is_present() # optional - kenzo FeatureTestResult('kenzo', True) """ - from sage.libs.ecl import ecl_eval + try: + from sage.libs.ecl import ecl_eval + except ImportError: + return FeatureTestResult(self, False, reason="sage.libs.ecl is not available") # Redirection of ECL and Maxima stdout to /dev/null # This is also done in the Maxima library, but we # also do it here for redundancy. diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index 580be376237..f7c3c0749ee 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -1,10 +1,37 @@ r""" Features for testing the presence of Python modules in the Sage library + +All of these features are present in a monolithic installation of the Sage library, +such as the one made by the SageMath distribution. + +The features are defined for the purpose of separately testing modularized +distributions such as :ref:`sagemath-categories ` +and :ref:`sagemath-repl `. + +Often, doctests in a module of the Sage library illustrate the +interplay with a range of different objects; this is a form of integration testing. +These objects may come from modules shipped in +other distributions. For example, :mod:`sage.structure.element` +(shipped by :ref:`sagemath-objects `, +one of the most fundamental distributions) contains the +doctest:: + + sage: G = SymmetricGroup(4) # needs sage.groups + sage: g = G([2, 3, 4, 1]) # needs sage.groups + sage: g.powers(4) # needs sage.groups + [(), (1,2,3,4), (1,3)(2,4), (1,4,3,2)] + +This test cannot pass when the distribution :ref:`sagemath-objects ` +is tested separately (in a virtual environment): In this situation, +:class:`SymmetricGroup` is not defined anywhere (and thus not present +in the top-level namespace). +Hence, we conditionalize this doctest on the presence of the feature +:class:`sage.groups `. """ # ***************************************************************************** -# Copyright (C) 2021 Matthias Koeppe -# 2021 Kwankyu Lee +# Copyright (C) 2021-2023 Matthias Koeppe +# 2021 Kwankyu Lee # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -14,18 +41,26 @@ from . import PythonModule, StaticFile from .join_feature import JoinFeature -from .singular import sage__libs__singular class sagemath_doc_html(StaticFile): r""" - A :class:`Feature` which describes the presence of the documentation + A :class:`~sage.features.Feature` which describes the presence of the documentation of the Sage library in HTML format. - EXAMPLES:: + Developers often use ``make build`` instead of ``make`` to avoid the + long time it takes to compile the documentation. Although commands + such as ``make ptest`` build the documentation before testing, other + test commands such as ``make ptestlong-nodoc`` or ``./sage -t --all`` + do not. + + All doctests that refer to the built documentation need to be marked + ``# needs sagemath_doc_html``. + + TESTS:: sage: from sage.features.sagemath import sagemath_doc_html - sage: sagemath_doc_html().is_present() # optional - sagemath_doc_html + sage: sagemath_doc_html().is_present() # needs sagemath_doc_html FeatureTestResult('sagemath_doc_html', True) """ def __init__(self): @@ -48,10 +83,54 @@ class sage__combinat(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.combinat`. - EXAMPLES:: + EXAMPLES: + + Python modules that provide elementary combinatorial objects such as :mod:`sage.combinat.subset`, + :mod:`sage.combinat.composition`, :mod:`sage.combinat.permutation` are always available; + there is no need for an ``# optional/needs`` tag:: + + sage: Permutation([1,2,3]).is_even() + True + sage: Permutation([6,1,4,5,2,3]).bruhat_inversions() + [[0, 1], [0, 2], [0, 3], [2, 4], [2, 5], [3, 4], [3, 5]] + + Use ``# needs sage.combinat`` for doctests that use any other Python modules + from :mod:`sage.combinat`, for example :mod:`sage.combinat.tableau_tuple`:: + + sage: TableauTuple([[[7,8,9]],[],[[1,2,3],[4,5],[6]]]).shape() # needs sage.combinat + ([3], [], [3, 2, 1]) + + Doctests that use Python modules from :mod:`sage.combinat` that involve trees, + graphs, hypergraphs, posets, quivers, combinatorial designs, + finite state machines etc. should be marked ``# needs sage.combinat sage.graphs``:: + + sage: L = Poset({0: [1], 1: [2], 2:[3], 3:[4]}) # needs sage.combinat sage.graphs + sage: L.is_chain() # needs sage.combinat sage.graphs + True + + Doctests that use combinatorial modules/algebras, or root systems should use the tag + ``# needs sage.combinat sage.modules``:: + + sage: # needs sage.combinat sage.modules + sage: A = SchurAlgebra(QQ, 2, 3) + sage: a = A.an_element(); a + 2*S((1, 1, 1), (1, 1, 1)) + 2*S((1, 1, 1), (1, 1, 2)) + + 3*S((1, 1, 1), (1, 2, 2)) + sage: L = RootSystem(['A',3,1]).root_lattice() + sage: PIR = L.positive_imaginary_roots(); PIR + Positive imaginary roots of type ['A', 3, 1] + + Doctests that use lattices, semilattices, or Dynkin diagrams should use the tag + ``# needs sage.combinat sage.graphs sage.modules``:: + + sage: L = LatticePoset({0: [1,2], 1: [3], 2: [3,4], 3: [5], 4: [5]}) # needs sage.combinat sage.graphs sage.modules + sage: L.meet_irreducibles() # needs sage.combinat sage.graphs sage.modules + [1, 3, 4] + + TESTS:: sage: from sage.features.sagemath import sage__combinat - sage: sage__combinat().is_present() # optional - sage.combinat + sage: sage__combinat().is_present() # needs sage.combinat FeatureTestResult('sage.combinat', True) """ def __init__(self): @@ -67,18 +146,36 @@ def __init__(self): # Some modules providing basic combinatorics are already included in sagemath-categories. # Hence, we test a Python module within the package. JoinFeature.__init__(self, 'sage.combinat', - [PythonModule('sage.combinat.tableau')], + [PythonModule('sage.combinat'), # namespace package + PythonModule('sage.combinat.tableau'), # representative + ], spkg='sagemath_combinat', type="standard") -class sage__geometry__polyhedron(PythonModule): +class sage__geometry__polyhedron(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.geometry.polyhedron`. - EXAMPLES:: + EXAMPLES: + + Doctests that use polyhedra, cones, geometric complexes, triangulations, etc. should use + the tag ``# needs sage.geometry.polyhedron``:: + + sage: co = polytopes.truncated_tetrahedron() # needs sage.geometry.polyhedron + sage: co.volume() # needs sage.geometry.polyhedron + 184/3 + + Some constructions of polyhedra require additional tags:: + + sage: # needs sage.combinat sage.geometry.polyhedron sage.rings.number_field + sage: perm_a3_reg_nf = polytopes.generalized_permutahedron( + ....: ['A',3], regular=True, backend='number_field'); perm_a3_reg_nf + A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices + + TESTS:: sage: from sage.features.sagemath import sage__geometry__polyhedron - sage: sage__geometry__polyhedron().is_present() # optional - sage.geometry.polyhedron + sage: sage__geometry__polyhedron().is_present() # needs sage.geometry.polyhedron FeatureTestResult('sage.geometry.polyhedron', True) """ @@ -90,18 +187,69 @@ def __init__(self): sage: isinstance(sage__geometry__polyhedron(), sage__geometry__polyhedron) True """ - PythonModule.__init__(self, 'sage.geometry.polyhedron', - spkg='sagemath_polyhedra', type="standard") + JoinFeature.__init__(self, 'sage.geometry.polyhedron', + [PythonModule('sage.geometry'), # namespace package + PythonModule('sage.geometry.polyhedron'), # representative + PythonModule('sage.schemes.toric'), # namespace package + PythonModule('sage.schemes.toric.variety'), # representative + ], + spkg='sagemath_polyhedra', type="standard") class sage__graphs(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.graphs`. - EXAMPLES:: + EXAMPLES: + + Doctests that use anything from :mod:`sage.graphs` (:class:`Graph`, :class:`DiGraph`, ...) + should be marked ``# needs sage.graphs``. The same applies to any doctest that + uses a :class:`~sage.combinat.posets.posets.Poset`, cluster algebra quiver, finite + state machines, abelian sandpiles, or Dynkin diagrams:: + + sage: g = graphs.PetersenGraph() # needs sage.graphs + sage: r, s = g.is_weakly_chordal(certificate=True); r # needs sage.graphs + False + + Also any use of tree classes defined in :mod:`sage.combinat` (:class:`BinaryTree`, + :class:`RootedTree`, ...) in doctests should be marked the same. + + By way of generalization, any use of :class:`SimplicialComplex` or other abstract complexes from + :mod:`sage.topology`, hypergraphs, and combinatorial designs, should be marked + ``# needs sage.graphs`` as well:: + + sage: X = SimplicialComplex([[0,1,2], [1,2,3]]) # needs sage.graphs + sage: X.link(Simplex([0])) # needs sage.graphs + Simplicial complex with vertex set (1, 2) and facets {(1, 2)} + + sage: IncidenceStructure([[1,2,3],[1,4]]).degrees(2) # needs sage.graphs + {(1, 2): 1, (1, 3): 1, (1, 4): 1, (2, 3): 1, (2, 4): 0, (3, 4): 0} + + On the other hand, matroids are not implemented as posets in Sage but are instead + closely tied to linear algebra over fields; hence use ``# needs sage.modules`` instead:: + + sage: # needs sage.modules + sage: M = Matroid(Matrix(QQ, [[1, 0, 0, 0, 1, 1, 1], + ....: [0, 1, 0, 1, 0, 1, 1], + ....: [0, 0, 1, 1, 1, 0, 1]])) + sage: N = M / [2] \ [3, 4] + sage: sorted(N.groundset()) + [0, 1, 5, 6] + + However, many constructions (and some methods) of matroids do involve graphs:: + + sage: # needs sage.modules + sage: W = matroids.Wheel(3) # despite the name, not created via graphs + sage: W.is_isomorphic(N) # goes through a graph isomorphism test # needs sage.graphs + False + sage: K4 = matroids.CompleteGraphic(4) # this one is created via graphs # needs sage.graphs + sage: K4.is_isomorphic(W) # needs sage.graphs + True + + TESTS:: sage: from sage.features.sagemath import sage__graphs - sage: sage__graphs().is_present() # optional - sage.graphs + sage: sage__graphs().is_present() # needs sage.graphs FeatureTestResult('sage.graphs', True) """ def __init__(self): @@ -113,54 +261,76 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.graphs', - [PythonModule('sage.graphs.graph')], + # These lists of modules are an (incomplete) duplication + # of information in the distribution's MANIFEST. + # But at least as long as the monolithic Sage library is + # around, we need this information here for use by + # sage-fixdoctests. + [PythonModule('sage.graphs'), # namespace package + PythonModule('sage.graphs.graph'), # representative + PythonModule('sage.combinat.designs'), # namespace package + PythonModule('sage.combinat.designs.block_design'), # representative + PythonModule('sage.combinat.posets'), # namespace package + PythonModule('sage.combinat.posets.posets'), # representative + PythonModule('sage.topology'), # namespace package + PythonModule('sage.topology.simplicial_complex'), # representative + ], spkg='sagemath_graphs', type="standard") -class sage__modular(JoinFeature): +class sage__groups(JoinFeature): r""" - A :class:`~sage.features.Feature` describing the presence of :mod:`sage.modular`. + A :class:`~sage.features.Feature` describing the presence of ``sage.groups``. - EXAMPLES:: + EXAMPLES: - sage: from sage.features.sagemath import sage__modular - sage: sage__modular().is_present() # optional - sage.modular - FeatureTestResult('sage.modular', True) + Permutations and sets of permutations are always available, but permutation groups are + implemented in Sage using the :ref:`GAP ` system and require the tag + ``# needs sage.groups``:: + + sage: p = Permutation([2,1,4,3]) + sage: p.to_permutation_group_element() # needs sage.groups + (1,2)(3,4) + + TESTS:: + + sage: from sage.features.sagemath import sage__groups + sage: sage__groups().is_present() # needs sage.groups + FeatureTestResult('sage.groups', True) """ def __init__(self): r""" TESTS:: - sage: from sage.features.sagemath import sage__modular - sage: isinstance(sage__modular(), sage__modular) + sage: from sage.features.sagemath import sage__groups + sage: isinstance(sage__groups(), sage__groups) True """ - JoinFeature.__init__(self, 'sage.modular', - [PythonModule('sage.modular.modform.eisenstein_submodule')], - spkg='sagemath_schemes', type='standard') + JoinFeature.__init__(self, 'sage.groups', + [PythonModule('sage.groups.perm_gps.permgroup')], + spkg='sagemath_groups', type='standard') -class sage__groups(JoinFeature): +class sage__libs__ecl(PythonModule): r""" - A :class:`sage.features.Feature` describing the presence of ``sage.groups``. + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.ecl`. EXAMPLES:: - sage: from sage.features.sagemath import sage__groups - sage: sage__groups().is_present() # optional - sage.groups - FeatureTestResult('sage.groups', True) + sage: from sage.features.sagemath import sage__libs__ecl + sage: sage__libs__ecl().is_present() # optional - sage.libs.ecl + FeatureTestResult('sage.libs.ecl', True) """ + def __init__(self): r""" TESTS:: - sage: from sage.features.sagemath import sage__groups - sage: isinstance(sage__groups(), sage__groups) + sage: from sage.features.sagemath import sage__libs__ecl + sage: isinstance(sage__libs__ecl(), sage__libs__ecl) True """ - JoinFeature.__init__(self, 'sage.groups', - [PythonModule('sage.groups.perm_gps.permgroup')], - spkg='sagemath_groups', type='standard') + PythonModule.__init__(self, 'sage.libs.ecl') class sage__libs__flint(JoinFeature): @@ -168,10 +338,13 @@ class sage__libs__flint(JoinFeature): A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.flint` and other modules depending on FLINT and arb. - EXAMPLES:: + In addition to the modularization purposes that this tag serves, it also provides attribution + to the upstream project. + + TESTS:: sage: from sage.features.sagemath import sage__libs__flint - sage: sage__libs__flint().is_present() # optional - sage.libs.flint + sage: sage__libs__flint().is_present() # needs sage.libs.flint FeatureTestResult('sage.libs.flint', True) """ def __init__(self): @@ -188,15 +361,49 @@ def __init__(self): spkg='sagemath_flint', type='standard') +class sage__libs__gap(JoinFeature): + r""" + A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.gap` + (the library interface to :ref:`GAP `) and :mod:`sage.interfaces.gap` (the pexpect + interface to GAP). By design, we do not distinguish between these two, in order + to facilitate the conversion of code from the pexpect interface to the library + interface. + + .. SEEALSO:: + + :class:`Features for GAP packages <~sage.features.gap.GapPackage>` + + TESTS:: + + sage: from sage.features.gap import sage__libs__gap + sage: sage__libs__gap().is_present() # needs sage.libs.gap + FeatureTestResult('sage.libs.gap', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.gap import sage__libs__gap + sage: isinstance(sage__libs__gap(), sage__libs__gap) + True + """ + JoinFeature.__init__(self, 'sage.libs.gap', + [PythonModule('sage.libs.gap.libgap'), + PythonModule('sage.interfaces.gap')]) + + class sage__libs__ntl(JoinFeature): r""" A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.ntl` and other modules depending on NTL and arb. - EXAMPLES:: + In addition to the modularization purposes that this tag serves, it also provides attribution + to the upstream project. + + TESTS:: sage: from sage.features.sagemath import sage__libs__ntl - sage: sage__libs__ntl().is_present() # optional - sage.libs.ntl + sage: sage__libs__ntl().is_present() # needs sage.libs.ntl FeatureTestResult('sage.libs.ntl', True) """ def __init__(self): @@ -214,12 +421,26 @@ def __init__(self): class sage__libs__pari(JoinFeature): r""" - A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.pari`. + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.pari`. + + SageMath uses the :ref:`PARI ` library (via :ref:`cypari2 `) for numerous purposes. + Doctests that involves such features should be marked ``# needs sage.libs.pari``. + + In addition to the modularization purposes that this tag serves, it also provides attribution + to the upstream project. EXAMPLES:: + sage: R. = QQ[] + sage: S. = R[] + sage: f = x^2 + a; g = x^3 + a + sage: r = f.resultant(g); r # needs sage.libs.pari + a^3 + a^2 + + TESTS:: + sage: from sage.features.sagemath import sage__libs__pari - sage: sage__libs__pari().is_present() # optional - sage.libs.pari + sage: sage__libs__pari().is_present() # needs sage.libs.pari FeatureTestResult('sage.libs.pari', True) """ def __init__(self): @@ -235,14 +456,83 @@ def __init__(self): spkg='sagemath_pari', type='standard') +class sage__libs__singular(JoinFeature): + r""" + A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.singular` + (the library interface to Singular) and :mod:`sage.interfaces.singular` (the pexpect + interface to Singular). By design, we do not distinguish between these two, in order + to facilitate the conversion of code from the pexpect interface to the library + interface. + + .. SEEALSO:: + + :class:`Feature singular <~sage.features.singular.Singular>` + + TESTS:: + + sage: from sage.features.singular import sage__libs__singular + sage: sage__libs__singular().is_present() # needs sage.libs.singular + FeatureTestResult('sage.libs.singular', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.singular import sage__libs__singular + sage: isinstance(sage__libs__singular(), sage__libs__singular) + True + """ + JoinFeature.__init__(self, 'sage.libs.singular', + [PythonModule('sage.libs.singular.singular'), + PythonModule('sage.interfaces.singular')]) + + +class sage__modular(JoinFeature): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.modular`. + + TESTS:: + + sage: from sage.features.sagemath import sage__modular + sage: sage__modular().is_present() # needs sage.modular + FeatureTestResult('sage.modular', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__modular + sage: isinstance(sage__modular(), sage__modular) + True + """ + JoinFeature.__init__(self, 'sage.modular', + [PythonModule('sage.modular.modform.eisenstein_submodule')], + spkg='sagemath_schemes', type='standard') + + class sage__modules(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.modules`. - EXAMPLES:: + EXAMPLES: + + All uses of implementations of vector spaces / free modules in SageMath, whether + :class:`sage.modules.free_module.FreeModule`, + :class:`sage.combinat.free_module.CombinatorialFreeModule`, + :class:`sage.tensor.modules.finite_rank_free_module.FiniteRankFreeModule`, or + additive abelian groups, should be marked ``# needs sage.modules``. + + The same holds for matrices, tensors, algebras, quadratic forms, + point lattices, root systems, matrix/affine/Weyl/Coxeter groups, matroids, + and ring derivations. + + Likewise, all uses of :mod:`sage.coding`, :mod:`sage.crypto`, and :mod:`sage.homology` + in doctests should be marked ``# needs sage.modules``. + + TESTS:: sage: from sage.features.sagemath import sage__modules - sage: sage__modules().is_present() # optional - sage.modules + sage: sage__modules().is_present() # needs sage.modules FeatureTestResult('sage.modules', True) """ def __init__(self): @@ -254,18 +544,57 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.modules', - [PythonModule('sage.modules.free_module')], + [PythonModule('sage.modules'), # namespace package + PythonModule('sage.modules.free_module'), # representative + PythonModule('sage.matrix'), # namespace package + PythonModule('sage.matrix.matrix2'), # representative + PythonModule('sage.combinat.free_module'), + PythonModule('sage.quadratic_forms'), # namespace package + PythonModule('sage.quadratic_forms.quadratic_form'), # representative + PythonModule('sage.groups.additive_abelian'), # namespace package + PythonModule('sage.groups.additive_abelian.qmodnz'), # representative + PythonModule('sage.groups.affine_gps'), # namespace package + PythonModule('sage.groups.affine_gps.affine_group'), # representative + PythonModule('sage.groups.matrix_gps'), # namespace package + PythonModule('sage.groups.matrix_gps.named_group'), # representative + PythonModule('sage.homology'), # namespace package + PythonModule('sage.homology.chain_complex'), # representative + PythonModule('sage.matroids'), # namespace package + PythonModule('sage.matroids.matroid'), # representative + ], spkg='sagemath_modules', type='standard') +class sage__numerical__mip(PythonModule): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.numerical.mip`. + + TESTS:: + + sage: from sage.features.sagemath import sage__numerical__mip + sage: sage__numerical__mip().is_present() # needs sage.numerical.mip + FeatureTestResult('sage.numerical.mip', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__numerical__mip + sage: isinstance(sage__numerical__mip(), sage__numerical__mip) + True + """ + PythonModule.__init__(self, 'sage.numerical.mip', + spkg='sagemath_polyhedra') + + class sage__plot(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.plot`. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__plot - sage: sage__plot().is_present() # optional - sage.plot + sage: sage__plot().is_present() # needs sage.plot FeatureTestResult('sage.plot', True) """ def __init__(self): @@ -278,18 +607,40 @@ def __init__(self): """ JoinFeature.__init__(self, 'sage.plot', [PythonModule('sage.plot.plot')], - spkg='sagemath_symbolics', type='standard') + spkg='sagemath_plot', type='standard') + + +class sage__rings__complex_double(PythonModule): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.complex_double`. + + TESTS:: + + sage: from sage.features.sagemath import sage__rings__complex_double + sage: sage__rings__complex_double().is_present() # needs sage.rings.complex_double + FeatureTestResult('sage.rings.complex_double', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__rings__complex_double + sage: isinstance(sage__rings__complex_double(), sage__rings__complex_double) + True + """ + PythonModule.__init__(self, 'sage.rings.complex_double', + spkg='sagemath_modules', type='standard') class sage__rings__finite_rings(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.finite_rings`; - specifically, the element implementations using PARI. + specifically, the element implementations using the :ref:`PARI ` library. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__rings__finite_rings - sage: sage__rings__finite_rings().is_present() # optional - sage.rings.finite_rings + sage: sage__rings__finite_rings().is_present() # needs sage.rings.finite_rings FeatureTestResult('sage.rings.finite_rings', True) """ def __init__(self): @@ -301,7 +652,8 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.rings.finite_rings', - [PythonModule('sage.rings.finite_rings.element_pari_ffelt')], + [PythonModule('sage.rings.finite_rings.element_pari_ffelt'), + PythonModule('sage.rings.algebraic_closure_finite_field')], type='standard') @@ -309,10 +661,30 @@ class sage__rings__function_field(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.function_field`. - EXAMPLES:: + EXAMPLES: + + Rational function fields are always available:: + + sage: K. = FunctionField(QQ) + sage: K.maximal_order() + Maximal order of Rational function field in x over Rational Field + + Use the tag ``# needs sage.rings.function_field`` whenever extensions + of function fields (by adjoining a root of a univariate polynomial) come into play:: + + sage: R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L # needs sage.rings.function_field + Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + + Such extensions of function fields are implemented using Gröbner bases of polynomial rings; + Sage makes essential use of the :ref:`Singular ` system for this. + (It is not necessary to use the tag ``# needs sage.libs.singular``; it is + implied by ``# needs sage.rings.function_field``.) + + TESTS:: sage: from sage.features.sagemath import sage__rings__function_field - sage: sage__rings__function_field().is_present() # optional - sage.rings.function_field + sage: sage__rings__function_field().is_present() # needs sage.rings.function_field FeatureTestResult('sage.rings.function_field', True) """ def __init__(self): @@ -333,10 +705,52 @@ class sage__rings__number_field(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.number_field`. - EXAMPLES:: + Number fields are implemented in Sage using a complicated mixture of various libraries, + including :ref:`arb `, :ref:`FLINT `, :ref:`GAP `, + :ref:`MPFI `, :ref:`NTL `, and :ref:`PARI `. + + EXAMPLES: + + Rational numbers are, of course, always available:: + + sage: QQ in NumberFields() + True + + Doctests that construct algebraic number fields should be marked ``# needs sage.rings.number_field``:: + + sage: # needs sage.rings.number_field + sage: K. = NumberField(x^3 - 2) + sage: L. = K.extension(x^3 - 3) + sage: S. = L.extension(x^2 - 2); S + Number Field in sqrt2 with defining polynomial x^2 - 2 over its base field + + sage: # needs sage.rings.number_field + sage: K. = CyclotomicField(15) + sage: CC(zeta) + 0.913545457642601 + 0.406736643075800*I + + Doctests that make use of the algebraic field ``QQbar``, the algebraic real field ``AA``, + or the universal cyclotomic field should be marked likewise:: + + sage: # needs sage.rings.number_field + sage: AA(-1)^(1/3) + -1 + sage: QQbar(-1)^(1/3) + 0.500000000000000? + 0.866025403784439?*I + + sage: # needs sage.rings.number_field + sage: UCF = UniversalCyclotomicField(); UCF + Universal Cyclotomic Field + sage: E = UCF.gen + sage: f = E(2) + E(3); f + 2*E(3) + E(3)^2 + sage: f.galois_conjugates() + [2*E(3) + E(3)^2, E(3) + 2*E(3)^2] + + TESTS:: sage: from sage.features.sagemath import sage__rings__number_field - sage: sage__rings__number_field().is_present() # optional - sage.rings.number_field + sage: sage__rings__number_field().is_present() # needs sage.rings.number_field FeatureTestResult('sage.rings.number_field', True) """ def __init__(self): @@ -348,18 +762,20 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.rings.number_field', - [PythonModule('sage.rings.number_field.number_field_element')], + [PythonModule('sage.rings.number_field.number_field_element'), + PythonModule('sage.rings.universal_cyclotomic_field'), + PythonModule('sage.rings.qqbar')], type='standard') class sage__rings__padics(JoinFeature): r""" - A :class:`sage.features.Feature` describing the presence of ``sage.rings.padics``. + A :class:`~sage.features.Feature` describing the presence of ``sage.rings.padics``. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__rings__padics - sage: sage__rings__padics().is_present() # optional - sage.rings.padics + sage: sage__rings__padics().is_present() # needs sage.rings.padics FeatureTestResult('sage.rings.padics', True) """ def __init__(self): @@ -379,10 +795,10 @@ class sage__rings__polynomial__pbori(JoinFeature): r""" A :class:`sage.features.Feature` describing the presence of :mod:`sage.rings.polynomial.pbori`. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__rings__polynomial__pbori - sage: sage__rings__polynomial__pbori().is_present() # optional - sage.rings.polynomial.pbori + sage: sage__rings__polynomial__pbori().is_present() # needs sage.rings.polynomial.pbori FeatureTestResult('sage.rings.polynomial.pbori', True) """ def __init__(self): @@ -402,10 +818,20 @@ class sage__rings__real_double(PythonModule): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.real_double`. - EXAMPLES:: + EXAMPLES: + + The Real Double Field is basically always available, and no ``# optional/needs`` tag is needed:: + + sage: RDF.characteristic() + 0 + + The feature exists for use in doctests of Python modules that are shipped by the + most fundamental distributions. + + TESTS:: sage: from sage.features.sagemath import sage__rings__real_double - sage: sage__rings__real_double().is_present() # optional - sage.rings.real_double + sage: sage__rings__real_double().is_present() # needs sage.rings.real_double FeatureTestResult('sage.rings.real_double', True) """ def __init__(self): @@ -416,17 +842,17 @@ def __init__(self): sage: isinstance(sage__rings__real_double(), sage__rings__real_double) True """ - PythonModule.__init__(self, 'sage.rings.real_double') + PythonModule.__init__(self, 'sage.rings.real_double', type='standard') -class sage__rings__real_mpfr(PythonModule): +class sage__rings__real_mpfr(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.real_mpfr`. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__rings__real_mpfr - sage: sage__rings__real_mpfr().is_present() # optional - sage.rings.real_mpfr + sage: sage__rings__real_mpfr().is_present() # needs sage.rings.real_mpfr FeatureTestResult('sage.rings.real_mpfr', True) """ def __init__(self): @@ -437,18 +863,44 @@ def __init__(self): sage: isinstance(sage__rings__real_mpfr(), sage__rings__real_mpfr) True """ - PythonModule.__init__(self, 'sage.rings.real_mpfr', - spkg='sagemath_modules') + JoinFeature.__init__(self, 'sage.rings.real_mpfr', + [PythonModule('sage.rings.real_mpfr'), + PythonModule('sage.rings.complex_mpfr'), + ], + spkg='sagemath_modules', type='standard') + + +class sage__sat(JoinFeature): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.sat`. + + TESTS:: + + sage: from sage.features.sagemath import sage__sat + sage: sage__sat().is_present() # needs sage.sat + FeatureTestResult('sage.sat', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__sat + sage: isinstance(sage__sat(), sage__sat) + True + """ + JoinFeature.__init__(self, 'sage.sat', + [PythonModule('sage.sat.expression')], + spkg='sagemath_combinat', type='standard') class sage__schemes(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.schemes`. - EXAMPLES:: + TESTS:: sage: from sage.features.sagemath import sage__schemes - sage: sage__schemes().is_present() # optional - sage.schemes + sage: sage__schemes().is_present() # needs sage.schemes FeatureTestResult('sage.schemes', True) """ def __init__(self): @@ -461,17 +913,29 @@ def __init__(self): """ JoinFeature.__init__(self, 'sage.schemes', [PythonModule('sage.schemes.elliptic_curves.ell_generic')], - spkg="sagemath_schemes") + spkg="sagemath_schemes", type='standard') class sage__symbolic(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.symbolic`. - EXAMPLES:: + EXAMPLES: + + The symbolics subsystem of Sage will be provided by the distribution + sagemath-symbolics, in preparation at :issue:`35095`. If it is not installed, + Sage will be able to provide installation advice:: + + sage: from sage.features.sagemath import sage__symbolic + sage: print(sage__symbolic().resolution()) # optional - sage_spkg, not tested + ...To install sagemath_symbolics...you can try to run... + pip install sagemath-symbolics + ... + + TESTS:: sage: from sage.features.sagemath import sage__symbolic - sage: sage__symbolic().is_present() # optional - sage.symbolic + sage: sage__symbolic().is_present() # needs sage.symbolic FeatureTestResult('sage.symbolic', True) """ def __init__(self): @@ -483,8 +947,32 @@ def __init__(self): True """ JoinFeature.__init__(self, 'sage.symbolic', - [PythonModule('sage.symbolic.expression')], - spkg="sagemath_symbolics") + [PythonModule('sage.symbolic.expression'), + PythonModule('sage.manifolds'), + PythonModule('sage.calculus.calculus'), + PythonModule('sage.calculus.desolvers'), + PythonModule('sage.calculus.predefined'), + PythonModule('sage.calculus.tests'), + PythonModule('sage.calculus.var'), + PythonModule('sage.geometry.riemannian_manifolds'), + PythonModule('sage.geometry.hyperbolic_space'), + PythonModule('sage.dynamics.complex_dynamics'), + PythonModule('sage.libs.pynac'), + PythonModule('sage.libs.ecl'), + PythonModule('sage.interfaces.fricas'), + PythonModule('sage.interfaces.giac'), + PythonModule('sage.interfaces.magma'), + PythonModule('sage.interfaces.magma_free'), + PythonModule('sage.interfaces.maple'), + PythonModule('sage.interfaces.mathematica'), + PythonModule('sage.interfaces.mathics'), + PythonModule('sage.interfaces.maxima'), + PythonModule('sage.interfaces.maxima_abstract'), + PythonModule('sage.interfaces.maxima_lib'), + PythonModule('sage.interfaces.qepcad'), + PythonModule('sage.interfaces.sympy'), + PythonModule('sage.interfaces.sympy_wrapper'), + ], spkg='sagemath_symbolics', type='standard') def all_features(): @@ -514,12 +1002,17 @@ def all_features(): sage__geometry__polyhedron(), sage__graphs(), sage__groups(), + sage__libs__ecl(), sage__libs__flint(), + sage__libs__gap(), sage__libs__ntl(), sage__libs__pari(), + sage__libs__singular(), sage__modular(), sage__modules(), + sage__numerical__mip(), sage__plot(), + sage__rings__complex_double(), sage__rings__finite_rings(), sage__rings__function_field(), sage__rings__number_field(), @@ -527,5 +1020,6 @@ def all_features(): sage__rings__polynomial__pbori(), sage__rings__real_double(), sage__rings__real_mpfr(), + sage__sat(), sage__schemes(), sage__symbolic()] diff --git a/src/sage/features/singular.py b/src/sage/features/singular.py index 64a24320044..fce89a8e91c 100644 --- a/src/sage/features/singular.py +++ b/src/sage/features/singular.py @@ -13,6 +13,7 @@ from . import Executable, PythonModule from .join_feature import JoinFeature +from .sagemath import sage__libs__singular from sage.env import SINGULAR_BIN @@ -20,10 +21,14 @@ class Singular(Executable): r""" A :class:`~sage.features.Feature` describing the presence of the :ref:`singular ` executable. + .. SEEALSO:: + + :class:`Feature sage.libs.singular <~sage.features.sagemath.sage__libs__singular>` + EXAMPLES:: sage: from sage.features.singular import Singular - sage: Singular().is_present() + sage: Singular().is_present() # needs singular FeatureTestResult('singular', True) """ def __init__(self): @@ -38,33 +43,5 @@ def __init__(self): spkg='singular', type='standard') -class sage__libs__singular(JoinFeature): - r""" - A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.singular` - (the library interface to Singular) and :mod:`sage.interfaces.singular` (the pexpect - interface to Singular). By design, we do not distinguish between these two, in order - to facilitate the conversion of code from the pexpect interface to the library - interface. - - EXAMPLES:: - - sage: from sage.features.singular import sage__libs__singular - sage: sage__libs__singular().is_present() # optional - sage.libs.singular - FeatureTestResult('sage.libs.singular', True) - """ - def __init__(self): - r""" - TESTS:: - - sage: from sage.features.singular import sage__libs__singular - sage: isinstance(sage__libs__singular(), sage__libs__singular) - True - """ - JoinFeature.__init__(self, 'sage.libs.singular', - [PythonModule('sage.libs.singular.singular'), - PythonModule('sage.interfaces.singular')]) - - def all_features(): - return [Singular(), - sage__libs__singular()] + return [Singular()] diff --git a/src/sage/geometry/lattice_polytope.py b/src/sage/geometry/lattice_polytope.py index 921b0004dfe..eab90bd12d2 100644 --- a/src/sage/geometry/lattice_polytope.py +++ b/src/sage/geometry/lattice_polytope.py @@ -3188,11 +3188,6 @@ def _palp_PM_max(self, check=False): sage: all(results) # long time True """ - def PGE(S, u, v): - if u == v: - return S.one() - return S((u, v), check=False) - PM = self.vertex_facet_pairing_matrix() n_v = PM.ncols() n_f = PM.nrows() @@ -3202,30 +3197,24 @@ def PGE(S, u, v): # and find all the ways of making the first row of PM_max def index_of_max(iterable): # returns the index of max of any iterable - m, x = 0, iterable[0] - for k, l in enumerate(iterable): - if l > x: - m, x = k, l - return m + return max(enumerate(iterable), key=lambda x: x[1])[0] n_s = 1 - permutations = {0 : [S_f.one(), S_v.one()]} + permutations = {0: [S_f.one(), S_v.one()]} for j in range(n_v): - m = index_of_max( - [(PM.with_permuted_columns(permutations[0][1]))[0][i] - for i in range(j, n_v)]) + m = index_of_max(PM[0, i] for i in range(j, n_v)) if m > 0: - permutations[0][1] = PGE(S_v, j + 1, m + j + 1) * permutations[0][1] + permutations[0][1] = S_v((j + 1, m + j + 1), check=False) * permutations[0][1] first_row = list(PM[0]) # Arrange other rows one by one and compare with first row for k in range(1, n_f): # Error for k == 1 already! permutations[n_s] = [S_f.one(), S_v.one()] - m = index_of_max(PM.with_permuted_columns(permutations[n_s][1])[k]) + m = index_of_max(PM[k, permutations[n_s][1](j+1) - 1] for j in range(n_v)) if m > 0: - permutations[n_s][1] = PGE(S_v, 1, m+1) * permutations[n_s][1] - d = ((PM.with_permuted_columns(permutations[n_s][1]))[k][0] + permutations[n_s][1] = S_v((1, m + 1), check=False) * permutations[n_s][1] + d = (PM[k, permutations[n_s][1](1) - 1] - permutations[0][1](first_row)[0]) if d < 0: # The largest elt of this row is smaller than largest elt @@ -3233,14 +3222,12 @@ def index_of_max(iterable): continue # otherwise: for i in range(1, n_v): - m = index_of_max( - [PM.with_permuted_columns(permutations[n_s][1])[k][j] - for j in range(i, n_v)]) + m = index_of_max(PM[k, permutations[n_s][1](j+1) - 1] for j in range(i,n_v)) if m > 0: - permutations[n_s][1] = PGE(S_v, i + 1, m + i + 1) \ + permutations[n_s][1] = S_v((i + 1, m + i + 1), check=False) \ * permutations[n_s][1] if d == 0: - d = (PM.with_permuted_columns(permutations[n_s][1])[k][i] + d = (PM[k, permutations[n_s][1](i+1) - 1] -permutations[0][1](first_row)[i]) if d < 0: break @@ -3248,7 +3235,7 @@ def index_of_max(iterable): # This row is smaller than 1st row, so nothing to do del permutations[n_s] continue - permutations[n_s][0] = PGE(S_f, 1, k + 1) * permutations[n_s][0] + permutations[n_s][0] = S_f((1, k + 1), check=False) * permutations[n_s][0] if d == 0: # This row is the same, so we have a symmetry! n_s += 1 @@ -3258,9 +3245,9 @@ def index_of_max(iterable): first_row = list(PM[k]) permutations = {0: permutations[n_s]} n_s = 1 - permutations = {k:permutations[k] for k in permutations if k < n_s} + permutations = {k: permutations[k] for k in permutations if k < n_s} - b = PM.with_permuted_rows_and_columns(*permutations[0])[0] + b = tuple(PM[permutations[0][0](1) - 1, permutations[0][1](j+1) - 1] for j in range(n_v)) # Work out the restrictions the current permutations # place on other permutations as a automorphisms # of the first row @@ -3294,34 +3281,35 @@ def index_of_max(iterable): # between 0 and S(0) for s in range(l, n_f): for j in range(1, S[0]): - v = PM.with_permuted_rows_and_columns( - *permutations_bar[n_p])[s] - if v[0] < v[j]: - permutations_bar[n_p][1] = PGE(S_v, 1, j + 1) * permutations_bar[n_p][1] + v0 = PM[permutations_bar[n_p][0](s+1) - 1, permutations_bar[n_p][1](1) - 1] + vj = PM[permutations_bar[n_p][0](s+1) - 1, permutations_bar[n_p][1](j+1) - 1] + if v0 < vj: + permutations_bar[n_p][1] = S_v((1, j + 1), check=False) * permutations_bar[n_p][1] if ccf == 0: - l_r[0] = PM.with_permuted_rows_and_columns( - *permutations_bar[n_p])[s][0] - permutations_bar[n_p][0] = PGE(S_f, l + 1, s + 1) * permutations_bar[n_p][0] + l_r[0] = PM[permutations_bar[n_p][0](s+1) - 1, permutations_bar[n_p][1](1) - 1] + if s != l: + permutations_bar[n_p][0] = S_f((l + 1, s + 1), check=False) * permutations_bar[n_p][0] n_p += 1 ccf = 1 permutations_bar[n_p] = copy(permutations[k]) else: - d1 = PM.with_permuted_rows_and_columns( - *permutations_bar[n_p])[s][0] + d1 = PM[permutations_bar[n_p][0](s+1) - 1, permutations_bar[n_p][1](1) - 1] d = d1 - l_r[0] if d < 0: # We move to the next line continue elif d==0: # Maximal values agree, so possible symmetry - permutations_bar[n_p][0] = PGE(S_f, l + 1, s + 1) * permutations_bar[n_p][0] + if s != l: + permutations_bar[n_p][0] = S_f((l + 1, s + 1), check=False) * permutations_bar[n_p][0] n_p += 1 permutations_bar[n_p] = copy(permutations[k]) else: # We found a greater maximal value for first entry. # It becomes our new reference: l_r[0] = d1 - permutations_bar[n_p][0] = PGE(S_f, l + 1, s + 1) * permutations_bar[n_p][0] + if s != l: + permutations_bar[n_p][0] = S_f((l + 1, s + 1), check=False) * permutations_bar[n_p][0] # Forget previous work done cf = 0 permutations_bar = {0:copy(permutations_bar[n_p])} @@ -3343,18 +3331,16 @@ def index_of_max(iterable): s -= 1 # Find the largest value in this symmetry block for j in range(c + 1, h): - v = PM.with_permuted_rows_and_columns( - *permutations_bar[s])[l] - if (v[c] < v[j]): - permutations_bar[s][1] = PGE(S_v, c + 1, j + 1) * permutations_bar[s][1] + vc = PM[(permutations_bar[s][0])(l+1) - 1, (permutations_bar[s][1])(c+1) - 1] + vj = PM[(permutations_bar[s][0])(l+1) - 1, (permutations_bar[s][1])(j+1) - 1] + if (vc < vj): + permutations_bar[s][1] = S_v((c + 1, j + 1), check=False) * permutations_bar[s][1] if ccf == 0: # Set reference and carry on to next permutation - l_r[c] = PM.with_permuted_rows_and_columns( - *permutations_bar[s])[l][c] + l_r[c] = PM[(permutations_bar[s][0])(l+1) - 1, (permutations_bar[s][1])(c+1) - 1] ccf = 1 else: - d1 = PM.with_permuted_rows_and_columns( - *permutations_bar[s])[l][c] + d1 = PM[(permutations_bar[s][0])(l+1) - 1, (permutations_bar[s][1])(c+1) - 1] d = d1 - l_r[c] if d < 0: n_p -= 1 @@ -3383,7 +3369,7 @@ def index_of_max(iterable): # the restrictions the last worked out # row imposes. c = 0 - M = (PM.with_permuted_rows_and_columns(*permutations[0]))[l] + M = tuple(PM[permutations[0][0](l+1) - 1, permutations[0][1](j+1) - 1] for j in range(n_v)) while c < n_v: s = S[c] + 1 S[c] = c + 1 @@ -5196,11 +5182,10 @@ def _palp_canonical_order(V, PM_max, permutations): in 2-d lattice M, (1,3,2,4)) """ n_v = PM_max.ncols() - n_f = PM_max.nrows() S_v = SymmetricGroup(n_v) p_c = S_v.one() - M_max = [max([PM_max[i][j] for i in range(n_f)]) for j in range(n_v)] - S_max = [sum([PM_max[i][j] for i in range(n_f)]) for j in range(n_v)] + M_max = [max(row[j] for row in PM_max.rows()) for j in range(n_v)] + S_max = sum(PM_max) for i in range(n_v): k = i for j in range(i + 1, n_v): @@ -5213,13 +5198,15 @@ def _palp_canonical_order(V, PM_max, permutations): p_c = S_v((1 + i, 1 + k), check=False) * p_c # Create array of possible NFs. permutations = [p_c * l[1] for l in permutations.values()] - Vs = [(V.column_matrix().with_permuted_columns(sig).hermite_form(), sig) + Vmatrix = V.column_matrix() + Vs = [(Vmatrix.with_permuted_columns(sig).hermite_form(), sig) for sig in permutations] Vmin = min(Vs, key=lambda x:x[0]) - vertices = [V.module()(_) for _ in Vmin[0].columns()] + Vmodule = V.module() + vertices = [Vmodule(_) for _ in Vmin[0].columns()] for v in vertices: v.set_immutable() - return (PointCollection(vertices, V.module()), Vmin[1]) + return (PointCollection(vertices, Vmodule), Vmin[1]) def _palp_convert_permutation(permutation): diff --git a/src/sage/geometry/polyhedral_complex.py b/src/sage/geometry/polyhedral_complex.py index cff7b73c94e..bec18228aca 100644 --- a/src/sage/geometry/polyhedral_complex.py +++ b/src/sage/geometry/polyhedral_complex.py @@ -1234,7 +1234,7 @@ def connected_component(self, cell=None): if v not in g: raise ValueError( "the polyhedral complex does not contain the given cell") - vertices = g.connected_component_containing_vertex(v) + vertices = g.connected_component_containing_vertex(v, sort=False) facets = [f for f in self.maximal_cell_iterator() if any(vf in f.vertices_matrix().columns() for vf in vertices)] @@ -1243,7 +1243,7 @@ def connected_component(self, cell=None): if cell not in g: raise ValueError( "the polyhedral complex does not contain the given cell") - faces = g.connected_component_containing_vertex(cell) + faces = g.connected_component_containing_vertex(cell, sort=False) facets = [f for f in self.maximal_cell_iterator() if f in faces] return PolyhedralComplex(facets, maximality_check=False, diff --git a/src/sage/geometry/polyhedron/base4.py b/src/sage/geometry/polyhedron/base4.py index 6b309553123..ae092615ac5 100644 --- a/src/sage/geometry/polyhedron/base4.py +++ b/src/sage/geometry/polyhedron/base4.py @@ -1,4 +1,4 @@ -# sage.doctest: optional - sage.graphs +# sage.doctest: needs sage.graphs r""" Base class for polyhedra: Graph-theoretic methods @@ -353,7 +353,7 @@ def face_lattice(self): sage: c5_20_fl = c5_20.face_lattice() # long time sage: [len(x) for x in c5_20_fl.level_sets()] # long time [1, 20, 190, 580, 680, 272, 1] - sage: polytopes.hypercube(2).face_lattice().plot() # optional - sage.plot + sage: polytopes.hypercube(2).face_lattice().plot() # needs sage.plot Graphics object consisting of 27 graphics primitives sage: level_sets = polytopes.cross_polytope(2).face_lattice().level_sets() sage: level_sets[0][0].ambient_V_indices(), level_sets[-1][0].ambient_V_indices() @@ -399,31 +399,33 @@ def hasse_diagram(self): EXAMPLES:: - sage: P = polytopes.regular_polygon(4).pyramid() # optional - sage.rings.number_field - sage: D = P.hasse_diagram(); D # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: P = polytopes.regular_polygon(4).pyramid() + sage: D = P.hasse_diagram(); D Digraph on 20 vertices - sage: D.degree_polynomial() # optional - sage.rings.number_field + sage: D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3 Faces of an mutable polyhedron are not hashable. Hence those are not suitable as vertices of the hasse diagram. Use the combinatorial polyhedron instead:: - sage: P = polytopes.regular_polygon(4).pyramid() # optional - sage.rings.number_field - sage: parent = P.parent() # optional - sage.rings.number_field - sage: parent = parent.change_ring(QQ, backend='ppl') # optional - sage.rings.number_field - sage: Q = parent._element_constructor_(P, mutable=True) # optional - sage.rings.number_field - sage: Q.hasse_diagram() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: P = polytopes.regular_polygon(4).pyramid() + sage: parent = P.parent() + sage: parent = parent.change_ring(QQ, backend='ppl') + sage: Q = parent._element_constructor_(P, mutable=True) + sage: Q.hasse_diagram() Traceback (most recent call last): ... TypeError: mutable polyhedra are unhashable - sage: C = Q.combinatorial_polyhedron() # optional - sage.rings.number_field - sage: D = C.hasse_diagram() # optional - sage.rings.number_field - sage: set(D.vertices()) == set(range(20)) # optional - sage.rings.number_field + sage: C = Q.combinatorial_polyhedron() + sage: D = C.hasse_diagram() + sage: set(D.vertices(sort=False)) == set(range(20)) True sage: def index_to_combinatorial_face(n): ....: return C.face_by_face_lattice_index(n) - sage: D.relabel(index_to_combinatorial_face, inplace=True) # optional - sage.rings.number_field - sage: D.vertices() # optional - sage.rings.number_field + sage: D.relabel(index_to_combinatorial_face, inplace=True) + sage: D.vertices(sort=True) [A -1-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, A 0-dimensional face of a 3-dimensional combinatorial polyhedron, @@ -444,7 +446,7 @@ def hasse_diagram(self): A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 2-dimensional face of a 3-dimensional combinatorial polyhedron, A 3-dimensional face of a 3-dimensional combinatorial polyhedron] - sage: D.degree_polynomial() # optional - sage.rings.number_field + sage: D.degree_polynomial() x^5 + x^4*y + x*y^4 + y^5 + 4*x^3*y + 8*x^2*y^2 + 4*x*y^3 """ @@ -643,7 +645,8 @@ def combinatorial_automorphism_group(self, vertex_graph_only=False): EXAMPLES:: sage: quadrangle = Polyhedron(vertices=[(0,0),(1,0),(0,1),(2,3)]) - sage: quadrangle.combinatorial_automorphism_group().is_isomorphic(groups.permutation.Dihedral(4)) + sage: quadrangle.combinatorial_automorphism_group().is_isomorphic( + ....: groups.permutation.Dihedral(4)) True sage: quadrangle.restricted_automorphism_group() Permutation Group with generators [()] @@ -655,19 +658,24 @@ def combinatorial_automorphism_group(self, vertex_graph_only=False): Permutation Group with generators [(A vertex at (1,0),A vertex at (1,1))] This shows an example of two polytopes whose vertex-edge graphs are isomorphic, - but their face_lattices are not isomorphic:: - - sage: Q=Polyhedron([[-123984206864/2768850730773, -101701330976/922950243591, -64154618668/2768850730773, -2748446474675/2768850730773], - ....: [-11083969050/98314591817, -4717557075/98314591817, -32618537490/98314591817, -91960210208/98314591817], - ....: [-9690950/554883199, -73651220/554883199, 1823050/554883199, -549885101/554883199], [-5174928/72012097, 5436288/72012097, -37977984/72012097, 60721345/72012097], - ....: [-19184/902877, 26136/300959, -21472/902877, 899005/902877], [53511524/1167061933, 88410344/1167061933, 621795064/1167061933, 982203941/1167061933], - ....: [4674489456/83665171433, -4026061312/83665171433, 28596876672/83665171433, -78383796375/83665171433], [857794884940/98972360190089, -10910202223200/98972360190089, 2974263671400/98972360190089, -98320463346111/98972360190089]]) + but their face lattices are not isomorphic:: + + sage: Q = Polyhedron([[-123984206864/2768850730773, -101701330976/922950243591, -64154618668/2768850730773, -2748446474675/2768850730773], + ....: [-11083969050/98314591817, -4717557075/98314591817, -32618537490/98314591817, -91960210208/98314591817], + ....: [-9690950/554883199, -73651220/554883199, 1823050/554883199, -549885101/554883199], + ....: [-5174928/72012097, 5436288/72012097, -37977984/72012097, 60721345/72012097], + ....: [-19184/902877, 26136/300959, -21472/902877, 899005/902877], + ....: [53511524/1167061933, 88410344/1167061933, 621795064/1167061933, 982203941/1167061933], + ....: [4674489456/83665171433, -4026061312/83665171433, 28596876672/83665171433, -78383796375/83665171433], + ....: [857794884940/98972360190089, -10910202223200/98972360190089, 2974263671400/98972360190089, -98320463346111/98972360190089]]) sage: C = polytopes.cyclic_polytope(4,8) sage: C.is_combinatorially_isomorphic(Q) False - sage: C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic(Q.combinatorial_automorphism_group(vertex_graph_only=True)) + sage: C.combinatorial_automorphism_group(vertex_graph_only=True).is_isomorphic( + ....: Q.combinatorial_automorphism_group(vertex_graph_only=True)) True - sage: C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic(Q.combinatorial_automorphism_group(vertex_graph_only=False)) + sage: C.combinatorial_automorphism_group(vertex_graph_only=False).is_isomorphic( + ....: Q.combinatorial_automorphism_group(vertex_graph_only=False)) False The automorphism group of the face lattice is isomorphic to the combinatorial automorphism group:: @@ -1080,10 +1088,10 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): All the faces of the 3-dimensional permutahedron are either combinatorially isomorphic to a square or a hexagon:: - sage: H = polytopes.regular_polygon(6) # optional - sage.rings.number_field + sage: H = polytopes.regular_polygon(6) # needs sage.rings.number_field sage: S = polytopes.hypercube(2) sage: P = polytopes.permutahedron(4) - sage: all(F.as_polyhedron().is_combinatorially_isomorphic(S) # optional - sage.rings.number_field + sage: all(F.as_polyhedron().is_combinatorially_isomorphic(S) # needs sage.rings.number_field ....: or F.as_polyhedron().is_combinatorially_isomorphic(H) ....: for F in P.faces(2)) True @@ -1102,7 +1110,7 @@ def is_combinatorially_isomorphic(self, other, algorithm='bipartite_graph'): ....: return C.intersection(H) sage: [simplex_intersection(k).is_combinatorially_isomorphic(cube_intersection(k)) for k in range(2,5)] [True, True, True] - sage: simplex_intersection(2).is_combinatorially_isomorphic(polytopes.regular_polygon(6)) # optional - sage.rings.number_field + sage: simplex_intersection(2).is_combinatorially_isomorphic(polytopes.regular_polygon(6)) # needs sage.rings.number_field True sage: simplex_intersection(3).is_combinatorially_isomorphic(polytopes.octahedron()) True @@ -1225,7 +1233,7 @@ def is_self_dual(self): True sage: polytopes.cube().is_self_dual() False - sage: polytopes.hypersimplex(5,2).is_self_dual() # optional - sage.combinat + sage: polytopes.hypersimplex(5,2).is_self_dual() # needs sage.combinat False sage: P = Polyhedron(vertices=[[1/2, 1/3]], rays=[[1, 1]]).is_self_dual() Traceback (most recent call last): diff --git a/src/sage/geometry/polyhedron/base_QQ.py b/src/sage/geometry/polyhedron/base_QQ.py index d279aa5ea7f..7aa1c568480 100644 --- a/src/sage/geometry/polyhedron/base_QQ.py +++ b/src/sage/geometry/polyhedron/base_QQ.py @@ -1180,7 +1180,7 @@ class functions of the acting group. A character `\rho` is effective if True sage: Hstar = p3.Hstar_function(S3) # optional - pynormaliz sage: Hlin = p3.Hstar_function(S3, # optional - pynormaliz - ....: output='Hstar_as_lin_comb')] + ....: output='Hstar_as_lin_comb') sage: p3.is_effective(Hstar, Hlin) # optional - pynormaliz True @@ -1242,7 +1242,7 @@ class functions of the acting group. A character `\rho` is effective if sage: p2 = Polyhedron(vertices=[[0], [1/2]], # optional - pynormaliz ....: backend='normaliz') sage: Hstar = p2.Hstar_function() # optional - pynormaliz - sage: Hstarlin = p2.Hstar_function(output='Hstar_as_lin_comb')] # optional - pynormaliz + sage: Hstarlin = p2.Hstar_function(output='Hstar_as_lin_comb') # optional - pynormaliz sage: p1._is_effective_normaliz(Hstar, Hstarlin) # optional - pynormaliz Traceback (most recent call last): ... diff --git a/src/sage/graphs/all__sagemath_bliss.py b/src/sage/graphs/all__sagemath_bliss.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/graphs/all__sagemath_mcqd.py b/src/sage/graphs/all__sagemath_mcqd.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/graphs/all__sagemath_tdlib.py b/src/sage/graphs/all__sagemath_tdlib.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/graphs/base/c_graph.pyx b/src/sage/graphs/base/c_graph.pyx index d6fb119d3cf..a4f2efd6060 100644 --- a/src/sage/graphs/base/c_graph.pyx +++ b/src/sage/graphs/base/c_graph.pyx @@ -4819,7 +4819,7 @@ cdef class Search_iterator: Immutable graphs (see :trac:`16019`):: - sage: DiGraph([[1,2]], immutable=True).connected_components() + sage: DiGraph([(1, 2)], immutable=True).connected_components(sort=True) [[1, 2]] """ diff --git a/src/sage/graphs/base/static_sparse_graph.pyx b/src/sage/graphs/base/static_sparse_graph.pyx index a1f246883ff..cfaff95ccde 100644 --- a/src/sage/graphs/base/static_sparse_graph.pyx +++ b/src/sage/graphs/base/static_sparse_graph.pyx @@ -702,7 +702,7 @@ def tarjan_strongly_connected_components(G): sage: tarjan_strongly_connected_components(digraphs.Path(3)) [[2], [1], [0]] sage: D = DiGraph( { 0 : [1, 3], 1 : [2], 2 : [3], 4 : [5, 6], 5 : [6] } ) - sage: D.connected_components() + sage: D.connected_components(sort=True) [[0, 1, 2, 3], [4, 5, 6]] sage: D = DiGraph( { 0 : [1, 3], 1 : [2], 2 : [3], 4 : [5, 6], 5 : [6] } ) sage: D.strongly_connected_components() diff --git a/src/sage/graphs/bipartite_graph.py b/src/sage/graphs/bipartite_graph.py index 86b6f4bba15..635d5554329 100644 --- a/src/sage/graphs/bipartite_graph.py +++ b/src/sage/graphs/bipartite_graph.py @@ -1209,7 +1209,7 @@ def _check_bipartition_for_add_edges(self, edges): vertex_in_left[v] = False # Map each vertex to the connected component it belongs to - vertex_to_component = {v: comp for comp in self.connected_components() + vertex_to_component = {v: comp for comp in self.connected_components(sort=False) for v in comp} for e in edges: diff --git a/src/sage/graphs/bliss_cpp/bliss_find_automorphisms.h b/src/sage/graphs/bliss_cpp/bliss_find_automorphisms.h index a2c084a163e..b87a14e7a8c 100644 --- a/src/sage/graphs/bliss_cpp/bliss_find_automorphisms.h +++ b/src/sage/graphs/bliss_cpp/bliss_find_automorphisms.h @@ -1,3 +1,5 @@ +/* sage_setup: distribution = sagemath-bliss */ + #include #include diff --git a/src/sage/graphs/comparability.pyx b/src/sage/graphs/comparability.pyx index 0271f43d9d8..b694b552ee8 100644 --- a/src/sage/graphs/comparability.pyx +++ b/src/sage/graphs/comparability.pyx @@ -257,7 +257,7 @@ def greedy_is_comparability(g, no_certificate=False, equivalence_class=False): # Each vertex can partition its neighbors into equivalence classes equivalence_classes = {} for v in g: - equivalence_classes[v] = g.subgraph(vertices=g.neighbors(v)).complement().connected_components() + equivalence_classes[v] = g.subgraph(vertices=g.neighbors(v)).complement().connected_components(sort=False) # We build a graph h with one vertex per (vertex of g + equivalence class) from sage.graphs.graph import Graph @@ -288,7 +288,7 @@ def greedy_is_comparability(g, no_certificate=False, equivalence_class=False): if equivalence_class: # Returning the largest equivalence class - cc = sorted(h.connected_components(), key=len)[-1] + cc = sorted(h.connected_components(sort=False), key=len)[-1] edges = [] for v, sid in cc: diff --git a/src/sage/graphs/connectivity.pyx b/src/sage/graphs/connectivity.pyx index 0cbcb87dd8d..c2100141f30 100644 --- a/src/sage/graphs/connectivity.pyx +++ b/src/sage/graphs/connectivity.pyx @@ -58,6 +58,18 @@ Methods ------- """ +# **************************************************************************** +# +# Copyright (C) 2023 David Coudert +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.misc.superseded import deprecation def is_connected(G): """ @@ -119,7 +131,7 @@ def is_connected(G): return len(conn_verts) == G.num_verts() -def connected_components(G, sort=True): +def connected_components(G, sort=None, key=None): """ Return the list of connected components. @@ -130,20 +142,30 @@ def connected_components(G, sort=True): - ``G`` -- the input graph - - ``sort`` -- boolean (default ``True``); whether to sort vertices inside - each component + - ``sort`` -- boolean (default: ``None``); if ``True``, vertices inside each + component are sorted according to the default ordering + + As of :trac:`35889`, this argument must be explicitly specified (unless a + ``key`` is given); otherwise a warning is printed and ``sort=True`` is + used. The default will eventually be changed to ``False``. + + - ``key`` -- a function (default: ``None``); a function that takes a + vertex as its one argument and returns a value that can be used for + comparisons in the sorting algorithm (we must have ``sort=True``) EXAMPLES:: sage: from sage.graphs.connectivity import connected_components sage: G = Graph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]}) - sage: connected_components(G) + sage: connected_components(G, sort=True) [[0, 1, 2, 3], [4, 5, 6]] - sage: G.connected_components() + sage: G.connected_components(sort=True) [[0, 1, 2, 3], [4, 5, 6]] sage: D = DiGraph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]}) - sage: connected_components(D) + sage: connected_components(D, sort=True) [[0, 1, 2, 3], [4, 5, 6]] + sage: connected_components(D, sort=True, key=lambda x: -x) + [[3, 2, 1, 0], [6, 5, 4]] TESTS: @@ -154,16 +176,40 @@ def connected_components(G, sort=True): Traceback (most recent call last): ... TypeError: the input must be a Sage graph + + When parameter ``key`` is set, parameter ``sort`` must be ``True``:: + + sage: G = Graph(2) + sage: G.connected_components(sort=False, key=lambda x: x) + Traceback (most recent call last): + ... + ValueError: sort keyword is False, yet a key function is given + + Deprecation warning for ``sort=None`` (:trac:`35889`):: + + sage: G = graphs.HouseGraph() + sage: G.connected_components() + doctest:...: DeprecationWarning: parameter 'sort' will be set to False by default in the future + See https://github.com/sagemath/sage/issues/35889 for details. + [[0, 1, 2, 3, 4]] """ from sage.graphs.generic_graph import GenericGraph if not isinstance(G, GenericGraph): raise TypeError("the input must be a Sage graph") + if sort is None: + if key is None: + deprecation(35889, "parameter 'sort' will be set to False by default in the future") + sort = True + + if (not sort) and key: + raise ValueError('sort keyword is False, yet a key function is given') + cdef set seen = set() cdef list components = [] for v in G: if v not in seen: - c = connected_component_containing_vertex(G, v, sort=sort) + c = connected_component_containing_vertex(G, v, sort=sort, key=key) seen.update(c) components.append(c) components.sort(key=lambda comp: -len(comp)) @@ -236,7 +282,7 @@ def connected_components_subgraphs(G): return [G.subgraph(c, inplace=False) for c in connected_components(G, sort=False)] -def connected_component_containing_vertex(G, vertex, sort=True): +def connected_component_containing_vertex(G, vertex, sort=None, key=None): """ Return a list of the vertices connected to vertex. @@ -246,20 +292,30 @@ def connected_component_containing_vertex(G, vertex, sort=True): - ``v`` -- the vertex to search for - - ``sort`` -- boolean (default ``True``); whether to sort vertices inside - the component + - ``sort`` -- boolean (default: ``None``); if ``True``, vertices inside the + component are sorted according to the default ordering + + As of :trac:`35889`, this argument must be explicitly specified (unless a + ``key`` is given); otherwise a warning is printed and ``sort=True`` is + used. The default will eventually be changed to ``False``. + + - ``key`` -- a function (default: ``None``); a function that takes a + vertex as its one argument and returns a value that can be used for + comparisons in the sorting algorithm (we must have ``sort=True``) EXAMPLES:: sage: from sage.graphs.connectivity import connected_component_containing_vertex sage: G = Graph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]}) - sage: connected_component_containing_vertex(G, 0) + sage: connected_component_containing_vertex(G, 0, sort=True) [0, 1, 2, 3] - sage: G.connected_component_containing_vertex(0) + sage: G.connected_component_containing_vertex(0, sort=True) [0, 1, 2, 3] sage: D = DiGraph({0: [1, 3], 1: [2], 2: [3], 4: [5, 6], 5: [6]}) - sage: connected_component_containing_vertex(D, 0) + sage: connected_component_containing_vertex(D, 0, sort=True) [0, 1, 2, 3] + sage: connected_component_containing_vertex(D, 0, sort=True, key=lambda x: -x) + [3, 2, 1, 0] TESTS: @@ -270,18 +326,52 @@ def connected_component_containing_vertex(G, vertex, sort=True): Traceback (most recent call last): ... TypeError: the input must be a Sage graph + + :trac:`35889` is fixed:: + + sage: G = Graph([('A', 1)]) + sage: G.connected_component_containing_vertex(1, sort=False) + [1, 'A'] + sage: G.connected_component_containing_vertex(1, sort=True) + Traceback (most recent call last): + ... + TypeError: '<' not supported between instances of 'str' and 'int' + + When parameter ``key`` is set, parameter ``sort`` must be ``True``:: + + sage: G = Graph(2) + sage: G.connected_component_containing_vertex(1, sort=False, key=lambda x: x) + Traceback (most recent call last): + ... + ValueError: sort keyword is False, yet a key function is given + + Deprecation warning for ``sort=None`` (:trac:`35889`):: + + sage: G = graphs.HouseGraph() + sage: G.connected_component_containing_vertex(1) + doctest:...: DeprecationWarning: parameter 'sort' will be set to False by default in the future + See https://github.com/sagemath/sage/issues/35889 for details. + [0, 1, 2, 3, 4] """ from sage.graphs.generic_graph import GenericGraph if not isinstance(G, GenericGraph): raise TypeError("the input must be a Sage graph") + if sort is None: + if key is None: + deprecation(35889, "parameter 'sort' will be set to False by default in the future") + sort = True + + if (not sort) and key: + raise ValueError('sort keyword is False, yet a key function is given') + try: c = list(G._backend.depth_first_search(vertex, ignore_direction=True)) except AttributeError: c = list(G.depth_first_search(vertex, ignore_direction=True)) if sort: - c.sort() + return sorted(c, key=key) return c @@ -325,7 +415,7 @@ def connected_components_sizes(G): return [len(cc) for cc in connected_components(G, sort=False)] -def blocks_and_cut_vertices(G, algorithm="Tarjan_Boost", sort=False): +def blocks_and_cut_vertices(G, algorithm="Tarjan_Boost", sort=False, key=None): """ Return the blocks and cut vertices of the graph. @@ -351,6 +441,10 @@ def blocks_and_cut_vertices(G, algorithm="Tarjan_Boost", sort=False): the components and the list of cut vertices **currently only available for ``"Tarjan_Sage"``** + - ``key`` -- a function (default: ``None``); a function that takes a + vertex as its one argument and returns a value that can be used for + comparisons in the sorting algorithm (we must have ``sort=True``) + OUTPUT: ``(B, C)``, where ``B`` is a list of blocks - each is a list of vertices and the blocks are the corresponding induced subgraphs - and ``C`` is a list of cut vertices. @@ -439,6 +533,9 @@ def blocks_and_cut_vertices(G, algorithm="Tarjan_Boost", sort=False): raise NotImplementedError("blocks and cut vertices algorithm '%s' is not implemented" % algorithm) # If algorithm is "Tarjan_Sage" + if (not sort) and key: + raise ValueError('sort keyword is False, yet a key function is given') + blocks = [] cut_vertices = set() @@ -531,7 +628,7 @@ def blocks_and_cut_vertices(G, algorithm="Tarjan_Boost", sort=False): u1, u2 = edge_stack.pop() new_block.add(u1) if sort: - this_block = sorted(new_block) + this_block = sorted(new_block, key=key) else: this_block = list(new_block) blocks.append(this_block) @@ -546,9 +643,8 @@ def blocks_and_cut_vertices(G, algorithm="Tarjan_Boost", sort=False): start_already_seen = True if sort: - return blocks, sorted(cut_vertices) - else: - return blocks, list(cut_vertices) + return blocks, sorted(cut_vertices, key=key) + return blocks, list(cut_vertices) def blocks_and_cuts_tree(G): @@ -1042,7 +1138,9 @@ def edge_connectivity(G, sage: g = graphs.PetersenGraph() sage: edge_connectivity((2 * g), vertices=True) - [0, [], [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19]]] + [0, [], [{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}]] + sage: edge_connectivity(Graph(), vertices=True) + [0, [], [{}, {}]] If ``G`` is not a Sage graph, an error is raised:: @@ -1078,7 +1176,7 @@ def edge_connectivity(G, if value_only: return 0 elif vertices: - return [0, [], [[], []]] + return [0, [], [{}, {}]] else: return [0, []] @@ -1101,9 +1199,9 @@ def edge_connectivity(G, b = set(H).difference(a) val.append([a, b]) else: - val.append(connected_components(H)) + val.append([set(c) for c in connected_components(H, sort=False)]) elif vertices: - val.append(connected_components(G)) + val.append([set(c) for c in connected_components(G, sort=False)]) return val @@ -1190,13 +1288,13 @@ def edge_connectivity(G, val.append(edges) if vertices: - a = [] - b = [] + a = {} + b = {} for v in g: if in_set[0, v]: - a.append(v) + a.add(v) else: - b.append(v) + b.add(v) val.append([a, b]) return val @@ -2125,7 +2223,7 @@ def cleave(G, cut_vertices=None, virtual_edges=True, solver=None, verbose=0, H = G.copy(immutable=False) H.delete_vertices(cut_vertices) - CC = H.connected_components() + CC = H.connected_components(sort=False) if len(CC) == 1: raise ValueError("the set cut_vertices is not a vertex cut of the graph") diff --git a/src/sage/graphs/distances_all_pairs.pyx b/src/sage/graphs/distances_all_pairs.pyx index 2451de506fd..46baaf8ca73 100644 --- a/src/sage/graphs/distances_all_pairs.pyx +++ b/src/sage/graphs/distances_all_pairs.pyx @@ -2572,7 +2572,7 @@ def antipodal_graph(G): if not G.is_connected(): import itertools - CC = G.connected_components() + CC = G.connected_components(sort=False) for c1, c2 in itertools.combinations(CC, 2): A.add_edges(itertools.product(c1, c2)) return A diff --git a/src/sage/graphs/generators/world_map.py b/src/sage/graphs/generators/world_map.py index 42d0a95372f..76c32335f60 100644 --- a/src/sage/graphs/generators/world_map.py +++ b/src/sage/graphs/generators/world_map.py @@ -95,7 +95,7 @@ def AfricaMap(continental=False, year=2018): G = Graph(common_border, format='dict_of_lists') if continental: - G = G.subgraph(G.connected_component_containing_vertex('Central Africa')) + G = G.subgraph(G.connected_component_containing_vertex('Central Africa', sort=False)) G.name(new="Continental Africa Map") else: G.add_vertices(no_land_border) @@ -172,7 +172,7 @@ def EuropeMap(continental=False, year=2018): G = Graph(common_border, format='dict_of_lists') if continental: - G = G.subgraph(G.connected_component_containing_vertex('Austria')) + G = G.subgraph(G.connected_component_containing_vertex('Austria', sort=False)) G.name(new="Continental Europe Map") else: G.add_vertices(no_land_border) @@ -308,7 +308,7 @@ def WorldMap(): True sage: g.gps_coordinates["Bolivia"] [[17, 'S'], [65, 'W']] - sage: sorted(g.connected_component_containing_vertex('Ireland')) + sage: g.connected_component_containing_vertex('Ireland', sort=True) ['Ireland', 'United Kingdom'] TESTS: diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 2f5659e94e6..a083ed43e5e 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -909,7 +909,7 @@ def _latex_(self): sage: from sage.graphs.graph_latex import check_tkz_graph sage: check_tkz_graph() # random - depends on TeX installation sage: g = graphs.CompleteGraph(2) - sage: print(g._latex_()) # optional - sage.plot + sage: print(g._latex_()) # needs sage.plot \begin{tikzpicture} \definecolor{cv0}{rgb}{0.0,0.0,0.0} \definecolor{cfv0}{rgb}{1.0,1.0,1.0} @@ -946,21 +946,21 @@ def _matrix_(self, R=None, vertices=None): EXAMPLES:: sage: G = graphs.CompleteBipartiteGraph(2, 3) - sage: m = matrix(G); m.parent() # optional - sage.modules + sage: m = matrix(G); m.parent() # needs sage.modules Full MatrixSpace of 5 by 5 dense matrices over Integer Ring - sage: m # optional - sage.modules + sage: m # needs sage.modules [0 0 1 1 1] [0 0 1 1 1] [1 1 0 0 0] [1 1 0 0 0] [1 1 0 0 0] - sage: G._matrix_() # optional - sage.modules + sage: G._matrix_() # needs sage.modules [0 0 1 1 1] [0 0 1 1 1] [1 1 0 0 0] [1 1 0 0 0] [1 1 0 0 0] - sage: factor(m.charpoly()) # optional - sage.modules + sage: factor(m.charpoly()) # needs sage.modules x^3 * (x^2 - 6) """ return self.am(vertices=vertices, base_ring=R) @@ -1369,24 +1369,24 @@ def export_to_file(self, filename, format=None, **kwds): sage: g = graphs.PetersenGraph() sage: filename = tmp_filename(ext=".pajek") - sage: g.export_to_file(filename) # optional - networkx - sage: import networkx # optional - networkx - sage: G_networkx = networkx.read_pajek(filename) # optional - networkx - sage: Graph(G_networkx).is_isomorphic(g) # optional - networkx + sage: g.export_to_file(filename) # needs networkx + sage: import networkx # needs networkx + sage: G_networkx = networkx.read_pajek(filename) # needs networkx + sage: Graph(G_networkx).is_isomorphic(g) # needs networkx True sage: filename = tmp_filename(ext=".edgelist") - sage: g.export_to_file(filename, data=False) # optional - networkx - sage: h = Graph(networkx.read_edgelist(filename)) # optional - networkx - sage: g.is_isomorphic(h) # optional - networkx + sage: g.export_to_file(filename, data=False) # needs networkx + sage: h = Graph(networkx.read_edgelist(filename)) # needs networkx + sage: g.is_isomorphic(h) # needs networkx True TESTS:: - sage: g.export_to_file("hey", format="When I feel heavy metaaaaaallll...") # optional - networkx + sage: g.export_to_file("hey", format="When I feel heavy metaaaaaallll...") # needs networkx Traceback (most recent call last): ... ValueError: format 'When I feel heavy metaaaaaallll...' unknown - sage: g.export_to_file("my_file.Yeeeeppeeeeee") # optional - networkx + sage: g.export_to_file("my_file.Yeeeeppeeeeee") # needs networkx Traceback (most recent call last): ... RuntimeError: the file format could not be guessed from 'my_file.Yeeeeppeeeeee' @@ -1508,21 +1508,21 @@ def networkx_graph(self, weight_function=None): EXAMPLES:: sage: G = graphs.TetrahedralGraph() - sage: N = G.networkx_graph() # optional - networkx - sage: type(N) # optional - networkx + sage: N = G.networkx_graph() # needs networkx + sage: type(N) # needs networkx sage: def weight_fn(e): ....: return e[2] sage: G1 = Graph([(1,2,1), (1,3,4), (2,3,3), (3,4,4)]) - sage: H = G1.networkx_graph(weight_function=weight_fn) # optional - networkx - sage: H.edges(data=True) # optional - networkx + sage: H = G1.networkx_graph(weight_function=weight_fn) # needs networkx + sage: H.edges(data=True) # needs networkx EdgeDataView([(1, 2, {'weight': 1}), (1, 3, {'weight': 4}), (2, 3, {'weight': 3}), (3, 4, {'weight': 4})]) sage: G2 = DiGraph([(1,2,1), (1,3,4), (2,3,3), (3,4,4), (3,4,5)], ....: multiedges=True) - sage: H = G2.networkx_graph(weight_function=weight_fn) # optional - networkx - sage: H.edges(data=True) # optional - networkx + sage: H = G2.networkx_graph(weight_function=weight_fn) # needs networkx + sage: H.edges(data=True) # needs networkx OutMultiEdgeDataView([(1, 2, {'weight': 1}), (1, 3, {'weight': 4}), (2, 3, {'weight': 3}), (3, 4, {'weight': 5}), (3, 4, {'weight': 4})]) @@ -1912,7 +1912,7 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds EXAMPLES:: sage: G = graphs.CubeGraph(4) - sage: G.adjacency_matrix() # optional - sage.modules + sage: G.adjacency_matrix() # needs sage.modules [0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0] [1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0] [1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0] @@ -1932,7 +1932,7 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds :: - sage: matrix(GF(2), G) # matrix over GF(2) # optional - sage.modules sage.rings.finite_rings + sage: matrix(GF(2), G) # matrix over GF(2) # needs sage.modules sage.rings.finite_rings [0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0] [1 0 0 1 0 1 0 0 0 1 0 0 0 0 0 0] [1 0 0 1 0 0 1 0 0 0 1 0 0 0 0 0] @@ -1954,7 +1954,7 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds sage: D = DiGraph({0: [1, 2, 3], 1: [0, 2], 2: [3], ....: 3: [4], 4: [0, 5], 5: [1]}) - sage: D.adjacency_matrix() # optional - sage.modules + sage: D.adjacency_matrix() # needs sage.modules [0 1 1 1 0 0] [1 0 1 0 0 0] [0 0 0 1 0 0] @@ -1964,7 +1964,7 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds A different ordering of the vertices:: - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[2, 4, 1, 3, 0]) # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[2, 4, 1, 3, 0]) # needs sage.modules [0 0 1 1 0] [0 0 0 1 0] [1 0 0 0 1] @@ -1973,18 +1973,18 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds A different base ring:: - sage: graphs.PathGraph(5).adjacency_matrix(base_ring=RDF) # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(base_ring=RDF) # needs sage.modules [0.0 1.0 0.0 0.0 0.0] [1.0 0.0 1.0 0.0 0.0] [0.0 1.0 0.0 1.0 0.0] [0.0 0.0 1.0 0.0 1.0] [0.0 0.0 0.0 1.0 0.0] - sage: type(_) # optional - sage.modules + sage: type(_) # needs sage.modules A different matrix implementation:: - sage: graphs.PathGraph(5).adjacency_matrix(sparse=False, # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(sparse=False, # needs sage.modules ....: implementation='numpy') [0 1 0 0 0] [1 0 1 0 0] @@ -1996,14 +1996,14 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds As an immutable matrix:: - sage: M = graphs.PathGraph(5).adjacency_matrix(sparse=False, # optional - sage.modules + sage: M = graphs.PathGraph(5).adjacency_matrix(sparse=False, # needs sage.modules ....: immutable=True); M [0 1 0 0 0] [1 0 1 0 0] [0 1 0 1 0] [0 0 1 0 1] [0 0 0 1 0] - sage: M[2, 2] = 1 + sage: M[2, 2] = 1 # needs sage.modules Traceback (most recent call last): ... ValueError: matrix is immutable; please change a copy instead @@ -2011,18 +2011,18 @@ def adjacency_matrix(self, sparse=None, vertices=None, *, base_ring=None, **kwds TESTS:: - sage: graphs.CubeGraph(8).adjacency_matrix().parent() # optional - sage.modules + sage: graphs.CubeGraph(8).adjacency_matrix().parent() # needs sage.modules Full MatrixSpace of 256 by 256 dense matrices over Integer Ring - sage: graphs.CubeGraph(9).adjacency_matrix().parent() # optional - sage.modules + sage: graphs.CubeGraph(9).adjacency_matrix().parent() # needs sage.modules Full MatrixSpace of 512 by 512 sparse matrices over Integer Ring - sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], # optional - sage.modules + sage: Graph([(i, i+1) for i in range(500)] + [(0,1),], # needs sage.modules ....: multiedges=True).adjacency_matrix().parent() Full MatrixSpace of 501 by 501 dense matrices over Integer Ring - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[0,0,0,0,0]) # needs sage.modules Traceback (most recent call last): ... ValueError: ``vertices`` must be a permutation of the vertices - sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) # optional - sage.modules + sage: graphs.PathGraph(5).adjacency_matrix(vertices=[1,2,3]) # needs sage.modules Traceback (most recent call last): ... ValueError: ``vertices`` must be a permutation of the vertices @@ -2129,7 +2129,7 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None EXAMPLES:: sage: G = graphs.PetersenGraph() - sage: G.incidence_matrix() # optional - sage.modules + sage: G.incidence_matrix() # needs sage.modules [1 1 1 0 0 0 0 0 0 0 0 0 0 0 0] [1 0 0 1 1 0 0 0 0 0 0 0 0 0 0] [0 0 0 1 0 1 1 0 0 0 0 0 0 0 0] @@ -2140,7 +2140,7 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None [0 0 0 0 0 0 1 0 0 0 1 0 0 0 1] [0 0 0 0 0 0 0 0 1 0 0 1 1 0 0] [0 0 0 0 0 0 0 0 0 1 0 0 0 1 1] - sage: G.incidence_matrix(oriented=True) # optional - sage.modules + sage: G.incidence_matrix(oriented=True) # needs sage.modules [-1 -1 -1 0 0 0 0 0 0 0 0 0 0 0 0] [ 1 0 0 -1 -1 0 0 0 0 0 0 0 0 0 0] [ 0 0 0 1 0 -1 -1 0 0 0 0 0 0 0 0] @@ -2153,18 +2153,18 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None [ 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1] sage: G = digraphs.Circulant(4, [1, 3]) - sage: G.incidence_matrix() # optional - sage.modules + sage: G.incidence_matrix() # needs sage.modules [-1 -1 1 0 0 0 1 0] [ 1 0 -1 -1 1 0 0 0] [ 0 0 0 1 -1 -1 0 1] [ 0 1 0 0 0 1 -1 -1] - sage: graphs.CompleteGraph(3).incidence_matrix() # optional - sage.modules + sage: graphs.CompleteGraph(3).incidence_matrix() # needs sage.modules [1 1 0] [1 0 1] [0 1 1] sage: G = Graph([(0, 0), (0, 1), (0, 1)], loops=True, multiedges=True) - sage: G.incidence_matrix(oriented=False) # optional - sage.modules + sage: G.incidence_matrix(oriented=False) # needs sage.modules [2 1 1] [0 1 1] @@ -2173,30 +2173,30 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None Kirchhoff matrix:: sage: G = graphs.PetersenGraph() - sage: m = G.incidence_matrix(oriented=True) # optional - sage.modules - sage: m * m.transpose() == G.kirchhoff_matrix() # optional - sage.modules + sage: m = G.incidence_matrix(oriented=True) # needs sage.modules + sage: m * m.transpose() == G.kirchhoff_matrix() # needs sage.modules True sage: K = graphs.CompleteGraph(3) - sage: m = K.incidence_matrix(oriented=True) # optional - sage.modules - sage: m * m.transpose() == K.kirchhoff_matrix() # optional - sage.modules + sage: m = K.incidence_matrix(oriented=True) # needs sage.modules + sage: m * m.transpose() == K.kirchhoff_matrix() # needs sage.modules True sage: H = Graph([(0, 0), (0, 1), (0, 1)], loops=True, multiedges=True) - sage: m = H.incidence_matrix(oriented=True) # optional - sage.modules - sage: m * m.transpose() == H.kirchhoff_matrix() # optional - sage.modules + sage: m = H.incidence_matrix(oriented=True) # needs sage.modules + sage: m * m.transpose() == H.kirchhoff_matrix() # needs sage.modules True A different ordering of the vertices:: sage: P5 = graphs.PathGraph(5) - sage: P5.incidence_matrix() # optional - sage.modules + sage: P5.incidence_matrix() # needs sage.modules [1 0 0 0] [1 1 0 0] [0 1 1 0] [0 0 1 1] [0 0 0 1] - sage: P5.incidence_matrix(vertices=[2, 4, 1, 3, 0]) # optional - sage.modules + sage: P5.incidence_matrix(vertices=[2, 4, 1, 3, 0]) # needs sage.modules [0 1 1 0] [0 0 0 1] [1 1 0 0] @@ -2206,13 +2206,13 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None A different ordering of the edges:: sage: E = list(P5.edge_iterator(labels=False)) - sage: P5.incidence_matrix(edges=E[::-1]) # optional - sage.modules + sage: P5.incidence_matrix(edges=E[::-1]) # needs sage.modules [0 0 0 1] [0 0 1 1] [0 1 1 0] [1 1 0 0] [1 0 0 0] - sage: P5.incidence_matrix(vertices=[2, 4, 1, 3, 0], edges=E[::-1]) # optional - sage.modules + sage: P5.incidence_matrix(vertices=[2, 4, 1, 3, 0], edges=E[::-1]) # needs sage.modules [0 1 1 0] [1 0 0 0] [0 0 1 1] @@ -2221,7 +2221,7 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None A different base ring:: - sage: P5.incidence_matrix(base_ring=RDF) # optional - sage.modules + sage: P5.incidence_matrix(base_ring=RDF) # needs sage.modules [1.0 0.0 0.0 0.0] [1.0 1.0 0.0 0.0] [0.0 1.0 1.0 0.0] @@ -2230,13 +2230,13 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None Creating an immutable matrix:: - sage: m = P5.incidence_matrix(immutable=True); m # optional - sage.modules + sage: m = P5.incidence_matrix(immutable=True); m # needs sage.modules [1 0 0 0] [1 1 0 0] [0 1 1 0] [0 0 1 1] [0 0 0 1] - sage: m[1,2] = 1 # optional - sage.modules + sage: m[1,2] = 1 # needs sage.modules Traceback (most recent call last): ... ValueError: matrix is immutable; please change a copy instead @@ -2245,15 +2245,15 @@ def incidence_matrix(self, oriented=None, sparse=True, vertices=None, edges=None TESTS:: sage: P5 = graphs.PathGraph(5) - sage: P5.incidence_matrix(vertices=[1] * P5.order()) # optional - sage.modules + sage: P5.incidence_matrix(vertices=[1] * P5.order()) # needs sage.modules Traceback (most recent call last): ... ValueError: ``vertices`` must be a permutation of the vertices - sage: P5.incidence_matrix(edges=[(0, 1)] * P5.size()) # optional - sage.modules + sage: P5.incidence_matrix(edges=[(0, 1)] * P5.size()) # needs sage.modules Traceback (most recent call last): ... ValueError: ``edges`` must be a permutation of the edges - sage: P5.incidence_matrix(edges=P5.edges(sort=False, labels=True)) # optional - sage.modules + sage: P5.incidence_matrix(edges=P5.edges(sort=False, labels=True)) # needs sage.modules [1 0 0 0] [1 1 0 0] [0 1 1 0] @@ -2342,19 +2342,19 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): EXAMPLES:: sage: d = DiGraph({1: [2, 3], 2: [3], 3: [4], 4: [1]}) - sage: d.distance_matrix() # optional - sage.modules + sage: d.distance_matrix() # needs sage.modules [0 1 1 2] [3 0 1 2] [2 3 0 1] [1 2 2 0] - sage: d.distance_matrix(vertices=[4, 3, 2, 1]) # optional - sage.modules + sage: d.distance_matrix(vertices=[4, 3, 2, 1]) # needs sage.modules [0 2 2 1] [1 0 3 2] [2 1 0 3] [2 1 1 0] sage: G = graphs.CubeGraph(3) - sage: G.distance_matrix() # optional - sage.modules + sage: G.distance_matrix() # needs sage.modules [0 1 1 2 1 2 2 3] [1 0 2 1 2 1 3 2] [1 2 0 1 2 3 1 2] @@ -2368,7 +2368,7 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): of the distance matrix of any tree of order `n` is `(-1)^{n-1}(n-1)2^{n-2}`:: - sage: all(T.distance_matrix().det() == (-1)^9*(9)*2^8 # optional - sage.modules + sage: all(T.distance_matrix().det() == (-1)^9*(9)*2^8 # needs sage.modules ....: for T in graphs.trees(10)) True @@ -2382,18 +2382,18 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): Asking for an immutable matrix:: sage: G = Graph([(0, 1)]) - sage: G.distance_matrix().is_immutable() # optional - sage.modules + sage: G.distance_matrix().is_immutable() # needs sage.modules False - sage: G.distance_matrix(immutable=True).is_immutable() # optional - sage.modules + sage: G.distance_matrix(immutable=True).is_immutable() # needs sage.modules True Specifying a base ring:: sage: G = Graph([(0, 1)]) - sage: G.distance_matrix(vertices=[0, 1], base_ring=ZZ) # optional - sage.modules + sage: G.distance_matrix(vertices=[0, 1], base_ring=ZZ) # needs sage.modules [0 1] [1 0] - sage: G.distance_matrix(vertices=[0, 1], base_ring=RDF) # optional - sage.modules + sage: G.distance_matrix(vertices=[0, 1], base_ring=RDF) # needs sage.modules [0.0 1.0] [1.0 0.0] @@ -2401,7 +2401,7 @@ def distance_matrix(self, vertices=None, *, base_ring=None, **kwds): constructor:: sage: G = Graph([(0, 1)]) - sage: G.distance_matrix(vertices=[0, 1], weight_function=lambda e:2) # optional - sage.modules + sage: G.distance_matrix(vertices=[0, 1], weight_function=lambda e:2) # needs sage.modules [0 2] [2 0] """ @@ -2479,15 +2479,15 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, sage: G = Graph(sparse=True, weighted=True) sage: G.add_edges([(0, 1, 1), (1, 2, 2), (0, 2, 3), (0, 3, 4)]) - sage: M = G.weighted_adjacency_matrix(); M # optional - sage.modules + sage: M = G.weighted_adjacency_matrix(); M # needs sage.modules [0 1 3 4] [1 0 2 0] [3 2 0 0] [4 0 0 0] - sage: H = Graph(data=M, format='weighted_adjacency_matrix', sparse=True) # optional - sage.modules - sage: H == G # optional - sage.modules + sage: H = Graph(data=M, format='weighted_adjacency_matrix', sparse=True) # needs sage.modules + sage: H == G # needs sage.modules True - sage: G.weighted_adjacency_matrix(vertices=[3, 2, 1, 0]) # optional - sage.modules + sage: G.weighted_adjacency_matrix(vertices=[3, 2, 1, 0]) # needs sage.modules [0 0 0 4] [0 0 2 3] [0 2 0 1] @@ -2495,7 +2495,7 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, Using a different matrix implementation:: - sage: M = G.weighted_adjacency_matrix(sparse=False, base_ring=ZZ, # optional - numpy sage.modules + sage: M = G.weighted_adjacency_matrix(sparse=False, base_ring=ZZ, # needs numpy sage.modules ....: implementation='numpy'); M [0 1 3 4] [1 0 2 0] @@ -2504,12 +2504,12 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, As an immutable matrix:: - sage: M = G.weighted_adjacency_matrix(immutable=True); M # optional - sage.modules + sage: M = G.weighted_adjacency_matrix(immutable=True); M # needs sage.modules [0 1 3 4] [1 0 2 0] [3 2 0 0] [4 0 0 0] - sage: M[2, 2] = 1 # optional - sage.modules + sage: M[2, 2] = 1 # needs sage.modules Traceback (most recent call last): ... ValueError: matrix is immutable; please change a copy instead @@ -2520,7 +2520,7 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, The following doctest verifies that :trac:`4888` is fixed:: sage: G = DiGraph({0:{}, 1:{0:1}, 2:{0:1}}, weighted=True, sparse=True) - sage: G.weighted_adjacency_matrix() # optional - sage.modules + sage: G.weighted_adjacency_matrix() # needs sage.modules [0 0 0] [1 0 0] [1 0 0] @@ -2528,16 +2528,16 @@ def weighted_adjacency_matrix(self, sparse=True, vertices=None, Check error message for non numerical edge weights (:trac:`33562`):: sage: G = Graph([(0, 1)]) - sage: G.weighted_adjacency_matrix() # optional - sage.modules + sage: G.weighted_adjacency_matrix() # needs sage.modules Traceback (most recent call last): ... ValueError: cannot find the weight of (0, 1, None). Consider setting parameter 'default_weight' - sage: G.weighted_adjacency_matrix(default_weight=3) # optional - sage.modules + sage: G.weighted_adjacency_matrix(default_weight=3) # needs sage.modules [0 3] [3 0] sage: G = Graph([(0, 1, 'a')]) - sage: G.weighted_adjacency_matrix() # optional - sage.modules + sage: G.weighted_adjacency_matrix() # needs sage.modules Traceback (most recent call last): ... TypeError: Cannot convert NoneType to sage.structure.parent.Parent @@ -2656,33 +2656,33 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl sage: G = Graph(sparse=True) sage: G.add_edges([(0, 1, 1), (1, 2, 2), (0, 2, 3), (0, 3, 4)]) - sage: M = G.kirchhoff_matrix(weighted=True); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(weighted=True); M # needs sage.modules [ 8 -1 -3 -4] [-1 3 -2 0] [-3 -2 5 0] [-4 0 0 4] - sage: M = G.kirchhoff_matrix(); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(); M # needs sage.modules [ 3 -1 -1 -1] [-1 2 -1 0] [-1 -1 2 0] [-1 0 0 1] - sage: M = G.laplacian_matrix(normalized=True); M # optional - sage.modules sage.symbolic + sage: M = G.laplacian_matrix(normalized=True); M # needs sage.modules sage.symbolic [ 1 -1/6*sqrt(3)*sqrt(2) -1/6*sqrt(3)*sqrt(2) -1/3*sqrt(3)] [-1/6*sqrt(3)*sqrt(2) 1 -1/2 0] [-1/6*sqrt(3)*sqrt(2) -1/2 1 0] [ -1/3*sqrt(3) 0 0 1] - sage: M = G.kirchhoff_matrix(weighted=True, signless=True); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(weighted=True, signless=True); M # needs sage.modules [8 1 3 4] [1 3 2 0] [3 2 5 0] [4 0 0 4] sage: G = Graph({0: [], 1: [2]}) - sage: G.laplacian_matrix(normalized=True) # optional - sage.modules + sage: G.laplacian_matrix(normalized=True) # needs sage.modules [ 0 0 0] [ 0 1 -1] [ 0 -1 1] - sage: G.laplacian_matrix(normalized=True, signless=True) # optional - sage.modules + sage: G.laplacian_matrix(normalized=True, signless=True) # needs sage.modules [0 0 0] [0 1 1] [0 1 1] @@ -2690,14 +2690,14 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl A weighted directed graph with loops, changing the variable ``indegree`` :: sage: G = DiGraph({1: {1: 2, 2: 3}, 2: {1: 4}}, weighted=True, sparse=True) - sage: G.laplacian_matrix() # optional - sage.modules + sage: G.laplacian_matrix() # needs sage.modules [ 4 -3] [-4 3] :: sage: G = DiGraph({1: {1: 2, 2: 3}, 2: {1: 4}}, weighted=True, sparse=True) - sage: G.laplacian_matrix(indegree=False) # optional - sage.modules + sage: G.laplacian_matrix(indegree=False) # needs sage.modules [ 3 -3] [-4 4] @@ -2706,12 +2706,12 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl sage: G = Graph(sparse=True) sage: G.add_edges([(0, 1, 1), (1, 2, 2), (0, 2, 3), (0, 3, 4)]) - sage: M = G.kirchhoff_matrix(vertices=[3, 2, 1, 0]); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(vertices=[3, 2, 1, 0]); M # needs sage.modules [ 1 0 0 -1] [ 0 2 -1 -1] [ 0 -1 2 -1] [-1 -1 -1 3] - sage: M = G.kirchhoff_matrix(weighted=True, vertices=[3, 2, 1, 0]); M # optional - sage.modules + sage: M = G.kirchhoff_matrix(weighted=True, vertices=[3, 2, 1, 0]); M # needs sage.modules [ 4 0 0 -4] [ 0 5 -2 -3] [ 0 -2 3 -1] @@ -2721,8 +2721,8 @@ def kirchhoff_matrix(self, weighted=None, indegree=True, normalized=False, signl immutable:: sage: G = Graph([(0, 1)]) - sage: M = G.kirchhoff_matrix(vertices=[0, 1], immutable=True) # optional - sage.modules - sage: M.is_immutable() # optional - sage.modules + sage: M = G.kirchhoff_matrix(vertices=[0, 1], immutable=True) # needs sage.modules + sage: M.is_immutable() # needs sage.modules True """ from sage.matrix.constructor import diagonal_matrix @@ -3717,8 +3717,8 @@ def get_pos(self, dim=2): sage: G.get_pos() sage: G.get_pos() is None True - sage: P = G.plot(save_pos=True) # optional - sage.plot - sage: G.get_pos() # optional - sage.plot + sage: P = G.plot(save_pos=True) # needs sage.plot + sage: G.get_pos() # needs sage.plot {} Some of the named graphs come with a pre-specified positioning:: @@ -3843,8 +3843,8 @@ def set_pos(self, pos, dim=2): invalid positioning are ignored:: sage: G.set_pos(dict(enumerate('abcdefghi'))) - sage: P = G.plot() # positions are ignored # optional - sage.plot - sage: G.get_pos() is None # optional - sage.plot + sage: P = G.plot() # positions are ignored # needs sage.plot + sage: G.get_pos() is None # needs sage.plot True """ if pos is None: @@ -3974,21 +3974,21 @@ def antisymmetric(self): A directed acyclic graph is antisymmetric:: - sage: G = digraphs.RandomDirectedGNR(20, 0.5) # optional - networkx - sage: G.antisymmetric() # optional - networkx + sage: G = digraphs.RandomDirectedGNR(20, 0.5) # needs networkx + sage: G.antisymmetric() # needs networkx True Loops are allowed:: - sage: G.allow_loops(True) # optional - networkx - sage: G.add_edge(0, 0) # optional - networkx - sage: G.antisymmetric() # optional - networkx + sage: G.allow_loops(True) # needs networkx + sage: G.add_edge(0, 0) # needs networkx + sage: G.antisymmetric() # needs networkx True An undirected graph is never antisymmetric unless it is just a union of isolated vertices (with possible loops):: - sage: graphs.RandomGNP(20, 0.5).antisymmetric() # optional - networkx + sage: graphs.RandomGNP(20, 0.5).antisymmetric() # needs networkx False sage: Graph(3).antisymmetric() True @@ -4070,7 +4070,7 @@ def is_bipartite(self, certificate=False): True sage: graphs.CycleGraph(5).is_bipartite() False - sage: graphs.RandomBipartite(10, 10, 0.7).is_bipartite() # optional - numpy + sage: graphs.RandomBipartite(10, 10, 0.7).is_bipartite() # needs numpy True A random graph is very rarely bipartite:: @@ -4705,7 +4705,7 @@ def min_spanning_tree(self, NetworkX algorithm:: - sage: sorted(g.min_spanning_tree(algorithm='NetworkX')) # optional - networkx + sage: sorted(g.min_spanning_tree(algorithm='NetworkX')) # needs networkx [(0, 1, None), (0, 2, None), (0, 3, None), (0, 4, None)] More complicated weights:: @@ -4756,7 +4756,7 @@ def min_spanning_tree(self, [(0, 1, 1), (1, 2, 1)] sage: sorted(g.min_spanning_tree(algorithm='Prim_Boost')) [(0, 1, 1), (1, 2, 1)] - sage: sorted(g.min_spanning_tree(algorithm='NetworkX')) # optional - networkx + sage: sorted(g.min_spanning_tree(algorithm='NetworkX')) # needs networkx [(0, 1, 1), (1, 2, 1)] sage: sorted(g.min_spanning_tree(algorithm='Boruvka')) [(0, 1, 1), (1, 2, 1)] @@ -4778,7 +4778,7 @@ def min_spanning_tree(self, [(0, 2, 10), (1, 2, 1)] sage: sorted(g.min_spanning_tree(algorithm='Prim_Boost', weight_function=weight)) [(0, 2, 10), (1, 2, 1)] - sage: sorted(g.min_spanning_tree(algorithm='NetworkX', weight_function=weight)) # optional - networkx + sage: sorted(g.min_spanning_tree(algorithm='NetworkX', weight_function=weight)) # needs networkx [(0, 2, 10), (1, 2, 1)] sage: sorted(g.min_spanning_tree(algorithm='Boruvka', weight_function=weight)) [(0, 2, 10), (1, 2, 1)] @@ -4977,13 +4977,14 @@ def spanning_trees_count(self, root_vertex=None): :: - sage: M = matrix(3, 3, [0, 1, 0, 0, 0, 1, 1, 1, 0]) # optional - sage.modules - sage: D = DiGraph(M) # optional - sage.modules - sage: D.spanning_trees_count() # optional - sage.modules + sage: # needs sage.modules + sage: M = matrix(3, 3, [0, 1, 0, 0, 0, 1, 1, 1, 0]) + sage: D = DiGraph(M) + sage: D.spanning_trees_count() 1 - sage: D.spanning_trees_count(0) # optional - sage.modules + sage: D.spanning_trees_count(0) 1 - sage: D.spanning_trees_count(2) # optional - sage.modules + sage: D.spanning_trees_count(2) 2 """ if not self.order(): @@ -5041,13 +5042,13 @@ def cycle_basis(self, output='vertex'): A cycle basis in Petersen's Graph :: sage: g = graphs.PetersenGraph() - sage: g.cycle_basis() # optional - networkx + sage: g.cycle_basis() # needs networkx [[1, 6, 8, 5, 0], [4, 9, 6, 8, 5, 0], [7, 9, 6, 8, 5], [4, 3, 8, 5, 0], [1, 2, 3, 8, 5, 0], [7, 2, 3, 8, 5]] One can also get the result as a list of lists of edges:: - sage: g.cycle_basis(output='edge') # optional - networkx + sage: g.cycle_basis(output='edge') # needs networkx [[(1, 6, None), (6, 8, None), (8, 5, None), (5, 0, None), (0, 1, None)], [(4, 9, None), (9, 6, None), (6, 8, None), (8, 5, None), (5, 0, None), (0, 4, None)], [(7, 9, None), @@ -5059,17 +5060,17 @@ def cycle_basis(self, output='vertex'): Checking the given cycles are algebraically free:: - sage: g = graphs.RandomGNP(30, .4) # optional - networkx - sage: basis = g.cycle_basis() # optional - networkx + sage: g = graphs.RandomGNP(30, .4) # needs networkx + sage: basis = g.cycle_basis() # needs networkx Building the space of (directed) edges over `Z/2Z`. On the way, building a dictionary associating a unique vector to each undirected edge:: sage: m = g.size() - sage: edge_space = VectorSpace(FiniteField(2), m) # optional - sage.modules sage.rings.finite_rings - sage: edge_vector = dict(zip(g.edges(labels=False, sort=False), # optional - sage.modules sage.rings.finite_rings + sage: edge_space = VectorSpace(FiniteField(2), m) # needs sage.modules sage.rings.finite_rings + sage: edge_vector = dict(zip(g.edges(labels=False, sort=False), # needs sage.modules sage.rings.finite_rings ....: edge_space.basis())) - sage: for (u, v), vec in list(edge_vector.items()): # optional - sage.modules sage.rings.finite_rings + sage: for (u, v), vec in list(edge_vector.items()): # needs sage.modules sage.rings.finite_rings ....: edge_vector[(v, u)] = vec Defining a lambda function associating a vector to the vertices of a @@ -5080,29 +5081,29 @@ def cycle_basis(self, output='vertex'): Finally checking the cycles are a free set:: - sage: basis_as_vectors = [cycle_to_vector(_) for _ in basis] # optional - sage.modules sage.rings.finite_rings - sage: edge_space.span(basis_as_vectors).rank() == len(basis) # optional - sage.modules sage.rings.finite_rings + sage: basis_as_vectors = [cycle_to_vector(_) for _ in basis] # needs networkx sage.modules sage.rings.finite_rings + sage: edge_space.span(basis_as_vectors).rank() == len(basis) # needs networkx sage.modules sage.rings.finite_rings True For undirected graphs with multiple edges:: sage: G = Graph([(0, 2, 'a'), (0, 2, 'b'), (0, 1, 'c'), (1, 2, 'd')], ....: multiedges=True) - sage: G.cycle_basis() # optional - networkx + sage: G.cycle_basis() # needs networkx [[0, 2], [2, 1, 0]] - sage: G.cycle_basis(output='edge') # optional - networkx + sage: G.cycle_basis(output='edge') # needs networkx [[(0, 2, 'a'), (2, 0, 'b')], [(2, 1, 'd'), (1, 0, 'c'), (0, 2, 'a')]] sage: H = Graph([(1, 2), (2, 3), (2, 3), (3, 4), (1, 4), ....: (1, 4), (4, 5), (5, 6), (4, 6), (6, 7)], multiedges=True) - sage: H.cycle_basis() # optional - networkx + sage: H.cycle_basis() # needs networkx [[1, 4], [2, 3], [4, 3, 2, 1], [6, 5, 4]] Disconnected graph:: sage: G.add_cycle(["Hey", "Wuuhuu", "Really ?"]) - sage: [sorted(c) for c in G.cycle_basis()] # optional - networkx + sage: [sorted(c) for c in G.cycle_basis()] # needs networkx [['Hey', 'Really ?', 'Wuuhuu'], [0, 2], [0, 1, 2]] - sage: [sorted(c) for c in G.cycle_basis(output='edge')] # optional - networkx + sage: [sorted(c) for c in G.cycle_basis(output='edge')] # needs networkx [[('Hey', 'Wuuhuu', None), ('Really ?', 'Hey', None), ('Wuuhuu', 'Really ?', None)], @@ -5113,13 +5114,13 @@ def cycle_basis(self, output='vertex'): sage: G = graphs.CycleGraph(3) sage: G.allow_multiple_edges(True) - sage: G.cycle_basis() # optional - networkx + sage: G.cycle_basis() # needs networkx [[2, 1, 0]] Not yet implemented for directed graphs:: sage: G = DiGraph([(0, 2, 'a'), (0, 1, 'c'), (1, 2, 'd')]) - sage: G.cycle_basis() # optional - networkx + sage: G.cycle_basis() # needs networkx Traceback (most recent call last): ... NotImplementedError: not implemented for directed graphs @@ -5130,9 +5131,9 @@ def cycle_basis(self, output='vertex'): sage: G = Graph([(1, 2, 'a'), (2, 3, 'b'), (2, 3, 'c'), ....: (3, 4, 'd'), (3, 4, 'e'), (4, 1, 'f')], multiedges=True) - sage: G.cycle_basis() # optional - networkx + sage: G.cycle_basis() # needs networkx [[2, 3], [4, 3, 2, 1], [4, 3, 2, 1]] - sage: G.cycle_basis(output='edge') # optional - networkx + sage: G.cycle_basis(output='edge') # needs networkx [[(2, 3, 'b'), (3, 2, 'c')], [(4, 3, 'd'), (3, 2, 'b'), (2, 1, 'a'), (1, 4, 'f')], [(4, 3, 'e'), (3, 2, 'b'), (2, 1, 'a'), (1, 4, 'f')]] @@ -5213,9 +5214,9 @@ def minimum_cycle_basis(self, algorithm=None, weight_function=None, by_weight=Fa [[1, 2, 3], [1, 2, 3, 4], [5, 6, 7]] sage: sorted(g.minimum_cycle_basis(by_weight=False)) [[1, 2, 3], [1, 3, 4], [5, 6, 7]] - sage: sorted(g.minimum_cycle_basis(by_weight=True, algorithm='NetworkX')) # optional - networkx + sage: sorted(g.minimum_cycle_basis(by_weight=True, algorithm='NetworkX')) # needs networkx [[1, 2, 3], [1, 2, 3, 4], [5, 6, 7]] - sage: g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX') # optional - networkx + sage: g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX') # needs networkx [[1, 2, 3], [1, 3, 4], [5, 6, 7]] :: @@ -5223,7 +5224,7 @@ def minimum_cycle_basis(self, algorithm=None, weight_function=None, by_weight=Fa sage: g = Graph([(1, 2), (2, 3), (3, 4), (4, 5), (5, 1), (5, 3)]) sage: sorted(g.minimum_cycle_basis(by_weight=False)) [[1, 2, 3, 5], [3, 4, 5]] - sage: sorted(g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX')) # optional - networkx + sage: sorted(g.minimum_cycle_basis(by_weight=False, algorithm='NetworkX')) # needs networkx [[1, 2, 3, 5], [3, 4, 5]] TESTS:: @@ -5352,7 +5353,7 @@ def is_planar(self, on_embedding=None, kuratowski=False, set_embedding=False, se :: sage: g = graphs.PetersenGraph() - sage: (g.is_planar(kuratowski=True))[1].adjacency_matrix() # optional - sage.modules + sage: (g.is_planar(kuratowski=True))[1].adjacency_matrix() # needs sage.modules [0 1 0 0 0 1 0 0 0] [1 0 1 0 0 0 1 0 0] [0 1 0 1 0 0 0 1 0] @@ -5555,7 +5556,7 @@ def is_circular_planar(self, on_embedding=None, kuratowski=False, EXAMPLES:: sage: g439 = Graph({1: [5, 7], 2: [5, 6], 3: [6, 7], 4: [5, 6, 7]}) - sage: g439.show() # optional - sage.plot + sage: g439.show() # needs sage.plot sage: g439.is_circular_planar(boundary=[1, 2, 3, 4]) False sage: g439.is_circular_planar(kuratowski=True, boundary=[1, 2, 3, 4]) @@ -5731,11 +5732,11 @@ def layout_planar(self, set_embedding=False, on_embedding=None, 7: [2, 4], 8: [1, 6], 9: [2, 5]} - sage: g = graphs.BalancedTree(3, 4) # optional - networkx - sage: pos = g.layout(layout='planar', save_pos=True, test=True) # optional - networkx - sage: pos[0] # optional - networkx + sage: g = graphs.BalancedTree(3, 4) # needs networkx + sage: pos = g.layout(layout='planar', save_pos=True, test=True) # needs networkx + sage: pos[0] # needs networkx [0, 119] - sage: pos[120] # optional - networkx + sage: pos[120] # needs networkx [21, 37] sage: g = graphs.CycleGraph(7) sage: g.layout(layout='planar', save_pos=True, test=True) @@ -6527,7 +6528,7 @@ def num_faces(self, embedding=None): else: if self.is_planar(): # We use Euler's formula: V-E+F-C=1 - C = len(self.connected_components()) + C = self.connected_components_number() return self.size() - self.order() + C + 1 else: raise ValueError("no embedding is provided and the graph is not planar") @@ -6734,7 +6735,7 @@ def steiner_tree(self, vertices, weighted=False, solver=None, verbose=0, # Can the problem be solved ? Are all the vertices in the same # connected component ? - cc = g.connected_component_containing_vertex(vertices[0]) + cc = g.connected_component_containing_vertex(vertices[0], sort=False) if any(v not in cc for v in vertices): from sage.categories.sets_cat import EmptySetError raise EmptySetError("the given vertices do not all belong to the " @@ -9140,6 +9141,12 @@ def feedback_vertex_set(self, value_only=False, solver=None, verbose=0, Traceback (most recent call last): ... ValueError: the only implementation available for undirected graphs is with constraint_generation set to True + + :trac:`35889` is fixed:: + + sage: G = Graph([('A', 1)]) + sage: G.feedback_vertex_set() + [] """ if not constraint_generation and not self.is_directed(): raise ValueError("the only implementation available for " @@ -10161,7 +10168,7 @@ def _build_flow_graph(self, flow, integer): The method removes zero-cost flow cycles and updates the values accordingly:: - sage: g = digraphs.DeBruijn(2,3) # optional - sage.combinat + sage: g = digraphs.DeBruijn(2,3) # needs sage.combinat sage: flow = {('001', '010'): 1, ('010', '100'): 1, ....: ('010', '101'): 1, ('101', '010'): 1} sage: flow_graph = g._build_flow_graph(flow, True) @@ -10480,34 +10487,34 @@ def pagerank(self, alpha=0.85, personalization=None, by_weight=False, EXAMPLES:: sage: G = graphs.CycleGraph(4) - sage: G.pagerank(algorithm="Networkx") # optional - networkx + sage: G.pagerank(algorithm="Networkx") # needs networkx {0: 0.25, 1: 0.25, 2: 0.25, 3: 0.25} sage: G.pagerank(alpha=0.50, algorithm="igraph") # abs tol 1e-9, optional - python_igraph {0: 0.25, 1: 0.25, 2: 0.25, 3: 0.25} sage: G = Graph([(1, 2, 40), (2, 3, 50), (3, 4, 60), ....: (1, 4, 70), (4, 5, 80), (5, 6, 20)]) - sage: G.pagerank(algorithm="NetworkX") # abs tol 1e-9 # optional - networkx + sage: G.pagerank(algorithm="NetworkX") # abs tol 1e-9 # needs networkx {1: 0.16112205885619563, 2: 0.1619531043247219, 3: 0.16112205885619563, 4: 0.2374999999999999, 5: 0.17775588228760858, 6: 0.100546895675278} - sage: G.pagerank(algorithm="NetworkX", by_weight=True) # abs tol 1e-9 # optional - networkx + sage: G.pagerank(algorithm="NetworkX", by_weight=True) # abs tol 1e-9 # needs networkx {1: 0.16459583718588994, 2: 0.13977928595154515, 3: 0.16539840184339605, 4: 0.3063198690713853, 5: 0.1700057609707141, 6: 0.05390084497706962} - sage: G.pagerank(algorithm="Scipy") # abs tol 1e-9 # optional - scipy + sage: G.pagerank(algorithm="Scipy") # abs tol 1e-9 # needs scipy {1: 0.16112205885619563, 2: 0.1619531043247219, 3: 0.16112205885619563, 4: 0.2374999999999999, 5: 0.17775588228760858, 6: 0.100546895675278} - sage: G.pagerank(algorithm="Scipy", by_weight=True) # abs tol 1e-9 # optional - scipy + sage: G.pagerank(algorithm="Scipy", by_weight=True) # abs tol 1e-9 # needs scipy {1: 0.16459583718588994, 2: 0.13977928595154515, 3: 0.16539840184339605, @@ -10539,7 +10546,7 @@ def pagerank(self, alpha=0.85, personalization=None, by_weight=False, TESTS:: sage: G = Graph([(1, 2), (2, 3), (3, 4), (1, 3)]) - sage: G.pagerank(algorithm="NetworkX", # optional - networkx + sage: G.pagerank(algorithm="NetworkX", # needs networkx ....: personalization={1:0, 2:3, 3:-2, 4:-1}) Traceback (most recent call last): ... @@ -10678,7 +10685,7 @@ def delete_vertex(self, vertex, in_order=False): sage: G = Graph(graphs.WheelGraph(9)) sage: G.delete_vertex(0) - sage: G.show() # optional - sage.plot + sage: G.show() # needs sage.plot :: @@ -11354,13 +11361,14 @@ def vertices(self, sort=None, key=None, degree=None, vertex_property=None): If you do not care about sorted output and you are concerned about the time taken to sort, consider the following alternative:: - sage: timeit V = P.vertices(sort=True) # not tested + sage: # not tested + sage: timeit V = P.vertices(sort=True) 625 loops, best of 3: 3.86 [micro]s per loop - sage: timeit V = P.vertices(sort=False) # not tested + sage: timeit V = P.vertices(sort=False) 625 loops, best of 3: 2.06 [micro]s per loop - sage: timeit V = list(P.vertex_iterator()) # not tested + sage: timeit V = list(P.vertex_iterator()) 625 loops, best of 3: 2.05 [micro]s per loop - sage: timeit('V = list(P)') # not tested + sage: timeit('V = list(P)') 625 loops, best of 3: 1.98 [micro]s per loop We illustrate various ways to use a ``key`` to sort the list:: @@ -12979,13 +12987,14 @@ def degree(self, vertices=None, labels=False): returned list is the degree of the `i`-th vertex in the list ``list(self)``:: - sage: D = digraphs.DeBruijn(4, 2) # optional - sage.combinat - sage: D.delete_vertex('20') # optional - sage.combinat - sage: print(D.degree()) # optional - sage.combinat + sage: # needs sage.combinat + sage: D = digraphs.DeBruijn(4, 2) + sage: D.delete_vertex('20') + sage: print(D.degree()) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print(D.degree(vertices=list(D))) # optional - sage.combinat + sage: print(D.degree(vertices=list(D))) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print(D.degree(vertices=D.vertices(sort=False))) # optional - sage.combinat + sage: print(D.degree(vertices=D.vertices(sort=False))) [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] """ if labels: @@ -13123,11 +13132,11 @@ def degree_iterator(self, vertices=None, labels=False): When ``vertices=None`` yields values in the order of ``list(D)``:: sage: V = list(D) - sage: D = digraphs.DeBruijn(4, 2) # optional - sage.combinat - sage: D.delete_vertex('20') # optional - sage.combinat - sage: print(list(D.degree_iterator())) # optional - sage.combinat + sage: D = digraphs.DeBruijn(4, 2) # needs sage.combinat + sage: D.delete_vertex('20') # needs sage.combinat + sage: print(list(D.degree_iterator())) # needs sage.combinat [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] - sage: print([D.degree(v) for v in D]) # optional - sage.combinat + sage: print([D.degree(v) for v in D]) # needs sage.combinat [7, 7, 6, 7, 8, 8, 7, 8, 8, 7, 8, 8, 8, 7, 8] """ if vertices is None: @@ -13758,99 +13767,100 @@ def subgraph_search(self, G, induced=False): The Petersen graph contains the path graph `P_5`:: sage: g = graphs.PetersenGraph() - sage: h1 = g.subgraph_search(graphs.PathGraph(5)); h1 # optional - sage.modules + sage: h1 = g.subgraph_search(graphs.PathGraph(5)); h1 # needs sage.modules Subgraph of (Petersen graph): Graph on 5 vertices - sage: h1.vertices(sort=True); h1.edges(sort=True, labels=False) # optional - sage.modules + sage: h1.vertices(sort=True); h1.edges(sort=True, labels=False) # needs sage.modules [0, 1, 2, 3, 4] [(0, 1), (1, 2), (2, 3), (3, 4)] - sage: I1 = g.subgraph_search(graphs.PathGraph(5), induced=True); I1 # optional - sage.modules + sage: I1 = g.subgraph_search(graphs.PathGraph(5), induced=True); I1 # needs sage.modules Subgraph of (Petersen graph): Graph on 5 vertices - sage: I1.vertices(sort=True); I1.edges(sort=True, labels=False) # optional - sage.modules + sage: I1.vertices(sort=True); I1.edges(sort=True, labels=False) # needs sage.modules [0, 1, 2, 3, 8] [(0, 1), (1, 2), (2, 3), (3, 8)] It also contains the claw `K_{1,3}`:: - sage: h2 = g.subgraph_search(graphs.ClawGraph()); h2 # optional - sage.modules + sage: # needs sage.modules + sage: h2 = g.subgraph_search(graphs.ClawGraph()); h2 Subgraph of (Petersen graph): Graph on 4 vertices - sage: h2.vertices(sort=True); h2.edges(sort=True, labels=False) # optional - sage.modules + sage: h2.vertices(sort=True); h2.edges(sort=True, labels=False) [0, 1, 4, 5] [(0, 1), (0, 4), (0, 5)] - sage: I2 = g.subgraph_search(graphs.ClawGraph(), induced=True); I2 # optional - sage.modules + sage: I2 = g.subgraph_search(graphs.ClawGraph(), induced=True); I2 Subgraph of (Petersen graph): Graph on 4 vertices - sage: I2.vertices(sort=True); I2.edges(sort=True, labels=False) # optional - sage.modules + sage: I2.vertices(sort=True); I2.edges(sort=True, labels=False) [0, 1, 4, 5] [(0, 1), (0, 4), (0, 5)] Of course the induced copies are isomorphic to the graphs we were looking for:: - sage: I1.is_isomorphic(graphs.PathGraph(5)) # optional - sage.modules + sage: I1.is_isomorphic(graphs.PathGraph(5)) # needs sage.modules True - sage: I2.is_isomorphic(graphs.ClawGraph()) # optional - sage.modules + sage: I2.is_isomorphic(graphs.ClawGraph()) # needs sage.modules True However, the Petersen graph does not contain a subgraph isomorphic to `K_3`:: - sage: g.subgraph_search(graphs.CompleteGraph(3)) is None # optional - sage.modules + sage: g.subgraph_search(graphs.CompleteGraph(3)) is None # needs sage.modules True Nor does it contain a nonempty induced subgraph isomorphic to `P_6`:: - sage: g.subgraph_search(graphs.PathGraph(6), induced=True) is None # optional - sage.modules + sage: g.subgraph_search(graphs.PathGraph(6), induced=True) is None # needs sage.modules True The empty graph is a subgraph of every graph:: - sage: g.subgraph_search(graphs.EmptyGraph()) # optional - sage.modules + sage: g.subgraph_search(graphs.EmptyGraph()) # needs sage.modules Graph on 0 vertices - sage: g.subgraph_search(graphs.EmptyGraph(), induced=True) # optional - sage.modules + sage: g.subgraph_search(graphs.EmptyGraph(), induced=True) # needs sage.modules Graph on 0 vertices The subgraph may just have edges missing:: sage: k3 = graphs.CompleteGraph(3); p3 = graphs.PathGraph(3) sage: k3.relabel(list('abc')) - sage: s = k3.subgraph_search(p3) # optional - sage.modules - sage: s.edges(sort=True, labels=False) # optional - sage.modules + sage: s = k3.subgraph_search(p3) # needs sage.modules + sage: s.edges(sort=True, labels=False) # needs sage.modules [('a', 'b'), ('b', 'c')] Of course, `P_3` is not an induced subgraph of `K_3`, though:: sage: k3 = graphs.CompleteGraph(3); p3 = graphs.PathGraph(3) sage: k3.relabel(list('abc')) - sage: k3.subgraph_search(p3, induced=True) is None # optional - sage.modules + sage: k3.subgraph_search(p3, induced=True) is None # needs sage.modules True If the graph has labels, the labels are just ignored:: sage: g.set_vertex(0, 'foo') - sage: c = g.subgraph_search(graphs.PathGraph(5)) # optional - sage.modules - sage: c.get_vertices() # optional - sage.modules + sage: c = g.subgraph_search(graphs.PathGraph(5)) # needs sage.modules + sage: c.get_vertices() # needs sage.modules {0: 'foo', 1: None, 2: None, 3: None, 4: None} TESTS: Inside of a small graph (:trac:`13906`):: - sage: Graph(5).subgraph_search(Graph(1)) # optional - sage.modules + sage: Graph(5).subgraph_search(Graph(1)) # needs sage.modules Graph on 1 vertex For labelled edges (:trac:`14999`):: sage: G = graphs.CompleteGraph(10) - sage: C = G.subgraph_search(graphs.CycleGraph(4)) # optional - sage.modules - sage: C.size() # optional - sage.modules + sage: C = G.subgraph_search(graphs.CycleGraph(4)) # needs sage.modules + sage: C.size() # needs sage.modules 4 - sage: C.edges(sort=True) # optional - sage.modules + sage: C.edges(sort=True) # needs sage.modules [(0, 1, None), (0, 3, None), (1, 2, None), (2, 3, None)] sage: for (u,v) in G.edges(sort=True, labels=False): ....: G.set_edge_label(u, v, u) - sage: C = G.subgraph_search(graphs.CycleGraph(4)) # optional - sage.modules - sage: C.edges(sort=True) # optional - sage.modules + sage: C = G.subgraph_search(graphs.CycleGraph(4)) # needs sage.modules + sage: C.edges(sort=True) # needs sage.modules [(0, 1, 0), (0, 3, 0), (1, 2, 1), (2, 3, 2)] """ @@ -13891,12 +13901,12 @@ def subgraph_search_count(self, G, induced=False): Counting the number of paths `P_5` in a PetersenGraph:: sage: g = graphs.PetersenGraph() - sage: g.subgraph_search_count(graphs.PathGraph(5)) # optional - sage.modules + sage: g.subgraph_search_count(graphs.PathGraph(5)) # needs sage.modules 240 Requiring these subgraphs be induced:: - sage: g.subgraph_search_count(graphs.PathGraph(5), induced=True) # optional - sage.modules + sage: g.subgraph_search_count(graphs.PathGraph(5), induced=True) # needs sage.modules 120 If we define the graph `T_k` (the transitive tournament on `k` vertices) @@ -13905,36 +13915,36 @@ def subgraph_search_count(self, G, induced=False): `0`:: sage: T5 = digraphs.TransitiveTournament(5) - sage: T5.subgraph_search_count(digraphs.Circuit(3)) # optional - sage.modules + sage: T5.subgraph_search_count(digraphs.Circuit(3)) # needs sage.modules 0 If we count instead the number of `T_3` in `T_5`, we expect the answer to be `\binom{5}{3}`:: sage: T3 = digraphs.TransitiveTournament(3) - sage: T5.subgraph_search_count(T3) # optional - sage.modules + sage: T5.subgraph_search_count(T3) # needs sage.modules 10 sage: binomial(5,3) 10 - sage: T3.is_isomorphic(T5.subgraph(vertices=[0, 1, 2])) # optional - sage.modules + sage: T3.is_isomorphic(T5.subgraph(vertices=[0, 1, 2])) # needs sage.modules True The empty graph is a subgraph of every graph:: - sage: g.subgraph_search_count(graphs.EmptyGraph()) # optional - sage.modules + sage: g.subgraph_search_count(graphs.EmptyGraph()) # needs sage.modules 1 If the graph has vertex labels or edge labels, the label is just ignored:: sage: g.set_vertex(0, 'foo') - sage: g.subgraph_search_count(graphs.PathGraph(5)) # optional - sage.modules + sage: g.subgraph_search_count(graphs.PathGraph(5)) # needs sage.modules 240 TESTS: Inside of a small graph (:trac:`13906`):: - sage: Graph(5).subgraph_search_count(Graph(1)) # optional - sage.modules + sage: Graph(5).subgraph_search_count(Graph(1)) # needs sage.modules 5 """ from sage.graphs.generic_graph_pyx import SubgraphSearch @@ -14001,7 +14011,7 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): sage: g = graphs.PathGraph(5) sage: P3 = graphs.PathGraph(3) - sage: for p in g.subgraph_search_iterator(P3, return_graphs=False): # optional - sage.modules + sage: for p in g.subgraph_search_iterator(P3, return_graphs=False): # needs sage.modules ....: print(p) [0, 1, 2] [1, 2, 3] @@ -14009,7 +14019,7 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): [2, 3, 4] [3, 2, 1] [4, 3, 2] - sage: for p in g.subgraph_search_iterator(P3, return_graphs=True): # optional - sage.modules + sage: for p in g.subgraph_search_iterator(P3, return_graphs=True): # needs sage.modules ....: print(p) Subgraph of (Path graph) Subgraph of (Path graph) @@ -14017,13 +14027,13 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): Subgraph of (Path graph) Subgraph of (Path graph) Subgraph of (Path graph) - sage: all(h.is_isomorphic(P3) for h in g.subgraph_search_iterator(P3)) # optional - sage.modules + sage: all(h.is_isomorphic(P3) for h in g.subgraph_search_iterator(P3)) # needs sage.modules True If the graph has vertex labels or edge labels, the label is just ignored:: sage: g.set_vertex(0, 'foo') - sage: for p in g.subgraph_search_iterator(P3, return_graphs=False): # optional - sage.modules + sage: for p in g.subgraph_search_iterator(P3, return_graphs=False): # needs sage.modules ....: print(p) [0, 1, 2] [1, 2, 3] @@ -14036,47 +14046,47 @@ def subgraph_search_iterator(self, G, induced=False, return_graphs=True): sage: H = graphs.HouseGraph() sage: P4 = graphs.PathGraph(4) - sage: all(h.is_isomorphic(P4) # optional - sage.modules + sage: all(h.is_isomorphic(P4) # needs sage.modules ....: for h in H.subgraph_search_iterator(P4, induced=True)) True - sage: sum(1 for h in H.subgraph_search_iterator(P4, induced=True)) # optional - sage.modules + sage: sum(1 for h in H.subgraph_search_iterator(P4, induced=True)) # needs sage.modules 4 - sage: sum(1 for h in H.subgraph_search_iterator(P4, induced=False)) # optional - sage.modules + sage: sum(1 for h in H.subgraph_search_iterator(P4, induced=False)) # needs sage.modules 20 Search for subdigraphs:: sage: H = digraphs.Complete(5) sage: P4 = digraphs.Path(4) - sage: sum(1 for _ in H.subgraph_search_iterator(P4, induced=True)) # optional - sage.modules + sage: sum(1 for _ in H.subgraph_search_iterator(P4, induced=True)) # needs sage.modules 0 - sage: sum(1 for _ in H.subgraph_search_iterator(P4, induced=False)) # optional - sage.modules + sage: sum(1 for _ in H.subgraph_search_iterator(P4, induced=False)) # needs sage.modules 120 This method also works for bipartite graphs:: sage: K33 = BipartiteGraph(graphs.CompleteBipartiteGraph(3, 3)) sage: K22 = BipartiteGraph(graphs.CompleteBipartiteGraph(2, 2)) - sage: sum(1 for _ in K33.subgraph_search_iterator(K22)) # optional - sage.modules + sage: sum(1 for _ in K33.subgraph_search_iterator(K22)) # needs sage.modules 72 TESTS: Inside of a small graph (:trac:`13906`):: - sage: list(Graph(5).subgraph_search_iterator(Graph(1))) # optional - sage.modules + sage: list(Graph(5).subgraph_search_iterator(Graph(1))) # needs sage.modules [Graph on 1 vertex, Graph on 1 vertex, Graph on 1 vertex, Graph on 1 vertex, Graph on 1 vertex] Check that the behavior of the method is consistent (:trac:`34004`):: sage: g = graphs.CycleGraph(3) - sage: for i in range(3): # optional - sage.modules + sage: for i in range(3): # needs sage.modules ....: g.subgraph_search_iterator(graphs.PathGraph(i)) sage: K4 = digraphs.Complete(4) sage: K3 = digraphs.Complete(3) - sage: for g in K4.subgraph_search_iterator(K3, return_graphs=True): # optional - sage.modules + sage: for g in K4.subgraph_search_iterator(K3, return_graphs=True): # needs sage.modules ....: print(type(g)) ....: break sage: K33 = BipartiteGraph(graphs.CompleteBipartiteGraph(3, 3)) sage: K22 = BipartiteGraph(graphs.CompleteBipartiteGraph(2, 2)) - sage: for b in K33.subgraph_search_iterator(K22, return_graphs=True): # optional - sage.modules + sage: for b in K33.subgraph_search_iterator(K22, return_graphs=True): # needs sage.modules ....: print(type(b)) ....: break sage: P5 = graphs.PathGraph(5) - sage: for b in K33.subgraph_search_iterator(P5, return_graphs=True): # optional - sage.modules + sage: for b in K33.subgraph_search_iterator(P5, return_graphs=True): # needs sage.modules ....: print(type(b)) ....: break - sage: for b in Graph(K33).subgraph_search_iterator(K22, return_graphs=True): # optional - sage.modules + sage: for b in Graph(K33).subgraph_search_iterator(K22, return_graphs=True): # needs sage.modules ....: print(type(b)) ....: break @@ -14217,20 +14227,18 @@ def is_chordal(self, certificate=False, algorithm="B"): a hole. - ``algorithm`` -- string (default: ``"B"``); the algorithm to choose - among ``"A"`` or ``"B"`` (see next section). While they will agree on - whether the given graph is chordal, they cannot be expected to return - the same certificates. + among ``"A"`` or ``"B"``. While they will agree on whether the given + graph is chordal, they cannot be expected to return the same + certificates. ALGORITHM: - This algorithm works through computing a Lex BFS on the graph, then - checking whether the order is a Perfect Elimination Order by computing - for each vertex `v` the subgraph induces by its non-deleted neighbors, - then testing whether this graph is complete. - - This problem can be solved in `O(m)` [RT1975]_ ( where `m` is the number - of edges in the graph ) but this implementation is not linear because of - the complexity of Lex BFS. + This method implements the algorithm proposed in [RT1975]_ for the + recognition of chordal graphs with time complexity in `O(m)`. The + algorithm works through computing a Lex BFS on the graph, then checking + whether the order is a Perfect Elimination Order by computing for each + vertex `v` the subgraph induced by its non-deleted neighbors, then + testing whether this graph is complete. EXAMPLES: @@ -14244,21 +14252,21 @@ def is_chordal(self, certificate=False, algorithm="B"): The same goes with the product of a random lobster (which is a tree) and a Complete Graph :: - sage: grl = graphs.RandomLobster(10, .5, .5) # optional - networkx - sage: g = grl.lexicographic_product(graphs.CompleteGraph(3)) # optional - networkx - sage: g.is_chordal() # optional - networkx + sage: grl = graphs.RandomLobster(10, .5, .5) # needs networkx + sage: g = grl.lexicographic_product(graphs.CompleteGraph(3)) # needs networkx + sage: g.is_chordal() # needs networkx True The disjoint union of chordal graphs is still chordal:: - sage: (2 * g).is_chordal() # optional - networkx + sage: (2 * g).is_chordal() # needs networkx True Let us check the certificate given by Sage is indeed a perfect elimination order:: - sage: _, peo = g.is_chordal(certificate=True) # optional - networkx - sage: for v in peo: # optional - networkx + sage: _, peo = g.is_chordal(certificate=True) # needs networkx + sage: for v in peo: # needs networkx ....: if not g.subgraph(g.neighbors(v)).is_clique(): ....: raise ValueError("this should never happen") ....: g.delete_vertex(v) @@ -14344,7 +14352,7 @@ def is_chordal(self, certificate=False, algorithm="B"): continue x = next(t_peo.neighbor_out_iterator(v)) - S = self.neighbors(x) + [x] + S = self.neighbors(x, closed=True) if not frozenset(g.neighbor_iterator(v)).issubset(S): @@ -14380,7 +14388,7 @@ def is_chordal(self, certificate=False, algorithm="B"): peo, t_peo = self.lex_BFS(reverse=True, tree=True) # Remembering the (closed) neighborhoods of each vertex - neighbors_subsets = {v: frozenset(self.neighbors(v) + [v]) for v in g} + neighbors_subsets = {v: frozenset(self.neighbor_iterator(v, closed=True)) for v in g} pos_in_peo = dict(zip(peo, range(self.order()))) # Iteratively removing vertices and checking everything is fine. @@ -14478,27 +14486,27 @@ def is_circulant(self, certificate=False): The Petersen graph is not a circulant graph:: sage: g = graphs.PetersenGraph() - sage: g.is_circulant() # optional - sage.groups + sage: g.is_circulant() # needs sage.groups False A cycle is obviously a circulant graph, but several sets of parameters can be used to define it:: sage: g = graphs.CycleGraph(5) - sage: g.is_circulant(certificate=True) # optional - sage.groups + sage: g.is_circulant(certificate=True) # needs sage.groups (True, [(5, [1, 4]), (5, [2, 3])]) The same goes for directed graphs:: sage: g = digraphs.Circuit(5) - sage: g.is_circulant(certificate=True) # optional - sage.groups + sage: g.is_circulant(certificate=True) # needs sage.groups (True, [(5, [1]), (5, [3]), (5, [2]), (5, [4])]) With this information, it is very easy to create (and plot) all possible drawings of a circulant graph:: sage: g = graphs.CirculantGraph(13, [2, 3, 10, 11]) - sage: for param in g.is_circulant(certificate=True)[1]: # optional - sage.groups + sage: for param in g.is_circulant(certificate=True)[1]: # needs sage.groups ....: graphs.CirculantGraph(*param) Circulant graph ([2, 3, 10, 11]): Graph on 13 vertices Circulant graph ([1, 5, 8, 12]): Graph on 13 vertices @@ -14506,13 +14514,14 @@ def is_circulant(self, certificate=False): TESTS:: - sage: digraphs.DeBruijn(3,1).is_circulant(certificate=True) # optional - sage.combinat sage.groups + sage: # needs sage.groups + sage: digraphs.DeBruijn(3,1).is_circulant(certificate=True) # needs sage.combinat (True, [(3, [0, 1, 2])]) - sage: Graph(1).is_circulant(certificate=True) # optional - sage.groups + sage: Graph(1).is_circulant(certificate=True) (True, (1, [])) - sage: Graph(0).is_circulant(certificate=True) # optional - sage.groups + sage: Graph(0).is_circulant(certificate=True) (True, (0, [])) - sage: Graph({0: [0]}).is_circulant(certificate=True) # optional - sage.groups + sage: Graph({0: [0]}).is_circulant(certificate=True) (True, (1, [0])) """ self._scream_if_not_simple(allow_loops=True) @@ -14746,7 +14755,7 @@ def is_gallai_tree(self): a special vertex `-1` is a ''star-shaped'' Gallai tree:: sage: g = 8 * graphs.CompleteGraph(6) - sage: g.add_edges([(-1, c[0]) for c in g.connected_components()]) + sage: g.add_edges([(-1, c[0]) for c in g.connected_components(sort=False)]) sage: g.is_gallai_tree() True @@ -15102,11 +15111,11 @@ def is_subgraph(self, other, induced=True, up_to_isomorphism=False): sage: p11 = graphs.PathGraph(11) sage: p15 = graphs.PathGraph(15) sage: g = graphs.Grid2dGraph(4, 4) - sage: p15.is_subgraph(g, induced=False, up_to_isomorphism=True) # optional - sage.modules + sage: p15.is_subgraph(g, induced=False, up_to_isomorphism=True) # needs sage.modules True - sage: p15.is_subgraph(g, induced=True, up_to_isomorphism=True) # optional - sage.modules + sage: p15.is_subgraph(g, induced=True, up_to_isomorphism=True) # needs sage.modules False - sage: p11.is_subgraph(g, induced=True, up_to_isomorphism=True) # optional - sage.modules + sage: p11.is_subgraph(g, induced=True, up_to_isomorphism=True) # needs sage.modules True TESTS: @@ -15184,15 +15193,15 @@ def cluster_triangles(self, nbunch=None, implementation=None): :: sage: G = graphs.RandomGNP(20, .3) - sage: d1 = G.cluster_triangles(implementation="networkx") # optional - networkx + sage: d1 = G.cluster_triangles(implementation="networkx") # needs networkx sage: d2 = G.cluster_triangles(implementation="dense_copy") sage: d3 = G.cluster_triangles(implementation="sparse_copy") - sage: d1 == d2 and d1 == d3 + sage: d1 == d2 and d1 == d3 # needs networkx True TESTS:: - sage: DiGraph().cluster_triangles(implementation="networkx") # optional - networkx + sage: DiGraph().cluster_triangles(implementation="networkx") # needs networkx Traceback (most recent call last): ... ValueError: the 'networkx' implementation does not support directed graphs @@ -15252,7 +15261,7 @@ def clustering_average(self, implementation=None): sage: (graphs.FruchtGraph()).clustering_average() 1/4 - sage: (graphs.FruchtGraph()).clustering_average(implementation='networkx') # optional - networkx + sage: (graphs.FruchtGraph()).clustering_average(implementation='networkx') # needs networkx 0.25 TESTS: @@ -15268,7 +15277,7 @@ def clustering_average(self, implementation=None): sage: G = graphs.RandomGNM(10,20) sage: impls = ['boost','sparse_copy','dense_copy'] - sage: impls += ['networkx'] # optional - networkx + sage: impls += ['networkx'] # needs networkx sage: coeffs = [G.clustering_average(implementation=impl) ....: for impl in impls] sage: max(coeffs) - min(coeffs) # tol abs 1e-12 @@ -15454,7 +15463,7 @@ def cluster_transitivity(self): EXAMPLES:: - sage: graphs.FruchtGraph().cluster_transitivity() # optional - networkx + sage: graphs.FruchtGraph().cluster_transitivity() # needs networkx 0.25 """ import networkx @@ -15689,13 +15698,13 @@ def girth(self, certificate=False): sage: g = digraphs.Circuit(6) sage: g.girth() 6 - sage: g = digraphs.RandomDirectedGNC(10) # optional - networkx - sage: g.girth() # optional - networkx + sage: g = digraphs.RandomDirectedGNC(10) # needs networkx + sage: g.girth() # needs networkx +Infinity - sage: g = DiGraph([(0, 1), (1, 2), (1, 3), (2, 3), (3, 4), (4, 0)]) # optional - networkx - sage: g.girth() # optional - networkx + sage: g = DiGraph([(0, 1), (1, 2), (1, 3), (2, 3), (3, 4), (4, 0)]) # needs networkx + sage: g.girth() # needs networkx 4 - sage: Graph(g).girth() # optional - networkx + sage: Graph(g).girth() # needs networkx 3 """ # Cases where girth <= 2 @@ -15746,10 +15755,10 @@ def odd_girth(self, algorithm="bfs", certificate=False): The McGee graph has girth 7 and therefore its odd girth is 7 as well:: - sage: G = graphs.McGeeGraph() # optional - networkx - sage: G.girth() # optional - networkx + sage: G = graphs.McGeeGraph() # needs networkx + sage: G.girth() # needs networkx 7 - sage: G.odd_girth() # optional - networkx + sage: G.odd_girth() # needs networkx 7 Any complete (directed) graph on more than 2 vertices contains @@ -15774,15 +15783,16 @@ def odd_girth(self, algorithm="bfs", certificate=False): The odd girth of a (directed) graph with loops is 1:: - sage: G = graphs.RandomGNP(10, .5) # optional - networkx - sage: G.allow_loops(True) # optional - networkx - sage: G.add_edge(0, 0) # optional - networkx - sage: G.odd_girth() # optional - networkx + sage: # needs networkx + sage: G = graphs.RandomGNP(10, .5) + sage: G.allow_loops(True) + sage: G.add_edge(0, 0) + sage: G.odd_girth() 1 - sage: G = digraphs.RandomDirectedGNP(10, .5) # optional - networkx - sage: G.allow_loops(True) # optional - networkx - sage: G.add_edge(0, 0) # optional - networkx - sage: G.odd_girth() # optional - networkx + sage: G = digraphs.RandomDirectedGNP(10, .5) + sage: G.allow_loops(True) + sage: G.add_edge(0, 0) + sage: G.odd_girth() 1 .. SEEALSO:: @@ -15987,17 +15997,17 @@ def centrality_betweenness(self, k=None, normalized=True, weight=None, 9: 3.333333333333333, 10: 3.333333333333333, 11: 3.333333333333333} sage: D = DiGraph({0:[1,2,3], 1:[2], 3:[0,1]}) - sage: D.show(figsize=[2,2]) # optional - sage.plot + sage: D.show(figsize=[2,2]) # needs sage.plot sage: D = D.to_undirected() - sage: D.show(figsize=[2,2]) # optional - sage.plot + sage: D.show(figsize=[2,2]) # needs sage.plot sage: D.centrality_betweenness() # abs tol abs 1e-10 {0: 0.16666666666666666, 1: 0.16666666666666666, 2: 0.0, 3: 0.0} TESTS:: - sage: tests = ([graphs.RandomGNP(30,.1) for i in range(10)]+ # optional - networkx + sage: tests = ([graphs.RandomGNP(30,.1) for i in range(10)]+ # needs networkx ....: [digraphs.RandomDirectedGNP(30,.1) for i in range(10)]) - sage: for g in tests: # optional - networkx + sage: for g in tests: # needs networkx ....: r1 = g.centrality_betweenness(algorithm="Sage",exact=0) ....: r2 = g.centrality_betweenness(algorithm="Sage",exact=1) ....: r3 = g.centrality_betweenness(algorithm="NetworkX") @@ -16127,11 +16137,11 @@ def centrality_closeness(self, vert=None, by_weight=False, algorithm=None, 8: 0.61111111111111..., 9: 0.61111111111111..., 10: 0.61111111111111..., 11: 0.61111111111111...} sage: D = DiGraph({0:[1,2,3], 1:[2], 3:[0,1]}) - sage: D.show(figsize=[2,2]) # optional - sage.plot + sage: D.show(figsize=[2,2]) # needs sage.plot sage: D.centrality_closeness(vert=[0,1]) {0: 1.0, 1: 0.3333333333333333} sage: D = D.to_undirected() - sage: D.show(figsize=[2,2]) # optional - sage.plot + sage: D.show(figsize=[2,2]) # needs sage.plot sage: D.centrality_closeness() {0: 1.0, 1: 1.0, 2: 0.75, 3: 0.75} @@ -16503,9 +16513,9 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, [4, 17, 16, 12, 13, 9] sage: D.shortest_path(4, 9, algorithm='BFS') [4, 3, 2, 1, 8, 9] - sage: D.shortest_path(4, 8, algorithm='Dijkstra_NetworkX') # optional - networkx + sage: D.shortest_path(4, 8, algorithm='Dijkstra_NetworkX') # needs networkx [4, 3, 2, 1, 8] - sage: D.shortest_path(4, 8, algorithm='Dijkstra_Bid_NetworkX') # optional - networkx + sage: D.shortest_path(4, 8, algorithm='Dijkstra_Bid_NetworkX') # needs networkx [4, 3, 2, 1, 8] sage: D.shortest_path(4, 9, algorithm='Dijkstra_Bid') [4, 3, 19, 0, 10, 9] @@ -16521,10 +16531,10 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, [0, 4, 3] sage: G.shortest_path(0, 3, by_weight=True) [0, 1, 2, 3] - sage: G.shortest_path(0, 3, by_weight=True, # optional - networkx + sage: G.shortest_path(0, 3, by_weight=True, # needs networkx ....: algorithm='Dijkstra_NetworkX') [0, 1, 2, 3] - sage: G.shortest_path(0, 3, by_weight=True, # optional - networkx + sage: G.shortest_path(0, 3, by_weight=True, # needs networkx ....: algorithm='Dijkstra_Bid_NetworkX') [0, 1, 2, 3] @@ -16564,7 +16574,7 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None, sage: G = Graph() sage: G.add_vertices([1, 2]) sage: algs = ['BFS', 'BFS_Bid', 'Dijkstra_Bid', 'Bellman-Ford_Boost'] - sage: algs += ['Dijkstra_NetworkX', 'Dijkstra_Bid_NetworkX'] # optional - networkx + sage: algs += ['Dijkstra_NetworkX', 'Dijkstra_Bid_NetworkX'] # needs networkx sage: all(G.shortest_path(1, 2, algorithm=alg) == [] ....: for alg in algs) True @@ -16688,9 +16698,9 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, 5 sage: D.shortest_path_length(4, 9, algorithm='BFS') 5 - sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_NetworkX') # optional - networkx + sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_NetworkX') # needs networkx 5 - sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_Bid_NetworkX') # optional - networkx + sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_Bid_NetworkX') # needs networkx 5 sage: D.shortest_path_length(4, 9, algorithm='Dijkstra_Bid') 5 @@ -16708,10 +16718,10 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, 2 sage: G.shortest_path_length(0, 3, by_weight=True) 3 - sage: G.shortest_path_length(0, 3, by_weight=True, # optional - networkx + sage: G.shortest_path_length(0, 3, by_weight=True, # needs networkx ....: algorithm='Dijkstra_NetworkX') 3 - sage: G.shortest_path_length(0, 3, by_weight=True, # optional - networkx + sage: G.shortest_path_length(0, 3, by_weight=True, # needs networkx ....: algorithm='Dijkstra_Bid_NetworkX') 3 @@ -16754,7 +16764,7 @@ def shortest_path_length(self, u, v, by_weight=False, algorithm=None, sage: G = Graph() sage: G.add_vertices([1, 2]) sage: algs = ['BFS', 'BFS_Bid', 'Dijkstra_Bid', 'Bellman-Ford_Boost'] - sage: algs += ['Dijkstra_NetworkX', 'Dijkstra_Bid_NetworkX'] # optional - networkx + sage: algs += ['Dijkstra_NetworkX', 'Dijkstra_Bid_NetworkX'] # needs networkx sage: all(G.shortest_path_length(1, 2, algorithm=alg) == Infinity ....: for alg in algs) True @@ -17265,7 +17275,7 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, sage: G = Graph({0: {1: 1}, 1: {2: 1}, 2: {3: 1}, 3: {4: 2}, 4: {0: 2}}, ....: sparse=True) - sage: G.plot(edge_labels=True).show() # long time # optional - sage.plot + sage: G.plot(edge_labels=True).show() # long time # needs sage.plot sage: G.shortest_path_lengths(0, by_weight=True) {0: 0, 1: 1, 2: 2, 3: 3, 4: 2} @@ -17273,7 +17283,7 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, sage: D = DiGraph([(0,1,{'weight':1}), (1,2,{'weight':3}), (0,2,{'weight':5})]) sage: weight_function = lambda e: e[2]['weight'] - sage: D.shortest_path_lengths(1, algorithm='Dijkstra_NetworkX', # optional - networkx + sage: D.shortest_path_lengths(1, algorithm='Dijkstra_NetworkX', # needs networkx ....: by_weight=False) {1: 0, 2: 1} sage: D.shortest_path_lengths(0, weight_function=weight_function) @@ -17299,10 +17309,10 @@ def shortest_path_lengths(self, u, by_weight=False, algorithm=None, sage: g = graphs.Grid2dGraph(5,5) sage: d1 = g.shortest_path_lengths((0,0), algorithm="BFS") - sage: d2 = g.shortest_path_lengths((0,0), algorithm="Dijkstra_NetworkX") # optional - networkx + sage: d2 = g.shortest_path_lengths((0,0), algorithm="Dijkstra_NetworkX") # needs networkx sage: d3 = g.shortest_path_lengths((0,0), algorithm="Dijkstra_Boost") sage: d4 = g.shortest_path_lengths((0,0), algorithm="Bellman-Ford_Boost") - sage: d1 == d2 == d3 == d4 + sage: d1 == d2 == d3 == d4 # needs networkx True """ by_weight, weight_function = self._get_weight_function(by_weight=by_weight, @@ -18131,7 +18141,7 @@ def breadth_first_search(self, start, ignore_direction=False, [0, 1, 4, 5, 2, 6, 3, 9, 7, 8] sage: D = DiGraph({0: [1, 3], 1: [0, 2], 2: [0, 3], 3: [4]}) - sage: D.show() # optional - sage.plot + sage: D.show() # needs sage.plot sage: list(D.breadth_first_search(4, neighbors=D.neighbor_in_iterator, ....: report_distance=True)) [(4, 0), (3, 1), (0, 2), (2, 2), (1, 3)] @@ -18476,11 +18486,11 @@ def add_cycle(self, vertices): sage: G = Graph() sage: G.add_vertices(range(10)); G Graph on 10 vertices - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot sage: G.add_cycle(list(range(10, 20))) - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot sage: G.add_cycle(list(range(10))) - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot :: @@ -18532,11 +18542,11 @@ def add_path(self, vertices): sage: G = Graph() sage: G.add_vertices(range(10)); G Graph on 10 vertices - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot sage: G.add_path(list(range(10, 20))) - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot sage: G.add_path(list(range(10))) - sage: show(G) # optional - sage.plot + sage: show(G) # needs sage.plot :: @@ -18561,10 +18571,10 @@ def complement(self): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.plot() # long time # optional - sage.plot + sage: P.plot() # long time # needs sage.plot Graphics object consisting of 26 graphics primitives sage: PC = P.complement() - sage: PC.plot() # long time # optional - sage.plot + sage: PC.plot() # long time # needs sage.plot Graphics object consisting of 41 graphics primitives :: @@ -18893,9 +18903,9 @@ def cartesian_product(self, other): Cartesian product of digraphs:: sage: P = DiGraph([(0, 1)]) - sage: B = digraphs.DeBruijn(['a', 'b'], 2) # optional - sage.combinat - sage: Q = P.cartesian_product(B) # optional - sage.combinat - sage: Q.edges(sort=True, labels=None) # optional - sage.combinat + sage: B = digraphs.DeBruijn(['a', 'b'], 2) # needs sage.combinat + sage: Q = P.cartesian_product(B) # needs sage.combinat + sage: Q.edges(sort=True, labels=None) # needs sage.combinat [((0, 'aa'), (0, 'aa')), ((0, 'aa'), (0, 'ab')), ((0, 'aa'), (1, 'aa')), ((0, 'ab'), (0, 'ba')), ((0, 'ab'), (0, 'bb')), ((0, 'ab'), (1, 'ab')), @@ -18906,10 +18916,10 @@ def cartesian_product(self, other): ((1, 'ab'), (1, 'ba')), ((1, 'ab'), (1, 'bb')), ((1, 'ba'), (1, 'aa')), ((1, 'ba'), (1, 'ab')), ((1, 'bb'), (1, 'ba')), ((1, 'bb'), (1, 'bb'))] - sage: Q.strongly_connected_components_digraph().num_verts() # optional - sage.combinat + sage: Q.strongly_connected_components_digraph().num_verts() # needs sage.combinat 2 - sage: V = Q.strongly_connected_component_containing_vertex((0, 'aa')) # optional - sage.combinat - sage: B.is_isomorphic(Q.subgraph(V)) # optional - sage.combinat + sage: V = Q.strongly_connected_component_containing_vertex((0, 'aa')) # needs sage.combinat + sage: B.is_isomorphic(Q.subgraph(V)) # needs sage.combinat True """ self._scream_if_not_simple(allow_loops=True) @@ -19144,13 +19154,14 @@ def strong_product(self, other): Counting the edges (see :trac:`13699`):: - sage: g = graphs.RandomGNP(5, .5) # optional - networkx - sage: gn,gm = g.order(), g.size() # optional - networkx - sage: h = graphs.RandomGNP(5, .5) # optional - networkx - sage: hn,hm = h.order(), h.size() # optional - networkx - sage: product_size = g.strong_product(h).size() # optional - networkx - sage: expected = gm * hn + hm * gn + 2 * gm * hm # optional - networkx - sage: product_size == expected # optional - networkx + sage: # needs networkx + sage: g = graphs.RandomGNP(5, .5) + sage: gn,gm = g.order(), g.size() + sage: h = graphs.RandomGNP(5, .5) + sage: hn,hm = h.order(), h.size() + sage: product_size = g.strong_product(h).size() + sage: expected = gm * hn + hm * gn + 2 * gm * hm + sage: product_size == expected True """ self._scream_if_not_simple(allow_loops=True) @@ -19361,7 +19372,7 @@ def transitive_reduction(self): return Graph(self.min_spanning_tree(weight_function=lambda e: 1)) else: G = Graph(list(self)) - for cc in self.connected_components(): + for cc in self.connected_components(sort=False): if len(cc) > 1: edges = self.subgraph(cc).min_spanning_tree(weight_function=lambda e: 1) G.add_edges(edges) @@ -19444,35 +19455,36 @@ def _color_by_label(self, format='hex', as_function=False, default_color="black" We consider the Cayley graph of the symmetric group, whose edges are labelled by the numbers 1,2, and 3:: - sage: G = SymmetricGroup(4).cayley_graph() # optional - sage.groups - sage: set(G.edge_labels()) # optional - sage.groups + sage: G = SymmetricGroup(4).cayley_graph() # needs sage.groups + sage: set(G.edge_labels()) # needs sage.groups {1, 2, 3} We first request the coloring as a function:: - sage: f = G._color_by_label(as_function=True) # optional - sage.groups - sage: [f(1), f(2), f(3)] # optional - sage.groups + sage: # needs sage.groups + sage: f = G._color_by_label(as_function=True) + sage: [f(1), f(2), f(3)] ['#0000ff', '#ff0000', '#00ff00'] - sage: f = G._color_by_label({1: "blue", 2: "red", 3: "green"}, # optional - sage.groups + sage: f = G._color_by_label({1: "blue", 2: "red", 3: "green"}, ....: as_function=True) - sage: [f(1), f(2), f(3)] # optional - sage.groups + sage: [f(1), f(2), f(3)] ['blue', 'red', 'green'] - sage: f = G._color_by_label({1: "red"}, as_function=True) # optional - sage.groups - sage: [f(1), f(2), f(3)] # optional - sage.groups + sage: f = G._color_by_label({1: "red"}, as_function=True) + sage: [f(1), f(2), f(3)] ['red', 'black', 'black'] - sage: f = G._color_by_label({1: "red"}, as_function=True, # optional - sage.groups + sage: f = G._color_by_label({1: "red"}, as_function=True, ....: default_color='blue') - sage: [f(1), f(2), f(3)] # optional - sage.groups + sage: [f(1), f(2), f(3)] ['red', 'blue', 'blue'] The default output is a dictionary assigning edges to colors:: - sage: G._color_by_label() # optional - sage.groups + sage: G._color_by_label() # needs sage.groups {'#0000ff': [((), (1,2), 1), ...], '#00ff00': [((), (3,4), 3), ...], '#ff0000': [((), (2,3), 2), ...]} - sage: G._color_by_label({1: "blue", 2: "red", 3: "green"}) # optional - sage.groups + sage: G._color_by_label({1: "blue", 2: "red", 3: "green"}) # needs sage.groups {'blue': [((), (1,2), 1), ...], 'green': [((), (3,4), 3), ...], 'red': [((), (2,3), 2), ...]} @@ -19481,12 +19493,13 @@ def _color_by_label(self, format='hex', as_function=False, default_color="black" We check what happens when several labels have the same color:: - sage: result = G._color_by_label({1: "blue", 2: "blue", 3: "green"}) # optional - sage.groups - sage: sorted(result) # optional - sage.groups + sage: # needs sage.groups + sage: result = G._color_by_label({1: "blue", 2: "blue", 3: "green"}) + sage: sorted(result) ['blue', 'green'] - sage: len(result['blue']) # optional - sage.groups + sage: len(result['blue']) 48 - sage: len(result['green']) # optional - sage.groups + sage: len(result['green']) 24 """ if format is True: @@ -19540,8 +19553,8 @@ def latex_options(self): sage: opts = g.latex_options() sage: opts LaTeX options for Petersen graph: {} - sage: opts.set_option('tkz_style', 'Classic') # optional - sage.plot - sage: opts # optional - sage.plot + sage: opts.set_option('tkz_style', 'Classic') # needs sage.plot + sage: opts # needs sage.plot LaTeX options for Petersen graph: {'tkz_style': 'Classic'} """ if self._latex_opts is None: @@ -19569,9 +19582,9 @@ def set_latex_options(self, **kwds): EXAMPLES:: sage: g = graphs.PetersenGraph() - sage: g.set_latex_options(tkz_style='Welsh') # optional - sage.plot - sage: opts = g.latex_options() # optional - sage.plot - sage: opts.get_option('tkz_style') # optional - sage.plot + sage: g.set_latex_options(tkz_style='Welsh') # needs sage.plot + sage: opts = g.latex_options() # needs sage.plot + sage: opts.get_option('tkz_style') # needs sage.plot 'Welsh' """ opts = self.latex_options() @@ -19735,7 +19748,7 @@ def layout_spring(self, by_component=True, **options): 4: [2.14..., -0.30...], 5: [2.80..., 0.22...]} sage: g = graphs.LadderGraph(7) - sage: g.plot(layout="spring") # optional - sage.plot + sage: g.plot(layout="spring") # needs sage.plot Graphics object consisting of 34 graphics primitives """ return spring_layout_fast(self, by_component=by_component, **options) @@ -19775,7 +19788,7 @@ def layout_ranked(self, heights=None, dim=2, spring=False, **options): 4: [1.33..., 1], 5: [1.33..., 2]} sage: g = graphs.LadderGraph(7) - sage: g.plot(layout="ranked", heights={i: (i, i+7) for i in range(7)}) # optional - sage.plot + sage: g.plot(layout="ranked", heights={i: (i, i+7) for i in range(7)}) # needs sage.plot Graphics object consisting of 34 graphics primitives """ assert heights is not None @@ -19903,7 +19916,7 @@ def layout_circular(self, dim=2, center=(0, 0), radius=1, shift=0, angle=0, **op 4: (0.43..., -0.90...), 5: (0.97..., -0.22...), 6: (0.78..., 0.62...)} - sage: G.plot(layout="circular") # optional - sage.plot + sage: G.plot(layout="circular") # needs sage.plot Graphics object consisting of 22 graphics primitives """ assert dim == 2, "3D circular layout not implemented" @@ -19937,19 +19950,19 @@ def layout_forest(self, tree_orientation="down", forest_roots=None, sage: G = graphs.RandomTree(4) + graphs.RandomTree(5) + graphs.RandomTree(6) sage: p = G.layout_forest() - sage: G.plot(pos=p) # random # optional - sage.plot + sage: G.plot(pos=p) # random # needs sage.plot Graphics object consisting of 28 graphics primitives sage: P5 = graphs.PathGraph(5) - sage: H = P5 + P5 + graphs.BalancedTree(2,2) # optional - networkx - sage: p = H.layout_forest(forest_roots=[14,3]) # optional - networkx - sage: H.plot(pos=p) # optional - networkx sage.plot + sage: H = P5 + P5 + graphs.BalancedTree(2,2) # needs networkx + sage: p = H.layout_forest(forest_roots=[14,3]) # needs networkx + sage: H.plot(pos=p) # needs networkx sage.plot Graphics object consisting of 32 graphics primitives TESTS:: sage: G = Graph(0) - sage: G.plot(layout='forest') # optional - sage.plot + sage: G.plot(layout='forest') # needs sage.plot Graphics object consisting of 0 graphics primitives Works for forests that are trees:: @@ -20010,19 +20023,19 @@ def layout_tree(self, tree_orientation="down", tree_root=None, EXAMPLES:: sage: G = graphs.RandomTree(80) - sage: G.plot(layout="tree", tree_orientation="right") # optional - sage.plot + sage: G.plot(layout="tree", tree_orientation="right") # needs sage.plot Graphics object consisting of 160 graphics primitives - sage: T = graphs.RandomLobster(25, 0.3, 0.3) # optional - networkx - sage: T.show(layout='tree', tree_orientation='up') # optional - networkx sage.plot + sage: T = graphs.RandomLobster(25, 0.3, 0.3) # needs networkx + sage: T.show(layout='tree', tree_orientation='up') # needs networkx sage.plot sage: G = graphs.HoffmanSingletonGraph() sage: T = Graph() sage: T.add_edges(G.min_spanning_tree(starting_vertex=0)) - sage: T.show(layout='tree', tree_root=0) # optional - sage.plot + sage: T.show(layout='tree', tree_root=0) # needs sage.plot - sage: G = graphs.BalancedTree(2, 2) # optional - networkx - sage: G.layout_tree(tree_root=0) # optional - networkx + sage: G = graphs.BalancedTree(2, 2) # needs networkx + sage: G.layout_tree(tree_root=0) # needs networkx {0: [1.5, 0], 1: [2.5, -1], 2: [0.5, -1], @@ -20031,8 +20044,8 @@ def layout_tree(self, tree_orientation="down", tree_root=None, 5: [1.0, -2], 6: [0.0, -2]} - sage: G = graphs.BalancedTree(2, 4) # optional - networkx - sage: G.plot(layout="tree", tree_root=0, tree_orientation="up") # optional - networkx sage.plot + sage: G = graphs.BalancedTree(2, 4) # needs networkx + sage: G.plot(layout="tree", tree_root=0, tree_orientation="up") # needs networkx sage.plot Graphics object consisting of 62 graphics primitives Using the embedding when it exists:: @@ -20050,13 +20063,13 @@ def layout_tree(self, tree_orientation="down", tree_root=None, 6: [2.0, -1], 7: [1.0, -2], 8: [0.0, -2]} - sage: T.plot(layout="tree", tree_root=3) # optional - sage.plot + sage: T.plot(layout="tree", tree_root=3) # needs sage.plot Graphics object consisting of 18 graphics primitives TESTS:: - sage: G = graphs.BalancedTree(2, 2) # optional - networkx - sage: G.layout_tree(tree_root=0, tree_orientation='left') # optional - networkx + sage: G = graphs.BalancedTree(2, 2) # needs networkx + sage: G.layout_tree(tree_root=0, tree_orientation='left') # needs networkx {0: [0, 1.5], 1: [-1, 2.5], 2: [-1, 0.5], @@ -20066,12 +20079,12 @@ def layout_tree(self, tree_orientation="down", tree_root=None, 6: [-2, 0.0]} sage: G = graphs.CycleGraph(3) - sage: G.plot(layout='tree') # optional - sage.plot + sage: G.plot(layout='tree') # needs sage.plot Traceback (most recent call last): ... RuntimeError: cannot use tree layout on this graph: self.is_tree() returns False sage: G = Graph(0) - sage: G.plot(layout='tree') # optional - sage.plot + sage: G.plot(layout='tree') # needs sage.plot Graphics object consisting of 0 graphics primitives """ if dim != 2: @@ -20238,8 +20251,8 @@ def layout_graphviz(self, dim=2, prog='dot', **options): Graphics object consisting of 29 graphics primitives sage: g.plot(layout="graphviz", prog="fdp") # optional - dot2tex graphviz Graphics object consisting of 29 graphics primitives - sage: g = graphs.BalancedTree(5,2) # optional - networkx - sage: g.plot(layout="graphviz", prog="circo") # optional - dot2tex graphviz networkx + sage: g = graphs.BalancedTree(5,2) # needs networkx + sage: g.plot(layout="graphviz", prog="circo") # optional - dot2tex graphviz, needs networkx Graphics object consisting of 62 graphics primitives .. TODO:: @@ -20364,7 +20377,7 @@ def _circle_embedding(self, vertices, center=(0, 0), radius=1, shift=0, angle=0, sage: g = graphs.CycleGraph(5) sage: g._circle_embedding([0, 2, 4, 1, 3], radius=2, shift=.5) - sage: g.show() # optional - sage.plot + sage: g.show() # needs sage.plot sage: g._circle_embedding(g.vertices(sort=True), angle=0) sage: g._pos[0] @@ -20444,7 +20457,7 @@ def _line_embedding(self, vertices, first=(0, 0), last=(0, 1), return_dict=False sage: g = graphs.PathGraph(5) sage: g._line_embedding([0, 2, 4, 1, 3], first=(-1, -1), last=(1, 1)) - sage: g.show() # optional - sage.plot + sage: g.show() # needs sage.plot sage: pos = g._line_embedding([4, 2, 0, 1, 3], first=(-1, -1), last=(1, 1), ....: return_dict=True) @@ -20514,19 +20527,20 @@ def graphplot(self, **options): sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0,0,'a'),(0,0,'b'),(0,1,'c'),(0,1,'d'), ....: (0,1,'e'),(0,1,'f'),(0,1,'f'),(2,1,'g'),(2,2,'h')]) - sage: GP = g.graphplot(edge_labels=True, color_by_label=True, # optional - sage.plot + sage: GP = g.graphplot(edge_labels=True, color_by_label=True, # needs sage.plot ....: edge_style='dashed') - sage: GP.plot() # optional - sage.plot + sage: GP.plot() # needs sage.plot Graphics object consisting of 22 graphics primitives We can modify the :class:`~sage.graphs.graph_plot.GraphPlot` object. Notice that the changes are cumulative:: - sage: GP.set_edges(edge_style='solid') # optional - sage.plot - sage: GP.plot() # optional - sage.plot + sage: # needs sage.plot + sage: GP.set_edges(edge_style='solid') + sage: GP.plot() Graphics object consisting of 22 graphics primitives - sage: GP.set_vertices(talk=True) # optional - sage.plot - sage: GP.plot() # optional - sage.plot + sage: GP.set_vertices(talk=True) + sage: GP.plot() Graphics object consisting of 22 graphics primitives """ from sage.graphs.graph_plot import GraphPlot @@ -20551,7 +20565,7 @@ def _rich_repr_(self, display_manager, **kwds): sage: dm.preferences.supplemental_plot 'never' sage: del dm.preferences.supplemental_plot - sage: graphs.RandomGNP(20,0.0) # optional - networkx + sage: graphs.RandomGNP(20,0.0) # needs networkx RandomGNP(20,0.000000000000000): Graph on 20 vertices (use the .plot() method to plot) sage: dm.preferences.supplemental_plot = 'never' """ @@ -20707,21 +20721,21 @@ def plot(self, **options): ....: x = float(0.5*cos(pi/2 + ((2*pi)/5)*i)) ....: y = float(0.5*sin(pi/2 + ((2*pi)/5)*i)) ....: pos_dict[i] = [x,y] - sage: pl = P.plot(pos=pos_dict, vertex_colors=d) # optional - sage.plot - sage: pl.show() # optional - sage.plot + sage: pl = P.plot(pos=pos_dict, vertex_colors=d) # needs sage.plot + sage: pl.show() # needs sage.plot :: sage: C = graphs.CubeGraph(8) - sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) # optional - sage.plot - sage: P.show() # optional - sage.plot + sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) # needs sage.plot + sage: P.show() # needs sage.plot :: sage: G = graphs.HeawoodGraph() sage: for u, v, l in G.edges(sort=False): ....: G.set_edge_label(u, v, '(' + str(u) + ',' + str(v) + ')') - sage: G.plot(edge_labels=True).show() # optional - sage.plot + sage: G.plot(edge_labels=True).show() # needs sage.plot :: @@ -20732,7 +20746,7 @@ def plot(self, **options): ....: 16: [17], 17: [18], 18: [19]}, sparse=True) sage: for u,v,l in D.edges(sort=False): ....: D.set_edge_label(u, v, '(' + str(u) + ',' + str(v) + ')') - sage: D.plot(edge_labels=True, layout='circular').show() # optional - sage.plot + sage: D.plot(edge_labels=True, layout='circular').show() # needs sage.plot :: @@ -20744,45 +20758,45 @@ def plot(self, **options): ....: for i in range(5): ....: if u[i] != v[i]: ....: edge_colors[R[i]].append((u, v, l)) - sage: C.plot(vertex_labels=False, vertex_size=0, # optional - sage.plot + sage: C.plot(vertex_labels=False, vertex_size=0, # needs sage.plot ....: edge_colors=edge_colors).show() :: sage: D = graphs.DodecahedralGraph() sage: Pi = [[6,5,15,14,7], [16,13,8,2,4], [12,17,9,3,1], [0,19,18,10,11]] - sage: D.show(partition=Pi) # optional - sage.plot + sage: D.show(partition=Pi) # needs sage.plot :: sage: G = graphs.PetersenGraph() sage: G.allow_loops(True) sage: G.add_edge(0, 0) - sage: G.show() # optional - sage.plot + sage: G.show() # needs sage.plot :: sage: D = DiGraph({0: [0, 1], 1: [2], 2: [3]}, loops=True) - sage: D.show() # optional - sage.plot - sage: D.show(edge_colors={(0, 1, 0): [(0, 1, None), (1, 2, None)], # optional - sage.plot + sage: D.show() # needs sage.plot + sage: D.show(edge_colors={(0, 1, 0): [(0, 1, None), (1, 2, None)], # needs sage.plot ....: (0, 0, 0): [(2, 3, None)]}) :: sage: pos = {0: [0.0, 1.5], 1: [-0.8, 0.3], 2: [-0.6, -0.8], 3: [0.6, -0.8], 4: [0.8, 0.3]} sage: g = Graph({0: [1], 1: [2], 2: [3], 3: [4], 4: [0]}) - sage: g.plot(pos=pos, layout='spring', iterations=0) # optional - sage.plot + sage: g.plot(pos=pos, layout='spring', iterations=0) # needs sage.plot Graphics object consisting of 11 graphics primitives :: sage: G = Graph() - sage: P = G.plot() # optional - sage.plot - sage: P.axes() # optional - sage.plot + sage: P = G.plot() # needs sage.plot + sage: P.axes() # needs sage.plot False sage: G = DiGraph() - sage: P = G.plot() # optional - sage.plot - sage: P.axes() # optional - sage.plot + sage: P = G.plot() # needs sage.plot + sage: P.axes() # needs sage.plot False :: @@ -20799,11 +20813,11 @@ def plot(self, **options): 7: (-0.29..., -0.40...), 8: (0.29..., -0.40...), 9: (0.47..., 0.15...)} - sage: P = G.plot(save_pos=True, layout='spring') # optional - sage.plot + sage: P = G.plot(save_pos=True, layout='spring') # needs sage.plot The following illustrates the format of a position dictionary:: - sage: G.get_pos() # currently random across platforms, see #9593 # optional - sage.plot + sage: G.get_pos() # currently random across platforms, see #9593 # needs sage.plot {0: [1.17..., -0.855...], 1: [1.81..., -0.0990...], 2: [1.35..., 0.184...], @@ -20819,14 +20833,14 @@ def plot(self, **options): sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) # optional - sage.plot + sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) # needs sage.plot Graphics object consisting of 14 graphics primitives :: sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) # optional - sage.plot + sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}) # needs sage.plot Graphics object consisting of 14 graphics primitives sage: t.set_edge_label(0, 1, -7) sage: t.set_edge_label(0, 5, 3) @@ -20835,7 +20849,7 @@ def plot(self, **options): sage: t.set_edge_label(3, 2, 'spam') sage: t.set_edge_label(2, 6, 3/2) sage: t.set_edge_label(0, 4, 66) - sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}, # optional - sage.plot + sage: t.plot(heights={0: [0], 1: [4, 5, 1], 2: [2], 3: [3, 6]}, # needs sage.plot ....: edge_labels=True) Graphics object consisting of 20 graphics primitives @@ -20843,32 +20857,32 @@ def plot(self, **options): sage: T = list(graphs.trees(7)) sage: t = T[3] - sage: t.plot(layout='tree') # optional - sage.plot + sage: t.plot(layout='tree') # needs sage.plot Graphics object consisting of 14 graphics primitives :: sage: t = DiGraph('JCC???@A??GO??CO??GO??') - sage: t.plot(layout='tree', tree_root=0, tree_orientation="up") # optional - sage.plot + sage: t.plot(layout='tree', tree_root=0, tree_orientation="up") # needs sage.plot Graphics object consisting of 22 graphics primitives sage: D = DiGraph({0: [1, 2, 3], 2: [1, 4], 3: [0]}) - sage: D.plot() # optional - sage.plot + sage: D.plot() # needs sage.plot Graphics object consisting of 16 graphics primitives sage: D = DiGraph(multiedges=True,sparse=True) sage: for i in range(5): ....: D.add_edge((i, i + 1, 'a')) ....: D.add_edge((i, i - 1, 'b')) - sage: D.plot(edge_labels=True, edge_colors=D._color_by_label()) # optional - sage.plot + sage: D.plot(edge_labels=True, edge_colors=D._color_by_label()) # needs sage.plot Graphics object consisting of 34 graphics primitives - sage: D.plot(edge_labels=True, color_by_label={'a': 'blue', 'b': 'red'}, # optional - sage.plot + sage: D.plot(edge_labels=True, color_by_label={'a': 'blue', 'b': 'red'}, # needs sage.plot ....: edge_style='dashed') Graphics object consisting of 34 graphics primitives sage: g = Graph({}, loops=True, multiedges=True, sparse=True) sage: g.add_edges([(0, 0, 'a'), (0, 0, 'b'), (0, 1, 'c'), (0, 1, 'd'), ....: (0, 1, 'e'), (0, 1, 'f'), (0, 1, 'f'), (2, 1, 'g'), (2, 2, 'h')]) - sage: g.plot(edge_labels=True, color_by_label=True, edge_style='dashed') # optional - sage.plot + sage: g.plot(edge_labels=True, color_by_label=True, edge_style='dashed') # needs sage.plot Graphics object consisting of 22 graphics primitives :: @@ -20876,21 +20890,21 @@ def plot(self, **options): sage: S = SupersingularModule(389) sage: H = S.hecke_matrix(2) sage: D = DiGraph(H, sparse=True) - sage: P = D.plot() # optional - sage.plot + sage: P = D.plot() # needs sage.plot :: sage: G = Graph({'a': ['a','b','b','b','e'], 'b': ['c','d','e'], ....: 'c':['c','d','d','d'],'d':['e']}, sparse=True) - sage: G.show(pos={'a':[0,1],'b':[1,1],'c':[2,0],'d':[1,0],'e':[0,0]}) # optional - sage.plot + sage: G.show(pos={'a':[0,1],'b':[1,1],'c':[2,0],'d':[1,0],'e':[0,0]}) # needs sage.plot TESTS:: sage: G = DiGraph({0: {1: 'a', 2: 'a'}, 1: {0: 'b'}, 2: {0: 'c'}}) - sage: p = G.plot(edge_labels=True, # optional - sage.plot + sage: p = G.plot(edge_labels=True, # needs sage.plot ....: color_by_label={'a': 'yellow', 'b': 'purple'}); p Graphics object consisting of 14 graphics primitives - sage: sorted(x.options()['rgbcolor'] for x in p # optional - sage.plot + sage: sorted(x.options()['rgbcolor'] for x in p # needs sage.plot ....: if isinstance(x, sage.plot.arrow.CurveArrow)) ['black', 'purple', 'yellow', 'yellow'] """ @@ -20917,8 +20931,8 @@ def show(self, method="matplotlib", **kwds): EXAMPLES:: sage: C = graphs.CubeGraph(8) - sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) # optional - sage.plot - sage: P.show() # long time (3s on sage.math, 2011) # optional - sage.plot + sage: P = C.plot(vertex_labels=False, vertex_size=0, graph_border=True) # needs sage.plot + sage: P.show() # long time (3s on sage.math, 2011), needs sage.plot """ if method == "js": @@ -21006,15 +21020,15 @@ def plot3d(self, bgcolor=(1, 1, 1), EXAMPLES:: sage: G = graphs.CubeGraph(5) - sage: G.plot3d(iterations=500, edge_size=None, vertex_size=0.04) # long time, optional - sage.plot + sage: G.plot3d(iterations=500, edge_size=None, vertex_size=0.04) # long time, needs sage.plot Graphics3d Object We plot a fairly complicated Cayley graph:: - sage: A5 = AlternatingGroup(5); A5 # optional - sage.groups + sage: A5 = AlternatingGroup(5); A5 # needs sage.groups Alternating group of order 5!/2 as a permutation group - sage: G = A5.cayley_graph() # optional - sage.groups - sage: G.plot3d(vertex_size=0.03, edge_size=0.01, # long time, optional - sage.groups sage.plot + sage: G = A5.cayley_graph() # needs sage.groups + sage: G.plot3d(vertex_size=0.03, edge_size=0.01, # long time # needs sage.groups sage.plot ....: vertex_colors={(1,1,1): list(G)}, bgcolor=(0,0,0), ....: color_by_label=True, iterations=200) Graphics3d Object @@ -21022,26 +21036,26 @@ def plot3d(self, bgcolor=(1, 1, 1), Some :class:`~sage.plot.plot3d.tachyon.Tachyon` examples:: sage: D = graphs.DodecahedralGraph() - sage: P3D = D.plot3d(engine='tachyon') # optional - sage.plot - sage: P3D.show() # long time, optional - sage.plot + sage: P3D = D.plot3d(engine='tachyon') # needs sage.plot + sage: P3D.show() # long time # needs sage.plot :: sage: G = graphs.PetersenGraph() - sage: G.plot3d(engine='tachyon', # long time, optional - sage.plot + sage: G.plot3d(engine='tachyon', # long time # needs sage.plot ....: vertex_colors={(0,0,1): list(G)}).show() :: sage: C = graphs.CubeGraph(4) - sage: C.plot3d(engine='tachyon', # long time, optional - sage.plot + sage: C.plot3d(engine='tachyon', # long time # needs sage.plot ....: edge_colors={(0,1,0): C.edges(sort=False)}, ....: vertex_colors={(1,1,1): list(C)}, bgcolor=(0,0,0)).show() :: sage: K = graphs.CompleteGraph(3) - sage: K.plot3d(engine='tachyon', # long time, optional - sage.plot + sage: K.plot3d(engine='tachyon', # long time # needs sage.plot ....: edge_colors={(1,0,0): [(0,1,None)], ....: (0,1,0): [(0,2,None)], ....: (0,0,1): [(1,2,None)]}).show() @@ -21055,7 +21069,7 @@ def plot3d(self, bgcolor=(1, 1, 1), ....: 8: [9], 9: [10, 13], 10: [11], 11: [12, 18], ....: 12: [16, 13], 13: [14], 14: [15], 15: [16], 16: [17], ....: 17: [18], 18: [19], 19: []}) - sage: D.plot3d().show() # long time, optional - sage.plot + sage: D.plot3d().show() # long time # needs sage.plot :: @@ -21063,7 +21077,7 @@ def plot3d(self, bgcolor=(1, 1, 1), sage: from sage.plot.colors import rainbow sage: R = rainbow(P.size(), 'rgbtuple') sage: edge_colors = {R[i]: [e] for i, e in enumerate(P.edge_iterator())} - sage: P.plot3d(engine='tachyon', edge_colors=edge_colors).show() # long time, optional - sage.plot + sage: P.plot3d(engine='tachyon', edge_colors=edge_colors).show() # long time, needs sage.plot :: @@ -21078,24 +21092,24 @@ def plot3d(self, bgcolor=(1, 1, 1), Using the ``partition`` keyword:: sage: G = graphs.WheelGraph(7) - sage: G.plot3d(partition=[[0], [1, 2, 3, 4, 5, 6]]) # optional - sage.plot + sage: G.plot3d(partition=[[0], [1, 2, 3, 4, 5, 6]]) # needs sage.plot Graphics3d Object TESTS:: sage: G = DiGraph({0: {1: 'a', 2: 'a'}, 1: {0: 'b'}, 2: {0: 'c'}}) - sage: p = G.plot3d(edge_labels=True, # optional - sage.plot + sage: p = G.plot3d(edge_labels=True, # needs sage.plot ....: color_by_label={'a': 'yellow', 'b': 'cyan'}) - sage: s = p.x3d_str() # optional - sage.plot + sage: s = p.x3d_str() # needs sage.plot This 3D plot contains four yellow objects (two cylinders and two cones), two black objects and 2 cyan objects:: - sage: s.count("Material diffuseColor='1.0 1.0 0.0'") # optional - sage.plot + sage: s.count("Material diffuseColor='1.0 1.0 0.0'") # needs sage.plot 4 - sage: s.count("Material diffuseColor='0.0 0.0 0.0'") # optional - sage.plot + sage: s.count("Material diffuseColor='0.0 0.0 0.0'") # needs sage.plot 2 - sage: s.count("Material diffuseColor='0.0 1.0 1.0'") # optional - sage.plot + sage: s.count("Material diffuseColor='0.0 1.0 1.0'") # needs sage.plot 2 .. SEEALSO:: @@ -21264,14 +21278,14 @@ def show3d(self, bgcolor=(1, 1, 1), vertex_colors=None, vertex_size=0.06, EXAMPLES:: sage: G = graphs.CubeGraph(5) - sage: G.show3d(iterations=500, edge_size=None, vertex_size=0.04) # long time, optional - sage.plot + sage: G.show3d(iterations=500, edge_size=None, vertex_size=0.04) # long time, needs sage.plot We plot a fairly complicated Cayley graph:: - sage: A5 = AlternatingGroup(5); A5 # optional - sage.groups + sage: A5 = AlternatingGroup(5); A5 # needs sage.groups Alternating group of order 5!/2 as a permutation group - sage: G = A5.cayley_graph() # optional - sage.groups - sage: G.show3d(vertex_size=0.03, # long time, optional - sage.groups sage.plot + sage: G = A5.cayley_graph() # needs sage.groups + sage: G.show3d(vertex_size=0.03, # long time # needs sage.groups sage.plot ....: edge_size=0.01, edge_size2=0.02, ....: vertex_colors={(1,1,1): list(G)}, bgcolor=(0,0,0), ....: color_by_label=True, iterations=200) @@ -21279,25 +21293,25 @@ def show3d(self, bgcolor=(1, 1, 1), vertex_colors=None, vertex_size=0.06, Some :class:`~sage.plot.plot3d.tachyon.Tachyon` examples:: sage: D = graphs.DodecahedralGraph() - sage: D.show3d(engine='tachyon') # long time, optional - sage.plot + sage: D.show3d(engine='tachyon') # long time # needs sage.plot :: sage: G = graphs.PetersenGraph() - sage: G.show3d(engine='tachyon', # long time, optional - sage.plot + sage: G.show3d(engine='tachyon', # long time # needs sage.plot ....: vertex_colors={(0,0,1): list(G)}) :: sage: C = graphs.CubeGraph(4) - sage: C.show3d(engine='tachyon', # long time, optional - sage.plot + sage: C.show3d(engine='tachyon', # long time # needs sage.plot ....: edge_colors={(0,1,0): C.edges(sort=False)}, ....: vertex_colors={(1,1,1): list(C)}, bgcolor=(0,0,0)) :: sage: K = graphs.CompleteGraph(3) - sage: K.show3d(engine='tachyon', # long time, optional - sage.plot + sage: K.show3d(engine='tachyon', # long time # needs sage.plot ....: edge_colors={(1,0,0): [(0, 1, None)], ....: (0, 1, 0): [(0, 2, None)], ....: (0, 0, 1): [(1, 2, None)]}) @@ -21460,12 +21474,13 @@ def graphviz_string(self, **options): A digraph using latex labels for vertices and edges:: - sage: f(x) = -1 / x # optional - sage.symbolic - sage: g(x) = 1 / (x + 1) # optional - sage.symbolic - sage: G = DiGraph() # optional - sage.symbolic - sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) # optional - sage.symbolic - sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) # optional - sage.symbolic - sage: print(G.graphviz_string(labels="latex", # random # optional - sage.symbolic + sage: # needs sage.symbolic + sage: f(x) = -1 / x + sage: g(x) = 1 / (x + 1) + sage: G = DiGraph() + sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) + sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) + sage: print(G.graphviz_string(labels="latex", # random ....: edge_labels=True)) digraph { node [shape="plaintext"]; @@ -21492,7 +21507,7 @@ def graphviz_string(self, **options): node_4 -> node_9 [label=" ", texlbl="$x \ {\mapsto}\ \frac{1}{x + 1}$"]; } - sage: print(G.graphviz_string(labels="latex", # random # optional - sage.symbolic + sage: print(G.graphviz_string(labels="latex", # random # needs sage.symbolic ....: color_by_label=True)) digraph { node [shape="plaintext"]; @@ -21519,7 +21534,7 @@ def graphviz_string(self, **options): node_4 -> node_9 [color = "#00ffff"]; } - sage: print(G.graphviz_string(labels="latex", # random # optional - sage.symbolic + sage: print(G.graphviz_string(labels="latex", # random # needs sage.symbolic ....: color_by_label={f: "red", g: "blue"})) digraph { node [shape="plaintext"]; @@ -21607,7 +21622,7 @@ def graphviz_string(self, **options): sage: def edge_options(data): ....: u, v, label = data ....: return {"dir":"back"} if u == 1 else {} - sage: print(G.graphviz_string(edge_options=edge_options)) # random # optional - sage.symbolic + sage: print(G.graphviz_string(edge_options=edge_options)) # random # needs sage.symbolic digraph { node_0 [label="-1"]; node_1 [label="-1/2"]; @@ -21641,7 +21656,7 @@ def graphviz_string(self, **options): ....: if (u,v) == (1, -1): options["label_style"] = "latex" ....: if (u,v) == (1, 1/2): options["dir"] = "back" ....: return options - sage: print(G.graphviz_string(edge_options=edge_options)) # random # optional - sage.symbolic + sage: print(G.graphviz_string(edge_options=edge_options)) # random # needs sage.symbolic digraph { node_0 [label="-1"]; node_1 [label="-1/2"]; @@ -21737,12 +21752,13 @@ def graphviz_string(self, **options): The following digraph has vertices with newlines in their string representations:: - sage: m1 = matrix(3, 3) # optional - sage.modules - sage: m2 = matrix(3, 3, 1) # optional - sage.modules - sage: m1.set_immutable() # optional - sage.modules - sage: m2.set_immutable() # optional - sage.modules - sage: g = DiGraph({m1: [m2]}) # optional - sage.modules - sage: print(g.graphviz_string()) # optional - sage.modules + sage: # needs sage.modules + sage: m1 = matrix(3, 3) + sage: m2 = matrix(3, 3, 1) + sage: m1.set_immutable() + sage: m2.set_immutable() + sage: g = DiGraph({m1: [m2]}) + sage: print(g.graphviz_string()) digraph { node_0 [label="[0 0 0]\n\ [0 0 0]\n\ @@ -22053,26 +22069,26 @@ def spectrum(self, laplacian=False): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.spectrum() # optional - sage.modules + sage: P.spectrum() # needs sage.modules [3, 1, 1, 1, 1, 1, -2, -2, -2, -2] - sage: P.spectrum(laplacian=True) # optional - sage.modules + sage: P.spectrum(laplacian=True) # needs sage.modules [5, 5, 5, 5, 2, 2, 2, 2, 2, 0] sage: D = P.to_directed() sage: D.delete_edge(7, 9) - sage: D.spectrum() # optional - sage.modules + sage: D.spectrum() # needs sage.modules [2.9032119259..., 1, 1, 1, 1, 0.8060634335..., -1.7092753594..., -2, -2, -2] :: sage: C = graphs.CycleGraph(8) - sage: C.spectrum() # optional - sage.modules + sage: C.spectrum() # needs sage.modules [2, 1.4142135623..., 1.4142135623..., 0, 0, -1.4142135623..., -1.4142135623..., -2] A digraph may have complex eigenvalues. Previously, the complex parts of graph eigenvalues were being dropped. For a 3-cycle, we have:: sage: T = DiGraph({0: [1], 1: [2], 2: [0]}) - sage: T.spectrum() # optional - sage.modules + sage: T.spectrum() # needs sage.modules [1, -0.5000000000... + 0.8660254037...*I, -0.5000000000... - 0.8660254037...*I] TESTS: @@ -22086,10 +22102,10 @@ def spectrum(self, laplacian=False): eigenvalues. :: sage: H = graphs.HoffmanSingletonGraph() - sage: evals = H.spectrum() # optional - sage.modules - sage: lap = [7 - x for x in evals] # optional - sage.modules - sage: lap.sort(reverse=True) # optional - sage.modules - sage: lap == H.spectrum(laplacian=True) # optional - sage.modules + sage: evals = H.spectrum() # needs sage.modules + sage: lap = [7 - x for x in evals] # needs sage.modules + sage: lap.sort(reverse=True) # needs sage.modules + sage: lap == H.spectrum(laplacian=True) # needs sage.modules True """ # Ideally the spectrum should return something like a Factorization object @@ -22135,11 +22151,11 @@ def characteristic_polynomial(self, var='x', laplacian=False): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.characteristic_polynomial() # optional - sage.modules + sage: P.characteristic_polynomial() # needs sage.modules x^10 - 15*x^8 + 75*x^6 - 24*x^5 - 165*x^4 + 120*x^3 + 120*x^2 - 160*x + 48 - sage: P.charpoly() # optional - sage.modules + sage: P.charpoly() # needs sage.modules x^10 - 15*x^8 + 75*x^6 - 24*x^5 - 165*x^4 + 120*x^3 + 120*x^2 - 160*x + 48 - sage: P.characteristic_polynomial(laplacian=True) # optional - sage.modules + sage: P.characteristic_polynomial(laplacian=True) # needs sage.modules x^10 - 30*x^9 + 390*x^8 - 2880*x^7 + 13305*x^6 - 39882*x^5 + 77640*x^4 - 94800*x^3 + 66000*x^2 - 20000*x """ @@ -22175,7 +22191,7 @@ def eigenvectors(self, laplacian=False): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.eigenvectors() # optional - sage.modules + sage: P.eigenvectors() # needs sage.modules [(3, [ (1, 1, 1, 1, 1, 1, 1, 1, 1, 1) ], 1), (-2, [ @@ -22195,7 +22211,7 @@ def eigenvectors(self, laplacian=False): graph is regular. However, since the output also contains the eigenvalues, the two outputs are slightly different:: - sage: P.eigenvectors(laplacian=True) # optional - sage.modules + sage: P.eigenvectors(laplacian=True) # needs sage.modules [(0, [ (1, 1, 1, 1, 1, 1, 1, 1, 1, 1) ], 1), (5, [ @@ -22214,7 +22230,7 @@ def eigenvectors(self, laplacian=False): :: sage: C = graphs.CycleGraph(8) - sage: C.eigenvectors() # optional - sage.modules + sage: C.eigenvectors() # needs sage.modules [(2, [ (1, 1, 1, 1, 1, 1, 1, 1) @@ -22244,7 +22260,7 @@ def eigenvectors(self, laplacian=False): graph eigenvalues were being dropped. For a 3-cycle, we have:: sage: T = DiGraph({0:[1], 1:[2], 2:[0]}) - sage: T.eigenvectors() # optional - sage.modules + sage: T.eigenvectors() # needs sage.modules [(1, [ (1, 1, 1) @@ -22285,7 +22301,7 @@ def eigenspaces(self, laplacian=False): EXAMPLES:: sage: P = graphs.PetersenGraph() - sage: P.eigenspaces() # optional - sage.modules + sage: P.eigenspaces() # needs sage.modules [ (3, Vector space of degree 10 and dimension 1 over Rational Field User basis matrix: @@ -22309,7 +22325,7 @@ def eigenspaces(self, laplacian=False): graph is regular. However, since the output also contains the eigenvalues, the two outputs are slightly different:: - sage: P.eigenspaces(laplacian=True) # optional - sage.modules + sage: P.eigenspaces(laplacian=True) # needs sage.modules [ (0, Vector space of degree 10 and dimension 1 over Rational Field User basis matrix: @@ -22334,7 +22350,7 @@ def eigenspaces(self, laplacian=False): corresponding eigenspace:: sage: C = graphs.CycleGraph(8) - sage: C.eigenspaces() # optional - sage.modules + sage: C.eigenspaces() # needs sage.modules [ (2, Vector space of degree 8 and dimension 1 over Rational Field User basis matrix: @@ -22357,7 +22373,7 @@ def eigenspaces(self, laplacian=False): we have:: sage: T = DiGraph({0: [1], 1: [2], 2: [0]}) - sage: T.eigenspaces() # optional - sage.modules + sage: T.eigenspaces() # needs sage.modules [ (1, Vector space of degree 3 and dimension 1 over Rational Field User basis matrix: @@ -22435,7 +22451,7 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c EXAMPLES:: sage: G = graphs.PathGraph(3) - sage: G.am() # optional - sage.modules + sage: G.am() # needs sage.modules [0 1 0] [1 0 1] [0 1 0] @@ -22443,7 +22459,7 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c Relabeling using a dictionary. Note that the dictionary does not define the new label of vertex `0`:: - sage: G.relabel({1:2,2:1}, inplace=False).am() # optional - sage.modules + sage: G.relabel({1:2,2:1}, inplace=False).am() # needs sage.modules [0 0 1] [0 0 1] [1 1 0] @@ -22453,7 +22469,7 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c vertices have an image can require some time, and this feature can be disabled (at your own risk):: - sage: G.relabel({1:2,2:1}, inplace=False, # optional - sage.modules + sage: G.relabel({1:2,2:1}, inplace=False, # needs sage.modules ....: complete_partial_function=False).am() Traceback (most recent call last): ... @@ -22461,14 +22477,14 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c Relabeling using a list:: - sage: G.relabel([0,2,1], inplace=False).am() # optional - sage.modules + sage: G.relabel([0,2,1], inplace=False).am() # needs sage.modules [0 0 1] [0 0 1] [1 1 0] Relabeling using an iterable:: - sage: G.relabel(iter((0,2,1)), inplace=False).am() # optional - sage.modules + sage: G.relabel(iter((0,2,1)), inplace=False).am() # needs sage.modules [0 0 1] [0 0 1] [1 1 0] @@ -22476,10 +22492,10 @@ def relabel(self, perm=None, inplace=True, return_map=False, check_input=True, c Relabeling using a Sage permutation:: sage: G = graphs.PathGraph(3) - sage: from sage.groups.perm_gps.permgroup_named import SymmetricGroup # optional - sage.groups - sage: S = SymmetricGroup(3) # optional - sage.groups - sage: gamma = S('(1,2)') # optional - sage.groups - sage: G.relabel(gamma, inplace=False).am() # optional - sage.groups sage.modules + sage: from sage.groups.perm_gps.permgroup_named import SymmetricGroup # needs sage.groups + sage: S = SymmetricGroup(3) # needs sage.groups + sage: gamma = S('(1,2)') # needs sage.groups + sage: G.relabel(gamma, inplace=False).am() # needs sage.groups sage.modules [0 0 1] [0 0 1] [1 1 0] @@ -22762,7 +22778,7 @@ def is_equitable(self, partition, quotient_matrix=False): False sage: G.is_equitable([[0,4],[1,3,5,9],[2,6,8,7]]) True - sage: G.is_equitable([[0,4],[1,3,5,9],[2,6,8,7]], quotient_matrix=True) # optional - sage.modules + sage: G.is_equitable([[0,4],[1,3,5,9],[2,6,8,7]], quotient_matrix=True) # needs sage.modules [1 2 0] [1 0 2] [0 2 1] @@ -22948,7 +22964,7 @@ def automorphism_group(self, partition=None, verbosity=0, sage: graphs_query = GraphQuery(display_cols=['graph6'],num_vertices=4) sage: L = graphs_query.get_graphs_list() - sage: graphs_list.show_graphs(L) # optional - sage.plot + sage: graphs_list.show_graphs(L) # needs sage.plot sage: for g in L: ....: G = g.automorphism_group() ....: G.order(), G.gens() @@ -23051,10 +23067,11 @@ def automorphism_group(self, partition=None, verbosity=0, One can also use the faster algorithm for computing the automorphism group of the graph - bliss:: - sage: G = graphs.HallJankoGraph() # optional - bliss - sage: A1 = G.automorphism_group() # optional - bliss - sage: A2 = G.automorphism_group(algorithm='bliss') # optional - bliss - sage: A1.is_isomorphic(A2) # optional - bliss + sage: # optional - bliss + sage: G = graphs.HallJankoGraph() + sage: A1 = G.automorphism_group() + sage: A2 = G.automorphism_group(algorithm='bliss') + sage: A1.is_isomorphic(A2) True TESTS: @@ -23286,13 +23303,13 @@ def is_vertex_transitive(self, partition=None, verbosity=0, sage: G.is_vertex_transitive() False sage: P = graphs.PetersenGraph() - sage: P.is_vertex_transitive() # optional - sage.groups + sage: P.is_vertex_transitive() # needs sage.groups True sage: D = graphs.DodecahedralGraph() - sage: D.is_vertex_transitive() # optional - sage.groups + sage: D.is_vertex_transitive() # needs sage.groups True - sage: R = graphs.RandomGNP(2000, .01) # optional - networkx - sage: R.is_vertex_transitive() # optional - networkx + sage: R = graphs.RandomGNP(2000, .01) # needs networkx + sage: R.is_vertex_transitive() # needs networkx False """ if partition is None: @@ -23424,21 +23441,21 @@ def is_isomorphic(self, other, certificate=False, verbosity=0, edge_labels=False Graphs:: - sage: from sage.groups.perm_gps.permgroup_named import SymmetricGroup # optional - sage.groups + sage: from sage.groups.perm_gps.permgroup_named import SymmetricGroup # needs sage.groups sage: D = graphs.DodecahedralGraph() sage: E = copy(D) - sage: gamma = SymmetricGroup(20).random_element() # optional - sage.groups - sage: E.relabel(gamma) # optional - sage.groups + sage: gamma = SymmetricGroup(20).random_element() # needs sage.groups + sage: E.relabel(gamma) # needs sage.groups sage: D.is_isomorphic(E) True :: sage: D = graphs.DodecahedralGraph() - sage: S = SymmetricGroup(20) # optional - sage.groups - sage: gamma = S.random_element() # optional - sage.groups - sage: E = copy(D) # optional - sage.groups - sage: E.relabel(gamma) # optional - sage.groups + sage: S = SymmetricGroup(20) # needs sage.groups + sage: gamma = S.random_element() # needs sage.groups + sage: E = copy(D) # needs sage.groups + sage: E.relabel(gamma) # needs sage.groups sage: a,b = D.is_isomorphic(E, certificate=True); a True sage: from sage.graphs.generic_graph_pyx import spring_layout_fast @@ -23803,7 +23820,7 @@ class by some canonization function `c`. If `G` and `H` are graphs, sage: P = graphs.PetersenGraph() sage: DP = P.to_directed() - sage: DP.canonical_label(algorithm='sage').adjacency_matrix() # optional - sage.modules + sage: DP.canonical_label(algorithm='sage').adjacency_matrix() # needs sage.modules [0 0 0 0 0 0 0 1 1 1] [0 0 0 0 1 0 1 0 0 1] [0 0 0 1 0 0 1 0 1 0] @@ -24027,15 +24044,15 @@ def is_cayley(self, return_group=False, mapping=False, A Petersen Graph is not a Cayley graph:: sage: g = graphs.PetersenGraph() - sage: g.is_cayley() # optional - sage.groups + sage: g.is_cayley() # needs sage.groups False A Cayley digraph is a Cayley graph:: - sage: C7 = groups.permutation.Cyclic(7) # optional - sage.groups + sage: C7 = groups.permutation.Cyclic(7) # needs sage.groups sage: S = [(1,2,3,4,5,6,7), (1,3,5,7,2,4,6), (1,5,2,6,3,7,4)] - sage: d = C7.cayley_graph(generators=S) # optional - sage.groups - sage: d.is_cayley() # optional - sage.groups + sage: d = C7.cayley_graph(generators=S) # needs sage.groups + sage: d.is_cayley() # needs sage.groups True Graphs with loops and multiedges will have identity and repeated @@ -24310,7 +24327,7 @@ def katz_matrix(self, alpha, nonedgesonly=False, vertices=None): We find the Katz matrix of an undirected 4-cycle. :: sage: G = graphs.CycleGraph(4) - sage: G.katz_matrix(1/20) # optional - sage.modules + sage: G.katz_matrix(1/20) # needs sage.modules [1/198 5/99 1/198 5/99] [ 5/99 1/198 5/99 1/198] [1/198 5/99 1/198 5/99] @@ -24319,7 +24336,7 @@ def katz_matrix(self, alpha, nonedgesonly=False, vertices=None): We find the Katz matrix of an undirected 4-cycle with all entries other than those which correspond to non-edges zeroed out. :: - sage: G.katz_matrix(1/20, True) # optional - sage.modules + sage: G.katz_matrix(1/20, True) # needs sage.modules [ 0 0 1/198 0] [ 0 0 0 1/198] [1/198 0 0 0] @@ -24331,7 +24348,7 @@ def katz_matrix(self, alpha, nonedgesonly=False, vertices=None): We find the Katz matrix in a fan on 6 vertices. :: sage: H = Graph([(0,1),(0,2),(0,3),(0,4),(0,5),(0,6),(1,2),(2,3),(3,4),(4,5)]) - sage: H.katz_matrix(1/10) # optional - sage.modules + sage: H.katz_matrix(1/10) # needs sage.modules [ 169/2256 545/4512 25/188 605/4512 25/188 545/4512 485/4512] [ 545/4512 7081/297792 4355/37224 229/9024 595/37224 4073/297792 109/9024] [ 25/188 4355/37224 172/4653 45/376 125/4653 595/37224 5/376] @@ -24347,22 +24364,23 @@ def katz_matrix(self, alpha, nonedgesonly=False, vertices=None): TESTS:: - sage: (graphs.CompleteGraph(4)).katz_matrix(1/4) # optional - sage.modules + sage: # needs sage.modules + sage: (graphs.CompleteGraph(4)).katz_matrix(1/4) [3/5 4/5 4/5 4/5] [4/5 3/5 4/5 4/5] [4/5 4/5 3/5 4/5] [4/5 4/5 4/5 3/5] - sage: (graphs.CompleteGraph(4)).katz_matrix(1/4, nonedgesonly=True) # optional - sage.modules + sage: (graphs.CompleteGraph(4)).katz_matrix(1/4, nonedgesonly=True) [0 0 0 0] [0 0 0 0] [0 0 0 0] [0 0 0 0] - sage: (graphs.PathGraph(4)).katz_matrix(1/4, nonedgesonly=False) # optional - sage.modules + sage: (graphs.PathGraph(4)).katz_matrix(1/4, nonedgesonly=False) [15/209 60/209 16/209 4/209] [60/209 31/209 64/209 16/209] [16/209 64/209 31/209 60/209] [ 4/209 16/209 60/209 15/209] - sage: (graphs.PathGraph(4)).katz_matrix(1/4, nonedgesonly=True) # optional - sage.modules + sage: (graphs.PathGraph(4)).katz_matrix(1/4, nonedgesonly=True) [ 0 0 16/209 4/209] [ 0 0 0 16/209] [16/209 0 0 0] @@ -24427,7 +24445,7 @@ def katz_centrality(self, alpha, u=None): all 4 vertices have the same centrality) :: sage: G = graphs.CycleGraph(4) - sage: G.katz_centrality(1/20) # optional - sage.modules + sage: G.katz_centrality(1/20) # needs sage.modules {0: 1/9, 1: 1/9, 2: 1/9, 3: 1/9} Note that in the below example the nodes having indegree `0` also have @@ -24436,7 +24454,7 @@ def katz_centrality(self, alpha, u=None): sage: G = DiGraph({1: [10], 2:[10,11], 3:[10,11], 4:[], 5:[11, 4], 6:[11], ....: 7:[10,11], 8:[10,11], 9:[10], 10:[11, 5, 8], 11:[6]}) - sage: G.katz_centrality(.85) # rel tol 1e-14 # optional - sage.modules + sage: G.katz_centrality(.85) # rel tol 1e-14 # needs sage.modules {1: 0.000000000000000, 2: 0.000000000000000, 3: 0.000000000000000, @@ -24457,15 +24475,16 @@ def katz_centrality(self, alpha, u=None): TESTS:: - sage: graphs.PathGraph(3).katz_centrality(1/20) # optional - sage.modules + sage: # needs sage.modules + sage: graphs.PathGraph(3).katz_centrality(1/20) {0: 11/199, 1: 21/199, 2: 11/199} - sage: graphs.PathGraph(4).katz_centrality(1/20) # optional - sage.modules + sage: graphs.PathGraph(4).katz_centrality(1/20) {0: 21/379, 1: 41/379, 2: 41/379, 3: 21/379} - sage: graphs.PathGraph(3).katz_centrality(1/20,2) # optional - sage.modules + sage: graphs.PathGraph(3).katz_centrality(1/20,2) 11/199 - sage: graphs.PathGraph(4).katz_centrality(1/20,3) # optional - sage.modules + sage: graphs.PathGraph(4).katz_centrality(1/20,3) 21/379 - sage: (graphs.PathGraph(3) + graphs.PathGraph(4)).katz_centrality(1/20) # optional - sage.modules + sage: (graphs.PathGraph(3) + graphs.PathGraph(4)).katz_centrality(1/20) {0: 11/199, 1: 21/199, 2: 11/199, 3: 21/379, 4: 41/379, 5: 41/379, 6: 21/379} """ @@ -24516,29 +24535,29 @@ def edge_polytope(self, backend=None): The EP of a `4`-cycle is a square:: sage: G = graphs.CycleGraph(4) - sage: P = G.edge_polytope(); P # optional - sage.geometry.polyhedron + sage: P = G.edge_polytope(); P # needs sage.geometry.polyhedron A 2-dimensional polyhedron in ZZ^4 defined as the convex hull of 4 vertices The EP of a complete graph on `4` vertices is cross polytope:: sage: G = graphs.CompleteGraph(4) - sage: P = G.edge_polytope(); P # optional - sage.geometry.polyhedron + sage: P = G.edge_polytope(); P # needs sage.geometry.polyhedron A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 6 vertices - sage: P.is_combinatorially_isomorphic(polytopes.cross_polytope(3)) # optional - sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(polytopes.cross_polytope(3)) # needs sage.geometry.polyhedron True The EP of a graph is isomorphic to the subdirect sum of its connected components EPs:: sage: n = randint(3, 6) - sage: G1 = graphs.RandomGNP(n, 0.2) # optional - networkx + sage: G1 = graphs.RandomGNP(n, 0.2) # needs networkx sage: n = randint(3, 6) - sage: G2 = graphs.RandomGNP(n, 0.2) # optional - networkx - sage: G = G1.disjoint_union(G2) # optional - networkx - sage: P = G.edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P1 = G1.edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P2 = G2.edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P.is_combinatorially_isomorphic(P1.subdirect_sum(P2)) # optional - networkx sage.geometry.polyhedron + sage: G2 = graphs.RandomGNP(n, 0.2) # needs networkx + sage: G = G1.disjoint_union(G2) # needs networkx + sage: P = G.edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P1 = G1.edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P2 = G2.edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(P1.subdirect_sum(P2)) # needs networkx sage.geometry.polyhedron True All trees on `n` vertices have isomorphic EPs:: @@ -24546,9 +24565,9 @@ def edge_polytope(self, backend=None): sage: n = randint(4, 10) sage: G1 = graphs.RandomTree(n) sage: G2 = graphs.RandomTree(n) - sage: P1 = G1.edge_polytope() # optional - sage.geometry.polyhedron - sage: P2 = G2.edge_polytope() # optional - sage.geometry.polyhedron - sage: P1.is_combinatorially_isomorphic(P2) # optional - sage.geometry.polyhedron + sage: P1 = G1.edge_polytope() # needs sage.geometry.polyhedron + sage: P2 = G2.edge_polytope() # needs sage.geometry.polyhedron + sage: P1.is_combinatorially_isomorphic(P2) # needs sage.geometry.polyhedron True However, there are still many different EPs:: @@ -24556,14 +24575,14 @@ def edge_polytope(self, backend=None): sage: len(list(graphs(5))) 34 sage: polys = [] - sage: for G in graphs(5): # optional - sage.geometry.polyhedron + sage: for G in graphs(5): # needs sage.geometry.polyhedron ....: P = G.edge_polytope() ....: for P1 in polys: ....: if P.is_combinatorially_isomorphic(P1): ....: break ....: else: ....: polys.append(P) - sage: len(polys) # optional - sage.geometry.polyhedron + sage: len(polys) # needs sage.geometry.polyhedron 19 TESTS: @@ -24571,7 +24590,7 @@ def edge_polytope(self, backend=None): Obtain the EP with unsortable vertices:: sage: G = Graph([[1, (1, 2)]]) - sage: G.edge_polytope() # optional - sage.geometry.polyhedron + sage: G.edge_polytope() # needs sage.geometry.polyhedron A 0-dimensional polyhedron in ZZ^2 defined as the convex hull of 1 vertex """ from sage.matrix.special import identity_matrix @@ -24602,17 +24621,17 @@ def symmetric_edge_polytope(self, backend=None): The SEP of a `4`-cycle is a cube:: sage: G = graphs.CycleGraph(4) - sage: P = G.symmetric_edge_polytope(); P # optional - sage.geometry.polyhedron + sage: P = G.symmetric_edge_polytope(); P # needs sage.geometry.polyhedron A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 8 vertices - sage: P.is_combinatorially_isomorphic(polytopes.cube()) # optional - sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(polytopes.cube()) # needs sage.geometry.polyhedron True The SEP of a complete graph on `4` vertices is a cuboctahedron:: sage: G = graphs.CompleteGraph(4) - sage: P = G.symmetric_edge_polytope(); P # optional - sage.geometry.polyhedron + sage: P = G.symmetric_edge_polytope(); P # needs sage.geometry.polyhedron A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 12 vertices - sage: P.is_combinatorially_isomorphic(polytopes.cuboctahedron()) # optional - sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(polytopes.cuboctahedron()) # needs sage.geometry.polyhedron True The SEP of a graph with edges on `n` vertices has dimension `n` @@ -24620,26 +24639,26 @@ def symmetric_edge_polytope(self, backend=None): sage: n = randint(5, 12) sage: G = Graph() - sage: while not G.num_edges(): # optional - networkx + sage: while not G.num_edges(): # needs networkx ....: G = graphs.RandomGNP(n, 0.2) - sage: P = G.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P.ambient_dim() == n # optional - networkx sage.geometry.polyhedron + sage: P = G.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P.ambient_dim() == n # needs networkx sage.geometry.polyhedron True - sage: P.dim() == n - G.connected_components_number() # optional - networkx sage.geometry.polyhedron + sage: P.dim() == n - G.connected_components_number() # needs networkx sage.geometry.polyhedron True The SEP of a graph is isomorphic to the subdirect sum of its connected components SEP's:: sage: n = randint(3, 6) - sage: G1 = graphs.RandomGNP(n, 0.2) # optional - networkx + sage: G1 = graphs.RandomGNP(n, 0.2) # needs networkx sage: n = randint(3, 6) - sage: G2 = graphs.RandomGNP(n, 0.2) # optional - networkx - sage: G = G1.disjoint_union(G2) # optional - networkx - sage: P = G.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P1 = G1.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P2 = G2.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: P.is_combinatorially_isomorphic(P1.subdirect_sum(P2)) # optional - networkx sage.geometry.polyhedron + sage: G2 = graphs.RandomGNP(n, 0.2) # needs networkx + sage: G = G1.disjoint_union(G2) # needs networkx + sage: P = G.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P1 = G1.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P2 = G2.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: P.is_combinatorially_isomorphic(P1.subdirect_sum(P2)) # needs networkx sage.geometry.polyhedron True All trees on `n` vertices have isomorphic SEPs:: @@ -24647,9 +24666,9 @@ def symmetric_edge_polytope(self, backend=None): sage: n = randint(4, 10) sage: G1 = graphs.RandomTree(n) sage: G2 = graphs.RandomTree(n) - sage: P1 = G1.symmetric_edge_polytope() # optional - sage.geometry.polyhedron - sage: P2 = G2.symmetric_edge_polytope() # optional - sage.geometry.polyhedron - sage: P1.is_combinatorially_isomorphic(P2) # optional - sage.geometry.polyhedron + sage: P1 = G1.symmetric_edge_polytope() # needs sage.geometry.polyhedron + sage: P2 = G2.symmetric_edge_polytope() # needs sage.geometry.polyhedron + sage: P1.is_combinatorially_isomorphic(P2) # needs sage.geometry.polyhedron True However, there are still many different SEPs:: @@ -24657,7 +24676,7 @@ def symmetric_edge_polytope(self, backend=None): sage: len(list(graphs(5))) 34 sage: polys = [] - sage: for G in graphs(5): # optional - sage.geometry.polyhedron + sage: for G in graphs(5): # needs sage.geometry.polyhedron ....: P = G.symmetric_edge_polytope() ....: for P1 in polys: ....: if P.is_combinatorially_isomorphic(P1): @@ -24676,24 +24695,24 @@ def symmetric_edge_polytope(self, backend=None): sage: G2.add_edges([[0, 7], [7, 3]]) sage: G1.is_isomorphic(G2) False - sage: P1 = G1.symmetric_edge_polytope() # optional - sage.geometry.polyhedron - sage: P2 = G2.symmetric_edge_polytope() # optional - sage.geometry.polyhedron - sage: P1.is_combinatorially_isomorphic(P2) # optional - sage.geometry.polyhedron + sage: P1 = G1.symmetric_edge_polytope() # needs sage.geometry.polyhedron + sage: P2 = G2.symmetric_edge_polytope() # needs sage.geometry.polyhedron + sage: P1.is_combinatorially_isomorphic(P2) # needs sage.geometry.polyhedron True Apparently, glueing two graphs together on a vertex gives isomorphic SEPs:: sage: n = randint(3, 7) - sage: g1 = graphs.RandomGNP(n, 0.2) # optional - networkx - sage: g2 = graphs.RandomGNP(n, 0.2) # optional - networkx - sage: G = g1.disjoint_union(g2) # optional - networkx - sage: H = copy(G) # optional - networkx - sage: G.merge_vertices(((0, randrange(n)), (1, randrange(n)))) # optional - networkx - sage: H.merge_vertices(((0, randrange(n)), (1, randrange(n)))) # optional - networkx - sage: PG = G.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: PH = H.symmetric_edge_polytope() # optional - networkx sage.geometry.polyhedron - sage: PG.is_combinatorially_isomorphic(PH) # optional - networkx sage.geometry.polyhedron + sage: g1 = graphs.RandomGNP(n, 0.2) # needs networkx + sage: g2 = graphs.RandomGNP(n, 0.2) # needs networkx + sage: G = g1.disjoint_union(g2) # needs networkx + sage: H = copy(G) # needs networkx + sage: G.merge_vertices(((0, randrange(n)), (1, randrange(n)))) # needs networkx + sage: H.merge_vertices(((0, randrange(n)), (1, randrange(n)))) # needs networkx + sage: PG = G.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: PH = H.symmetric_edge_polytope() # needs networkx sage.geometry.polyhedron + sage: PG.is_combinatorially_isomorphic(PH) # needs networkx sage.geometry.polyhedron True TESTS: @@ -24701,7 +24720,7 @@ def symmetric_edge_polytope(self, backend=None): Obtain the SEP with unsortable vertices:: sage: G = Graph([[1, (1, 2)]]) - sage: G.symmetric_edge_polytope() # optional - sage.geometry.polyhedron + sage: G.symmetric_edge_polytope() # needs sage.geometry.polyhedron A 1-dimensional polyhedron in ZZ^2 defined as the convex hull of 2 vertices """ from itertools import chain @@ -24737,10 +24756,10 @@ def tachyon_vertex_plot(g, bgcolor=(1, 1, 1), sage: G = graphs.TetrahedralGraph() sage: from sage.graphs.generic_graph import tachyon_vertex_plot - sage: T,p = tachyon_vertex_plot(G, pos3d=G.layout(dim=3)) # optional - sage.plot - sage: type(T) # optional - sage.plot + sage: T,p = tachyon_vertex_plot(G, pos3d=G.layout(dim=3)) # needs sage.plot + sage: type(T) # needs sage.plot - sage: type(p) # optional - sage.plot + sage: type(p) # needs sage.plot <... 'dict'> """ assert pos3d is not None diff --git a/src/sage/graphs/generic_graph_pyx.pyx b/src/sage/graphs/generic_graph_pyx.pyx index 90ed8f64383..45867176382 100644 --- a/src/sage/graphs/generic_graph_pyx.pyx +++ b/src/sage/graphs/generic_graph_pyx.pyx @@ -35,6 +35,7 @@ from sage.libs.gmp.mpz cimport * from sage.misc.prandom import random from sage.graphs.base.static_sparse_graph cimport short_digraph from sage.graphs.base.static_sparse_graph cimport init_short_digraph +from sage.graphs.base.static_sparse_graph cimport init_reverse from sage.graphs.base.static_sparse_graph cimport free_short_digraph from sage.graphs.base.static_sparse_graph cimport out_degree, has_edge @@ -663,15 +664,15 @@ cdef class SubgraphSearch: sage: SubgraphSearch(Graph(5), Graph(1)) # optional - sage.modules Traceback (most recent call last): ... - ValueError: Searched graph should have at least 2 vertices. + ValueError: searched graph should have at least 2 vertices sage: SubgraphSearch(Graph(5), Graph(2)) # optional - sage.modules """ if H.order() <= 1: - raise ValueError("Searched graph should have at least 2 vertices.") + raise ValueError("searched graph should have at least 2 vertices") - if sum([G.is_directed(), H.is_directed()]) == 1: - raise ValueError("One graph cannot be directed while the other is not.") + if G.is_directed() != H.is_directed(): + raise ValueError("one graph cannot be directed while the other is not") G._scream_if_not_simple(allow_loops=True) H._scream_if_not_simple(allow_loops=True) @@ -724,6 +725,18 @@ cdef class SubgraphSearch: sage: S = SubgraphSearch(g, h) # optional - sage.modules sage: S.cardinality() # optional - sage.modules 6 + + Check that the method is working even when vertices or edges are of + incomparable types (see :trac:`35904`):: + + sage: from sage.graphs.generic_graph_pyx import SubgraphSearch + sage: G = Graph() + sage: G.add_cycle(['A', 1, 2, 3, ('a', 1)]) + sage: H = Graph() + sage: H.add_path("xyz") + sage: S = SubgraphSearch(G, H) # optional - sage.modules + sage: S.cardinality() # optional - sage.modules + 10 """ if self.nh > self.ng: return 0 @@ -812,7 +825,8 @@ cdef class SubgraphSearch: self.nh = H.order() # Storing the list of vertices - self.g_vertices = G.vertices(sort=True) + self.g_vertices = list(G) + cdef list h_vertices = list(H) # Are the graphs directed (in __init__(), we check # whether both are of the same type) @@ -846,15 +860,22 @@ cdef class SubgraphSearch: self.h = DenseGraph(self.nh) # copying the adjacency relations in both G and H - for i, row in enumerate(G.adjacency_matrix()): - for j, k in enumerate(row): - if k: - self.g.add_arc(i, j) - - for i, row in enumerate(H.adjacency_matrix()): - for j, k in enumerate(row): - if k: - self.h.add_arc(i, j) + cdef dict vertex_to_int = {v: i for i, v in enumerate(self.g_vertices)} + cdef bint undirected = not G.is_directed() + for u, v in G.edge_iterator(labels=False): + i = vertex_to_int[u] + j = vertex_to_int[v] + self.g.add_arc(i, j) + if undirected: + self.g.add_arc(j, i) + + vertex_to_int = {v: i for i, v in enumerate(h_vertices)} + for u, v in H.edge_iterator(labels=False): + i = vertex_to_int[u] + j = vertex_to_int[v] + self.h.add_arc(i, j) + if undirected: + self.h.add_arc(j, i) # vertices is equal to range(nh), as an int *variable for i in range(self.nh): @@ -1237,11 +1258,29 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, Finally, an example on a graph which does not have a Hamiltonian path:: - sage: G=graphs.HyperStarGraph(5,2) - sage: fh(G,find_path=False) - (False, ['00110', '10100', '01100', '11000', '01010', '10010', '00011', '10001', '00101']) - sage: fh(G,find_path=True) - (False, ['01001', '10001', '00101', '10100', '00110', '10010', '01010', '11000', '01100']) + sage: G = graphs.HyperStarGraph(5, 2) + sage: G.order() + 10 + sage: b, P = fh(G,find_path=False) + sage: b, len(P) + (False, 9) + sage: b, P = fh(G,find_path=True) + sage: b, len(P) + (False, 9) + + The method can also be used for directed graphs:: + + sage: G = DiGraph([(0, 1), (1, 2), (2, 3)]) + sage: fh(G) + (False, [0, 1, 2, 3]) + sage: G = G.reverse() + sage: fh(G) + (False, [3, 2, 1, 0]) + sage: G = DiGraph() + sage: G.add_cycle([0, 1, 2, 3, 4, 5]) + sage: b, P = fh(G) + sage: b, len(P) + (True, 6) TESTS: @@ -1289,15 +1328,21 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, sage: fh(G, find_path=True) (False, [0, 1, 2, 3]) + Check that the method is robust to incomparable vertices:: + + sage: G = Graph([(1, 'a'), ('a', 2), (2, 3), (3, 1)]) + sage: b, C = fh(G, find_path=False) + sage: b, len(C) + (True, 4) """ + G._scream_if_not_simple() + from sage.misc.prandom import randint cdef int n = G.order() # Easy cases - if not n: - return False, [] - if n == 1: - return False, G.vertices(sort=False) + if n < 2: + return False, list(G) # To clean the output when find_path is None or a number find_path = (find_path > 0) @@ -1305,7 +1350,7 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, if G.is_clique(induced=False): # We have an hamiltonian path since n >= 2, but we have an hamiltonian # cycle only if n >= 3 - return find_path or n >= 3, G.vertices(sort=True) + return find_path or n >= 3, list(G) cdef list best_path, p if not G.is_connected(): @@ -1313,6 +1358,8 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, # longest path in its connected components. best_path = [] for H in G.connected_components_subgraphs(): + if H.order() <= len(best_path): + continue _, p = find_hamiltonian(H, max_iter=max_iter, reset_bound=reset_bound, backtrack_bound=backtrack_bound, find_path=True) if len(p) > len(best_path): @@ -1321,20 +1368,24 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, # Misc variables used below cdef int i, j - cdef int n_available + cdef bint directed = G.is_directed() # Initialize the path. cdef MemoryAllocator mem = MemoryAllocator() cdef int *path = mem.allocarray(n, sizeof(int)) - memset(path, -1, n * sizeof(int)) # Initialize the membership array cdef bint *member = mem.allocarray(n, sizeof(int)) memset(member, 0, n * sizeof(int)) # static copy of the graph for more efficient operations + cdef list int_to_vertex = list(G) cdef short_digraph sd - init_short_digraph(sd, G) + init_short_digraph(sd, G, edge_labelled=False, vertex_list=int_to_vertex) + cdef short_digraph rev_sd + cdef bint reverse = False + if directed: + init_reverse(rev_sd, sd) # A list to store the available vertices at each step cdef list available_vertices = [] @@ -1344,7 +1395,7 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, cdef int u = randint(0, n - 1) while not out_degree(sd, u): u = randint(0, n - 1) - # Then we pick at random a neighbor of u + # Then we pick at random a neighbor of u cdef int x = randint(0, out_degree(sd, u) - 1) cdef int v = sd.neighbors[u][x] # This will be the first edge in the path @@ -1362,34 +1413,35 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, # Initialize a path to contain the longest path cdef int *longest_path = mem.allocarray(n, sizeof(int)) - memset(longest_path, -1, n * sizeof(int)) for i in range(length): longest_path[i] = path[i] - # Initialize a temporary path for flipping - cdef int *temp_path = mem.allocarray(n, sizeof(int)) - memset(temp_path, -1, n * sizeof(int)) - cdef bint longer = False - cdef bint good = True + cdef bint longest_reversed = False cdef bint flag while not done: counter = counter + 1 if counter % 10 == 0: # Reverse the path - for i in range(length//2): t = path[i] path[i] = path[length - i - 1] path[length - i - 1] = t + if directed: + # We now work on the reverse graph + reverse = not reverse + if counter > reset_bound: bigcount = bigcount + 1 counter = 1 # Time to reset the procedure memset(member, 0, n * sizeof(int)) + if directed and reverse: + # We restore the original orientation + reverse = False # First we pick a random vertex u of (out-)degree at least one u = randint(0, n - 1) @@ -1405,37 +1457,44 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, member[u] = True member[v] = True - if counter % backtrack_bound == 0: + if length > 5 and counter % backtrack_bound == 0: for i in range(5): member[path[length - i - 1]] = False length = length - 5 longer = False + # We search for a possible extension of the path available_vertices = [] u = path[length - 1] - for i in range(out_degree(sd, u)): - v = sd.neighbors[u][i] - if not member[v]: - available_vertices.append(v) + if directed and reverse: + for i in range(out_degree(rev_sd, u)): + v = rev_sd.neighbors[u][i] + if not member[v]: + available_vertices.append(v) + else: + for i in range(out_degree(sd, u)): + v = sd.neighbors[u][i] + if not member[v]: + available_vertices.append(v) - n_available = len(available_vertices) - if n_available > 0: + if available_vertices: longer = True - x = randint(0, n_available - 1) - path[length] = available_vertices[x] + x = randint(0, len(available_vertices) - 1) + v = available_vertices[x] + path[length] = v length = length + 1 - member[available_vertices[x]] = True + member[v] = True if not longer and length > longest: - + # Store the current best solution for i in range(length): longest_path[i] = path[i] longest = length + longest_reversed = reverse - if not longer: - - memset(temp_path, -1, n * sizeof(int)) + if not directed and not longer and out_degree(sd, path[length - 1]) > 1: + # We revert a cycle to change the extremity of the path degree = out_degree(sd, path[length - 1]) while True: x = randint(0, degree - 1) @@ -1455,37 +1514,53 @@ cpdef tuple find_hamiltonian(G, long max_iter=100000, long reset_bound=30000, j += 1 if path[i] == u: flag = True + if length == n: if find_path: done = True + elif directed and reverse: + done = has_edge(rev_sd, path[0], path[n - 1]) != NULL else: done = has_edge(sd, path[n - 1], path[0]) != NULL if bigcount * reset_bound > max_iter: - verts = G.vertices(sort=True) - output = [verts[longest_path[i]] for i in range(longest)] + output = [int_to_vertex[longest_path[i]] for i in range(longest)] free_short_digraph(sd) + if directed: + free_short_digraph(rev_sd) + if longest_reversed: + return (False, output[::-1]) return (False, output) # # # # Output test # # + if directed and reverse: + # We revert the path to work on sd + for i in range(length//2): + t = path[i] + path[i] = path[length - i - 1] + path[length - i - 1] = t + # Test adjacencies + cdef bint good = True for i in range(n - 1): u = path[i] v = path[i + 1] - # Graph is simple, so both arcs are present if has_edge(sd, u, v) == NULL: good = False break if good is False: - raise RuntimeError('vertices %d and %d are consecutive in the cycle but are not adjacent' % (u, v)) - if not find_path and has_edge(sd, path[0], path[n - 1]) == NULL: - raise RuntimeError('vertices %d and %d are not adjacent' % (path[0], path[n - 1])) + raise RuntimeError(f"vertices {int_to_vertex[u]} and {int_to_vertex[v]}" + " are consecutive in the cycle but are not adjacent") + if not find_path and has_edge(sd, path[n - 1], path[0]) == NULL: + raise RuntimeError(f"vertices {int_to_vertex[path[n - 1]]} and " + f"{int_to_vertex[path[0]]} are not adjacent") - verts = G.vertices(sort=True) - output = [verts[path[i]] for i in range(length)] + output = [int_to_vertex[path[i]] for i in range(length)] free_short_digraph(sd) + if directed: + free_short_digraph(rev_sd) return (True, output) diff --git a/src/sage/graphs/graph.py b/src/sage/graphs/graph.py index 3113fe0af38..e2c86f69d40 100644 --- a/src/sage/graphs/graph.py +++ b/src/sage/graphs/graph.py @@ -1634,9 +1634,9 @@ def is_forest(self, certificate=False, output='vertex'): sage: g.is_forest(certificate=True) (True, None) sage: (2*g + graphs.PetersenGraph() + g).is_forest(certificate=True) - (False, [68, 66, 69, 67, 65]) + (False, [64, 69, 67, 65, 60]) """ - connected_components = self.connected_components() + connected_components = self.connected_components(sort=False) number_of_connected_components = len(connected_components) isit = (self.order() == self.size() + number_of_connected_components) @@ -8910,7 +8910,7 @@ def perfect_matchings(self, labels=False): if not self: yield [] return - if self.order() % 2 or any(len(cc) % 2 for cc in self.connected_components()): + if self.order() % 2 or any(len(cc) % 2 for cc in self.connected_components(sort=False)): return def rec(G): @@ -9164,7 +9164,7 @@ def effective_resistance(self, i, j, *, base_ring=None): self._scream_if_not_simple() if not self.is_connected(): - connected_i = self.connected_component_containing_vertex(i) + connected_i = self.connected_component_containing_vertex(i, sort=False) if j in connected_i: component = self.subgraph(connected_i) return component.effective_resistance(i, j) diff --git a/src/sage/graphs/graph_coloring.pyx b/src/sage/graphs/graph_coloring.pyx index 0b34088ef9c..60abd45d4d1 100644 --- a/src/sage/graphs/graph_coloring.pyx +++ b/src/sage/graphs/graph_coloring.pyx @@ -602,7 +602,7 @@ def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver=None, # by the test of degeneracy (as previously). if not g.is_connected(): if value_only: - for component in g.connected_components(): + for component in g.connected_components(sort=False): tmp = vertex_coloring(g.subgraph(component), k=k, value_only=value_only, hex_colors=hex_colors, @@ -612,7 +612,7 @@ def vertex_coloring(g, k=None, value_only=False, hex_colors=False, solver=None, return False return True colorings = [] - for component in g.connected_components(): + for component in g.connected_components(sort=False): tmp = vertex_coloring(g.subgraph(component), k=k, value_only=value_only, hex_colors=False, diff --git a/src/sage/graphs/graph_decompositions/all__sagemath_tdlib.py b/src/sage/graphs/graph_decompositions/all__sagemath_tdlib.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/graphs/graph_decompositions/clique_separators.pyx b/src/sage/graphs/graph_decompositions/clique_separators.pyx index 73a26a6615c..b4f92db5172 100644 --- a/src/sage/graphs/graph_decompositions/clique_separators.pyx +++ b/src/sage/graphs/graph_decompositions/clique_separators.pyx @@ -423,7 +423,7 @@ def atoms_and_clique_separators(G, tree=False, rooted_tree=False, separators=Fal if not G.is_connected(): from sage.graphs.graph import Graph - for cc in G.connected_components(): + for cc in G.connected_components(sort=False): g = Graph([cc, G.edge_boundary(cc, cc, False, False)], format='vertices_and_edges', loops=True, multiedges=True) diff --git a/src/sage/graphs/graph_decompositions/graph_products.pyx b/src/sage/graphs/graph_decompositions/graph_products.pyx index fa7fdb2e0c8..7042eabbfaf 100644 --- a/src/sage/graphs/graph_decompositions/graph_products.pyx +++ b/src/sage/graphs/graph_decompositions/graph_products.pyx @@ -309,7 +309,7 @@ def is_cartesian_product(g, certificate=False, relabeling=False): # Gathering the connected components, relabeling the vertices on-the-fly edges = [[(int_to_vertex[u], int_to_vertex[v]) for u, v in cc] - for cc in h.connected_components()] + for cc in h.connected_components(sort=False)] # Only one connected component ? if len(edges) == 1: @@ -320,7 +320,7 @@ def is_cartesian_product(g, certificate=False, relabeling=False): for cc in edges: tmp = Graph() tmp.add_edges(cc) - factors.append(tmp.subgraph(vertices=tmp.connected_components()[0])) + factors.append(tmp.subgraph(vertices=tmp.connected_components(sort=False)[0])) # Computing the product of these graphs answer = factors[0] diff --git a/src/sage/graphs/graph_decompositions/modular_decomposition.py b/src/sage/graphs/graph_decompositions/modular_decomposition.py index 794c1d95d7b..230525e00e1 100644 --- a/src/sage/graphs/graph_decompositions/modular_decomposition.py +++ b/src/sage/graphs/graph_decompositions/modular_decomposition.py @@ -451,7 +451,7 @@ def gamma_classes(graph): pieces = DisjointSet(frozenset(e) for e in graph.edge_iterator(labels=False)) for v in graph: neighborhood = graph.subgraph(vertices=graph.neighbors(v)) - for component in neighborhood.complement().connected_components(): + for component in neighborhood.complement().connected_components(sort=False): v1 = component[0] e = frozenset([v1, v]) for vi in component[1:]: @@ -625,7 +625,7 @@ def habib_maurer_algorithm(graph, g_classes=None): elif not graph.is_connected(): root = create_parallel_node() root.children = [habib_maurer_algorithm(graph.subgraph(vertices=sg), g_classes) - for sg in graph.connected_components()] + for sg in graph.connected_components(sort=False)] return root g_comp = graph.complement() @@ -650,7 +650,7 @@ def habib_maurer_algorithm(graph, g_classes=None): root = create_series_node() root.children = [habib_maurer_algorithm(graph.subgraph(vertices=sg), g_classes) - for sg in g_comp.connected_components()] + for sg in g_comp.connected_components(sort=False)] return root diff --git a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx index fb9b217a49d..1e364277c8c 100644 --- a/src/sage/graphs/graph_decompositions/tree_decomposition.pyx +++ b/src/sage/graphs/graph_decompositions/tree_decomposition.pyx @@ -1117,7 +1117,7 @@ cdef class TreelengthConnected: # Removing v may have disconnected cc. We iterate on its # connected components - for _cci in g.subgraph(ccv).connected_components(): + for _cci in g.subgraph(ccv).connected_components(sort=False): cci = frozenset(_cci) # The recursive subcalls. We remove on-the-fly the vertices diff --git a/src/sage/graphs/mcqd.pxd b/src/sage/graphs/mcqd.pxd index d8d8b01bc72..d7a2faa6d58 100644 --- a/src/sage/graphs/mcqd.pxd +++ b/src/sage/graphs/mcqd.pxd @@ -1,3 +1,5 @@ +# sage_setup: distribution = sagemath-mcqd + from libcpp cimport bool cdef extern from "mcqd.h": @@ -5,4 +7,3 @@ cdef extern from "mcqd.h": Maxclique() Maxclique(bool **, int n) void mcqdyn(int * maxclique, int& size) - diff --git a/src/sage/graphs/partial_cube.py b/src/sage/graphs/partial_cube.py index f7bb3e25173..0ec81f6b200 100644 --- a/src/sage/graphs/partial_cube.py +++ b/src/sage/graphs/partial_cube.py @@ -342,7 +342,7 @@ def is_partial_cube(G, certificate=False): # Map vertices to components of labeled-edge graph component = {} - for i, SCC in enumerate(labeled.connected_components()): + for i, SCC in enumerate(labeled.connected_components(sort=False)): for v in SCC: component[v] = i diff --git a/src/sage/graphs/spanning_tree.pyx b/src/sage/graphs/spanning_tree.pyx index 179625ab743..08f441d90a8 100644 --- a/src/sage/graphs/spanning_tree.pyx +++ b/src/sage/graphs/spanning_tree.pyx @@ -1178,8 +1178,8 @@ def spanning_trees(g, labels=False): # e=xy links the CC (connected component) of forest containing x # with the CC containing y. Any other edge which does that cannot be # added to forest anymore, and B is the list of them - c1 = forest.connected_component_containing_vertex(e[0]) - c2 = forest.connected_component_containing_vertex(e[1]) + c1 = forest.connected_component_containing_vertex(e[0], sort=False) + c2 = forest.connected_component_containing_vertex(e[1], sort=False) G.delete_edge(e) B = G.edge_boundary(c1, c2, sort=False) G.add_edge(e) diff --git a/src/sage/graphs/traversals.pyx b/src/sage/graphs/traversals.pyx index 9ebc253b6f9..a565d46fdcb 100644 --- a/src/sage/graphs/traversals.pyx +++ b/src/sage/graphs/traversals.pyx @@ -2009,7 +2009,7 @@ def maximum_cardinality_search_M(G, initial_vertex=None): ....: if len(X) < k - 1: ....: raise ValueError("something goes wrong") sage: G = graphs.RandomGNP(10, .2) - sage: cc = G.connected_components() + sage: cc = G.connected_components(sort=False) sage: _, _, X = G.maximum_cardinality_search_M() sage: len(X) >= len(cc) - 1 True diff --git a/src/sage/graphs/tutte_polynomial.py b/src/sage/graphs/tutte_polynomial.py index 8f003084619..6794c960805 100644 --- a/src/sage/graphs/tutte_polynomial.py +++ b/src/sage/graphs/tutte_polynomial.py @@ -317,7 +317,7 @@ def find_ear(g): in g.degree_iterator(labels=True) if degree == 2] subgraph = g.subgraph(degree_two_vertices) - for component in subgraph.connected_components(): + for component in subgraph.connected_components(sort=False): edges = g.edges_incident(vertices=component, labels=True) all_vertices = sorted(set(sum([e[:2] for e in edges], ()))) if len(all_vertices) < 3: diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd index b469d09c2b5..8c5ae14b5bb 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pxd +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pxd @@ -13,7 +13,6 @@ from sage.data_structures.bitset_base cimport * from libc.string cimport memcpy from libc.stdlib cimport rand from sage.libs.gmp.mpz cimport * -from sage.groups.perm_gps.partn_ref2.refinement_generic cimport PartitionRefinement_generic cdef enum: @@ -260,8 +259,7 @@ cdef PS_print(PartitionStack *PS) cdef void PS_unit_partition(PartitionStack *PS) -cdef int PS_first_smallest(PartitionStack *PS, bitset_t b, int *second_pos=?, - PartitionRefinement_generic partn_ref_alg=?) +cdef int PS_first_smallest(PartitionStack *PS, bitset_t b, int *second_pos=?) cdef PartitionStack *PS_from_list(list L) diff --git a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx index 065c5f3365a..b6567fb3dc9 100644 --- a/src/sage/groups/perm_gps/partn_ref/data_structures.pyx +++ b/src/sage/groups/perm_gps/partn_ref/data_structures.pyx @@ -33,7 +33,10 @@ from cysignals.memory cimport sig_malloc, sig_calloc, sig_realloc, sig_free from sage.data_structures.bitset_base cimport * from sage.rings.integer cimport Integer -from sage.libs.flint.ulong_extras cimport n_is_prime +# from sage.libs.flint.ulong_extras cimport n_is_prime +# -- avoid modularization obstruction -- function is only used for a doctest helper +from sage.arith.misc import is_prime as n_is_prime + # OrbitPartition (OP) @@ -281,8 +284,7 @@ cdef PS_print_partition(PartitionStack *PS, int k): s = s[:-1] + ')' print(s) -cdef int PS_first_smallest(PartitionStack *PS, bitset_t b, int *second_pos=NULL, - PartitionRefinement_generic partn_ref_alg=None): +cdef int PS_first_smallest(PartitionStack *PS, bitset_t b, int *second_pos=NULL): """ Find the first occurrence of the smallest cell of size greater than one, which is admissible (checked by the function ``test_allowance``). @@ -290,10 +292,9 @@ cdef int PS_first_smallest(PartitionStack *PS, bitset_t b, int *second_pos=NULL, """ cdef int i = 0, j = 0, location = 0, n = PS.degree bitset_zero(b) - while 1: + while True: if PS.levels[i] <= PS.depth: - if i != j and n > i - j + 1 and (partn_ref_alg is None or - partn_ref_alg._minimization_allowed_on_col(PS.entries[j])): + if i != j and n > i - j + 1: n = i - j + 1 location = j j = i + 1 @@ -303,19 +304,18 @@ cdef int PS_first_smallest(PartitionStack *PS, bitset_t b, int *second_pos=NULL, # location now points to the beginning of the first, smallest, # nontrivial cell i = location - while 1: + while True: bitset_flip(b, PS.entries[i]) if PS.levels[i] <= PS.depth: break i += 1 if second_pos != NULL: - if n==2: - second_pos[0] = PS.entries[location+1] + if n == 2: + second_pos[0] = PS.entries[location + 1] else: second_pos[0] = -1 - return PS.entries[location] diff --git a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pxd b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pxd index e45afd3e578..df300e4e0ce 100644 --- a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pxd +++ b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pxd @@ -7,6 +7,7 @@ # http://www.gnu.org/licenses/ #******************************************************************************* +from sage.data_structures.bitset_base cimport * from sage.groups.perm_gps.partn_ref.data_structures cimport OrbitPartition, PartitionStack from sage.libs.gap.element cimport GapElement, GapElement_Permutation from sage.structure.parent cimport Parent @@ -82,3 +83,7 @@ cdef class PartitionRefinement_generic: bint* inner_group_changed, bint* changed_partition, str refine_name) cdef int len(self) + + +cdef int PS_first_smallest_PR(PartitionStack *PS, bitset_t b, int *second_pos=?, + PartitionRefinement_generic partn_ref_alg=?) diff --git a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx index 47d6862333c..6e4e2b27eeb 100644 --- a/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx +++ b/src/sage/groups/perm_gps/partn_ref2/refinement_generic.pyx @@ -684,8 +684,7 @@ cdef class PartitionRefinement_generic: bitset_init(b, self._n) PS_move_all_mins_to_front(self._part) cdef int second_pos - cdef int smallest = PS_first_smallest(self._part, b, &second_pos, - self) + cdef int smallest = PS_first_smallest_PR(self._part, b, &second_pos, self) if second_pos != -1: self._fixed_not_minimized.append(second_pos) cdef int pos = smallest @@ -941,3 +940,42 @@ cdef class PartitionRefinement_generic: """ if BACKTRACK_WITHLATEX_DEBUG: self._latex_debug_string += "]\n" + + +cdef int PS_first_smallest_PR(PartitionStack *PS, bitset_t b, int *second_pos=NULL, + PartitionRefinement_generic partn_ref_alg=None): + """ + Find the first occurrence of the smallest cell of size greater than one, + which is admissible (checked by the function ``test_allowance``). + Its entries are stored to b and its minimum element is returned. + + This generalizes :func:`sage.groups.perm_gps.partn_ref.data_structures.PS_first_smallest`. + """ + cdef int i = 0, j = 0, location = 0, n = PS.degree + bitset_zero(b) + while True: + if PS.levels[i] <= PS.depth: + if i != j and n > i - j + 1 and (partn_ref_alg is None or + partn_ref_alg._minimization_allowed_on_col(PS.entries[j])): + n = i - j + 1 + location = j + j = i + 1 + if PS.levels[i] == -1: + break + i += 1 + # location now points to the beginning of the first, smallest, + # nontrivial cell + i = location + while True: + bitset_flip(b, PS.entries[i]) + if PS.levels[i] <= PS.depth: + break + i += 1 + + if second_pos != NULL: + if n == 2: + second_pos[0] = PS.entries[location + 1] + else: + second_pos[0] = -1 + + return PS.entries[location] diff --git a/src/sage/interfaces/gnuplot.py b/src/sage/interfaces/gnuplot.py index 8ab14c75e1c..b423866fd15 100644 --- a/src/sage/interfaces/gnuplot.py +++ b/src/sage/interfaces/gnuplot.py @@ -159,7 +159,7 @@ def plot3d_parametric(self, f='cos(u)*(3 + v*cos(u/2)), sin(u)*(3 + v*cos(u/2)), EXAMPLES:: - sage: gnuplot.plot3d_parametric('v^2*sin(u), v*cos(u), v*(1-v)') # optional - gnuplot (not tested, since something pops up). + sage: gnuplot.plot3d_parametric('v^2*sin(u), v*cos(u), v*(1-v)') # optional - gnuplot, not tested (since something pops up) """ if title is None: title = str(f) diff --git a/src/sage/interfaces/r.py b/src/sage/interfaces/r.py index 144d581b18e..7d18f1e467e 100644 --- a/src/sage/interfaces/r.py +++ b/src/sage/interfaces/r.py @@ -66,13 +66,14 @@ sage: x.var() # optional - rpy2 [1] 53.853 - sage: x.sort() # optional - rpy2 + sage: # optional - rpy2 + sage: x.sort() [1] 3.1 5.6 6.4 10.4 21.7 - sage: x.min() # optional - rpy2 + sage: x.min() [1] 3.1 - sage: x.max() # optional - rpy2 + sage: x.max() [1] 21.7 - sage: x # optional - rpy2 + sage: x [1] 10.4 5.6 3.1 6.4 21.7 sage: r(-17).sqrt() # optional - rpy2 @@ -92,42 +93,44 @@ sage: r.seq(length=10, from_=-1, by=.2) # optional - rpy2 [1] -1.0 -0.8 -0.6 -0.4 -0.2 0.0 0.2 0.4 0.6 0.8 - sage: x = r([10.4,5.6,3.1,6.4,21.7]) # optional - rpy2 - sage: x.rep(2) # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([10.4,5.6,3.1,6.4,21.7]) + sage: x.rep(2) [1] 10.4 5.6 3.1 6.4 21.7 10.4 5.6 3.1 6.4 21.7 - sage: x.rep(times=2) # optional - rpy2 + sage: x.rep(times=2) [1] 10.4 5.6 3.1 6.4 21.7 10.4 5.6 3.1 6.4 21.7 - sage: x.rep(each=2) # optional - rpy2 + sage: x.rep(each=2) [1] 10.4 10.4 5.6 5.6 3.1 3.1 6.4 6.4 21.7 21.7 Missing Values:: - sage: na = r('NA') # optional - rpy2 - sage: z = r([1,2,3,na]) # optional - rpy2 - sage: z # optional - rpy2 + sage: # optional - rpy2 + sage: na = r('NA') + sage: z = r([1,2,3,na]) + sage: z [1] 1 2 3 NA - sage: ind = r.is_na(z) # optional - rpy2 - sage: ind # optional - rpy2 + sage: ind = r.is_na(z) + sage: ind [1] FALSE FALSE FALSE TRUE - sage: zero = r(0) # optional - rpy2 - sage: zero / zero # optional - rpy2 + sage: zero = r(0) + sage: zero / zero [1] NaN - sage: inf = r('Inf') # optional - rpy2 - sage: inf-inf # optional - rpy2 + sage: inf = r('Inf') + sage: inf-inf [1] NaN - sage: r.is_na(inf) # optional - rpy2 + sage: r.is_na(inf) [1] FALSE - sage: r.is_na(inf-inf) # optional - rpy2 + sage: r.is_na(inf-inf) [1] TRUE - sage: r.is_na(zero/zero) # optional - rpy2 + sage: r.is_na(zero/zero) [1] TRUE - sage: r.is_na(na) # optional - rpy2 + sage: r.is_na(na) [1] TRUE - sage: r.is_nan(inf-inf) # optional - rpy2 + sage: r.is_nan(inf-inf) [1] TRUE - sage: r.is_nan(zero/zero) # optional - rpy2 + sage: r.is_nan(zero/zero) [1] TRUE - sage: r.is_nan(na) # optional - rpy2 + sage: r.is_nan(na) [1] FALSE @@ -145,13 +148,14 @@ sage: x['!is.na(self)'] # optional - rpy2 [1] 10.4 5.6 3.1 6.4 21.7 - sage: x = r([10.4,5.6,3.1,6.4,21.7,na]); x # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([10.4,5.6,3.1,6.4,21.7,na]); x [1] 10.4 5.6 3.1 6.4 21.7 NA - sage: (x+1)['(!is.na(self)) & self>0'] # optional - rpy2 + sage: (x+1)['(!is.na(self)) & self>0'] [1] 11.4 6.6 4.1 7.4 22.7 - sage: x = r([10.4,-2,3.1,-0.5,21.7,na]); x # optional - rpy2 + sage: x = r([10.4,-2,3.1,-0.5,21.7,na]); x [1] 10.4 -2.0 3.1 -0.5 21.7 NA - sage: (x+1)['(!is.na(self)) & self>0'] # optional - rpy2 + sage: (x+1)['(!is.na(self)) & self>0'] [1] 11.4 4.1 0.5 22.7 Distributions:: @@ -187,16 +191,17 @@ Or you get a dictionary to be able to access all the information:: - sage: rs = r.summary(r.c(1,4,3,4,3,2,5,1)) # optional - rpy2 - sage: rs # optional - rpy2 + sage: # optional - rpy2 + sage: rs = r.summary(r.c(1,4,3,4,3,2,5,1)) + sage: rs Min. 1st Qu. Median Mean 3rd Qu. Max. 1.000 1.750 3.000 2.875 4.000 5.000 - sage: d = rs._sage_() # optional - rpy2 - sage: d['DATA'] # optional - rpy2 + sage: d = rs._sage_() + sage: d['DATA'] [1, 1.75, 3, 2.875, 4, 5] - sage: d['_Names'] # optional - rpy2 + sage: d['_Names'] ['Min.', '1st Qu.', 'Median', 'Mean', '3rd Qu.', 'Max.'] - sage: d['_r_class'] # optional - rpy2 + sage: d['_r_class'] ['summaryDefault', 'table'] It is also possible to access the plotting capabilities of R @@ -346,13 +351,14 @@ def _setup_r_to_sage_converter(): The conversion can handle "not a number", infinity, imaginary values and missing values:: - sage: r(-17).sqrt().sage() # optional - rpy2 + sage: # optional - rpy2 + sage: r(-17).sqrt().sage() nan - sage: r('-17+0i').sqrt().sage() # optional - rpy2 + sage: r('-17+0i').sqrt().sage() 4.123105625617661j - sage: r('NA').sage() # optional - rpy2 + sage: r('NA').sage() NA - sage: inf = r('Inf'); inf.sage() # optional - rpy2 + sage: inf = r('Inf'); inf.sage() inf Character Vectors are represented by regular python arrays:: @@ -504,30 +510,33 @@ def _lazy_init(self): Initialization happens on eval:: - sage: my_r = R() # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: # optional - rpy2 + sage: my_r = R() + sage: my_r._initialized False - sage: my_r(42) # indirect doctest # optional - rpy2 + sage: my_r(42) # indirect doctest [1] 42 - sage: my_r._initialized # optional - rpy2 + sage: my_r._initialized True And on package import:: - sage: my_r = R() # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: # optional - rpy2 + sage: my_r = R() + sage: my_r._initialized False - sage: my_r.library('grid') # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: my_r.library('grid') + sage: my_r._initialized True And when fetching help pages:: - sage: my_r = R() # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: # optional - rpy2 + sage: my_r = R() + sage: my_r._initialized False - sage: _ = my_r.help('c') # optional - rpy2 - sage: my_r._initialized # optional - rpy2 + sage: _ = my_r.help('c') + sage: my_r._initialized True """ if not self._initialized: @@ -611,15 +620,16 @@ def png(self, *args, **kwds): EXAMPLES:: - sage: filename = tmp_filename() + '.png' # optional - rpy2 - sage: r.png(filename='"%s"'%filename) # optional -- rgraphics # optional - rpy2 + sage: # optional - rpy2 + sage: filename = tmp_filename() + '.png' + sage: r.png(filename='"%s"'%filename) # optional - rgraphics NULL - sage: x = r([1,2,3]) # optional - rpy2 - sage: y = r([4,5,6]) # optional - rpy2 - sage: r.plot(x,y) # This saves to filename, but is not viewable from command line; optional -- rgraphics # optional - rpy2 + sage: x = r([1,2,3]) + sage: y = r([4,5,6]) + sage: r.plot(x,y) # optional - rgraphics null device 1 - sage: import os; os.unlink(filename) # We remove the file for doctesting; optional -- rgraphics # optional - rpy2 + sage: import os; os.unlink(filename) # optional - rgraphics We want to make sure that we actually can view R graphics, which happens differently on different platforms:: @@ -743,12 +753,13 @@ def read(self, filename): EXAMPLES:: - sage: filename = tmp_filename() # optional - rpy2 - sage: f = open(filename, 'w') # optional - rpy2 - sage: _ = f.write('a <- 2+2\n') # optional - rpy2 - sage: f.close() # optional - rpy2 - sage: r.read(filename) # optional - rpy2 - sage: r.get('a') # optional - rpy2 + sage: # optional - rpy2 + sage: filename = tmp_filename() + sage: f = open(filename, 'w') + sage: _ = f.write('a <- 2+2\n') + sage: f.close() + sage: r.read(filename) + sage: r.get('a') '[1] 4' """ self.eval( self._read_in_file_command(filename) ) @@ -804,12 +815,13 @@ def version(self): EXAMPLES:: - sage: r.version() # not tested # optional - rpy2 + sage: # optional - rpy2 + sage: r.version() # not tested ((3, 0, 1), 'R version 3.0.1 (2013-05-16)') - sage: rint, rstr = r.version() # optional - rpy2 - sage: rint[0] >= 3 # optional - rpy2 + sage: rint, rstr = r.version() + sage: rint[0] >= 3 True - sage: rstr.startswith('R version') # optional - rpy2 + sage: rstr.startswith('R version') True """ major_re = re.compile(r'^major\s*(\d.*?)$', re.M) @@ -1271,15 +1283,16 @@ def plot(self, *args, **kwds): the output device to that file. If this is done in the notebook, it must be done in the same cell as the plot itself:: - sage: filename = tmp_filename() + '.png' # optional - rpy2 - sage: r.png(filename='"%s"'%filename) # Note the double quotes in single quotes!; optional -- rgraphics # optional - rpy2 + sage: # optional - rpy2 + sage: filename = tmp_filename() + '.png' + sage: r.png(filename='"%s"'%filename) # optional - rgraphics NULL - sage: x = r([1,2,3]) # optional - rpy2 - sage: y = r([4,5,6]) # optional - rpy2 - sage: r.plot(x,y) # optional -- rgraphics # optional - rpy2 + sage: x = r([1,2,3]) + sage: y = r([4,5,6]) + sage: r.plot(x,y) # optional - rgraphics null device 1 - sage: import os; os.unlink(filename) # For doctesting, we remove the file; optional -- rgraphics # optional - rpy2 + sage: import os; os.unlink(filename) # optional - rgraphics Please note that for more extensive use of R's plotting capabilities (such as the lattices package), it is advisable @@ -1287,23 +1300,25 @@ def plot(self, *args, **kwds): notebook. The following examples are not tested, because they differ depending on operating system:: - sage: r.X11() # not tested - opens interactive device on systems with X11 support # optional - rpy2 - sage: r.quartz() # not tested - opens interactive device on OSX # optional - rpy2 - sage: r.hist("rnorm(100)") # not tested - makes a plot # optional - rpy2 - sage: r.library("lattice") # not tested - loads R lattice plotting package # optional - rpy2 - sage: r.histogram(x = "~ wt | cyl", data="mtcars") # not tested - makes a lattice plot # optional - rpy2 - sage: r.dev_off() # not tested, turns off the interactive viewer # optional - rpy2 + sage: # not tested, optional - rpy2 + sage: r.X11() + sage: r.quartz() + sage: r.hist("rnorm(100)") + sage: r.library("lattice") + sage: r.histogram(x = "~ wt | cyl", data="mtcars") + sage: r.dev_off() In the notebook, one can use r.png() to open the device, but would need to use the following since R lattice graphics do not automatically print away from the command line:: - sage: filename = tmp_filename() + '.png' # Not needed in notebook, used for doctesting # optional - rpy2 - sage: r.png(filename='"%s"'%filename) # filename not needed in notebook, used for doctesting; optional -- rgraphics # optional - rpy2 + sage: # optional - rpy2 + sage: filename = tmp_filename() + '.png' # Not needed in notebook, used for doctesting + sage: r.png(filename='"%s"'%filename) # optional - rgraphics NULL - sage: r.library("lattice") # optional - rpy2 - sage: r("print(histogram(~wt | cyl, data=mtcars))") # plot should appear; optional -- rgraphics # optional - rpy2 - sage: import os; os.unlink(filename) # We remove the file for doctesting, not needed in notebook; optional -- rgraphics # optional - rpy2 + sage: r.library("lattice") + sage: r("print(histogram(~wt | cyl, data=mtcars))") # optional - rgraphics + sage: import os; os.unlink(filename) # optional - rgraphics """ # We have to define this to override the plot function defined in the # superclass. @@ -1337,14 +1352,15 @@ def _r_to_sage_name(self, s): EXAMPLES:: - sage: f = r._r_to_sage_name # optional - rpy2 - sage: f('t.test') # optional - rpy2 + sage: # optional - rpy2 + sage: f = r._r_to_sage_name + sage: f('t.test') 't_test' - sage: f('attr<-') # optional - rpy2 + sage: f('attr<-') 'attr__' - sage: f('parent.env<-') # optional - rpy2 + sage: f('parent.env<-') 'parent_env__' - sage: f('class') # optional - rpy2 + sage: f('class') 'class_' """ from keyword import iskeyword @@ -1360,16 +1376,17 @@ def _sage_to_r_name(self, s): EXAMPLES:: - sage: f = r._sage_to_r_name # optional - rpy2 - sage: f('t_test') # optional - rpy2 + sage: # optional - rpy2 + sage: f = r._sage_to_r_name + sage: f('t_test') 't.test' - sage: f('attr__') # optional - rpy2 + sage: f('attr__') 'attr<-' - sage: f('parent_env__') # optional - rpy2 + sage: f('parent_env__') 'parent.env<-' - sage: r._r_to_sage_name(f('parent_env__')) # optional - rpy2 + sage: r._r_to_sage_name(f('parent_env__')) 'parent_env__' - sage: f('class_') # optional - rpy2 + sage: f('class_') 'class' """ if len(s) > 1 and s[-2:] == "__": @@ -1451,11 +1468,12 @@ def tilde(self, x): EXAMPLES:: - sage: x = r([1,2,3,4,5]) # optional - rpy2 - sage: y = r([3,5,7,9,11]) # optional - rpy2 - sage: a = r.lm( y.tilde(x) ) # lm( y ~ x ) # optional - rpy2 - sage: d = a._sage_() # optional - rpy2 - sage: d['DATA']['coefficients']['DATA'][1] # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([1,2,3,4,5]) + sage: y = r([3,5,7,9,11]) + sage: a = r.lm( y.tilde(x) ) # lm( y ~ x ) + sage: d = a._sage_() + sage: d['DATA']['coefficients']['DATA'][1] 2 """ par = self.parent() @@ -1505,11 +1523,12 @@ def __getattr__(self, attrname): EXAMPLES:: - sage: x = r([1,2,3]) # optional - rpy2 - sage: length = x.length # optional - rpy2 - sage: type(length) # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([1,2,3]) + sage: length = x.length + sage: type(length) - sage: length() # optional - rpy2 + sage: length() [1] 3 """ try: @@ -1535,24 +1554,25 @@ def __getitem__(self, n): EXAMPLES:: - sage: x = r([10.4,5.6,3.1,6.4,21.7]) # optional - rpy2 - sage: x[0] # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([10.4,5.6,3.1,6.4,21.7]) + sage: x[0] numeric(0) - sage: x[1] # optional - rpy2 + sage: x[1] [1] 10.4 - sage: x[-1] # optional - rpy2 + sage: x[-1] [1] 5.6 3.1 6.4 21.7 - sage: x[-2] # optional - rpy2 + sage: x[-2] [1] 10.4 3.1 6.4 21.7 - sage: x[-3] # optional - rpy2 + sage: x[-3] [1] 10.4 5.6 6.4 21.7 - sage: x['c(2,3)'] # optional - rpy2 + sage: x['c(2,3)'] [1] 5.6 3.1 - sage: key = r.c(2,3) # optional - rpy2 - sage: x[key] # optional - rpy2 + sage: key = r.c(2,3) + sage: x[key] [1] 5.6 3.1 - sage: m = r.array('1:3',r.c(2,4,2)) # optional - rpy2 - sage: m # optional - rpy2 + sage: m = r.array('1:3',r.c(2,4,2)) + sage: m , , 1 [,1] [,2] [,3] [,4] [1,] 1 3 2 1 @@ -1561,9 +1581,9 @@ def __getitem__(self, n): [,1] [,2] [,3] [,4] [1,] 3 2 1 3 [2,] 1 3 2 1 - sage: m[1,2,2] # optional - rpy2 + sage: m[1,2,2] [1] 2 - sage: m[1,r.c(1,2),1] # optional - rpy2 + sage: m[1,r.c(1,2),1] [1] 1 3 """ P = self._check_valid() @@ -1593,15 +1613,16 @@ def __bool__(self): EXAMPLES:: - sage: x = r([10.4,5.6,3.1,6.4,21.7]) # optional - rpy2 - sage: bool(x) # optional - rpy2 + sage: # optional - rpy2 + sage: x = r([10.4,5.6,3.1,6.4,21.7]) + sage: bool(x) True - sage: y = r([0,0,0,0]) # optional - rpy2 - sage: bool(y) # optional - rpy2 + sage: y = r([0,0,0,0]) + sage: bool(y) False - sage: bool(r(0)) # optional - rpy2 + sage: bool(r(0)) False - sage: bool(r(1)) # optional - rpy2 + sage: bool(r(1)) True """ return "FALSE" in repr(self == 0) @@ -2049,12 +2070,13 @@ def r_version(): EXAMPLES:: - sage: r_version() # not tested # optional - rpy2 + sage: # optional - rpy2 + sage: r_version() # not tested ((3, 0, 1), 'R version 3.0.1 (2013-05-16)') - sage: rint, rstr = r_version() # optional - rpy2 - sage: rint[0] >= 3 # optional - rpy2 + sage: rint, rstr = r_version() + sage: rint[0] >= 3 True - sage: rstr.startswith('R version') # optional - rpy2 + sage: rstr.startswith('R version') True """ return r.version() diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index fc2f53081e3..411e7cb6bca 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -175,13 +175,14 @@ You can launch web-pages attached to the links:: - sage: K.diagram() # not tested + sage: # not tested + sage: K.diagram() True - sage: L.diagram(single=True) # not tested + sage: L.diagram(single=True) True - sage: L.knot_atlas_webpage() # not tested + sage: L.knot_atlas_webpage() True - sage: K.knotilus_webpage() # not tested + sage: K.knotilus_webpage() True and the description web-pages of the properties:: diff --git a/src/sage/libs/all__sagemath_coxeter3.py b/src/sage/libs/all__sagemath_coxeter3.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/libs/all__sagemath_meataxe.py b/src/sage/libs/all__sagemath_meataxe.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/libs/all__sagemath_sirocco.py b/src/sage/libs/all__sagemath_sirocco.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/libs/coxeter3/all__sagemath_coxeter3.py b/src/sage/libs/coxeter3/all__sagemath_coxeter3.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/libs/coxeter3/coxeter.pxd b/src/sage/libs/coxeter3/coxeter.pxd index cffa2505e39..9d5cb2a3f5d 100644 --- a/src/sage/libs/coxeter3/coxeter.pxd +++ b/src/sage/libs/coxeter3/coxeter.pxd @@ -1,3 +1,5 @@ +# sage_setup: distribution = sagemath-coxeter3 + #***************************************************************************** # Copyright (C) 2009-2013 Mike Hansen # diff --git a/src/sage/libs/coxeter3/coxeter.pyx b/src/sage/libs/coxeter3/coxeter.pyx index 61fa617ede1..b6444ea177d 100644 --- a/src/sage/libs/coxeter3/coxeter.pyx +++ b/src/sage/libs/coxeter3/coxeter.pyx @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # distutils: language = c++ # distutils: libraries = coxeter3 # sage_setup: distribution = sagemath-coxeter3 diff --git a/src/sage/libs/coxeter3/coxeter_group.py b/src/sage/libs/coxeter3/coxeter_group.py index ab8dfb2ae0c..527cfc61c6f 100644 --- a/src/sage/libs/coxeter3/coxeter_group.py +++ b/src/sage/libs/coxeter3/coxeter_group.py @@ -1,4 +1,5 @@ -# -*- coding: utf-8 -*- +# sage_setup: distribution = sagemath-coxeter3 + """ Coxeter Groups implemented with Coxeter3 """ diff --git a/src/sage/libs/coxeter3/decl.pxd b/src/sage/libs/coxeter3/decl.pxd index 56002154226..4f9c7b0c186 100644 --- a/src/sage/libs/coxeter3/decl.pxd +++ b/src/sage/libs/coxeter3/decl.pxd @@ -1,3 +1,5 @@ +# sage_setup: distribution = sagemath-coxeter3 + #***************************************************************************** # Copyright (C) 2009-2013 Mike Hansen # diff --git a/src/sage/libs/meataxe.pxd b/src/sage/libs/meataxe.pxd index 68c8b3467b0..0a928e19c37 100644 --- a/src/sage/libs/meataxe.pxd +++ b/src/sage/libs/meataxe.pxd @@ -1,3 +1,5 @@ +# sage_setup: distribution = sagemath-meataxe + #***************************************************************************** # Copyright (C) 2015 Simon King # diff --git a/src/sage/matrix/all__sagemath_meataxe.py b/src/sage/matrix/all__sagemath_meataxe.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/matrix/matrix_gfpn_dense.pxd b/src/sage/matrix/matrix_gfpn_dense.pxd index 92b0a78d6d2..7a457876b9a 100644 --- a/src/sage/matrix/matrix_gfpn_dense.pxd +++ b/src/sage/matrix/matrix_gfpn_dense.pxd @@ -1,3 +1,5 @@ +# sage_setup: distribution = sagemath-meataxe + #***************************************************************************** # Copyright (C) 2015 Simon King # diff --git a/src/sage/matroids/linear_matroid.pyx b/src/sage/matroids/linear_matroid.pyx index 30bca7bea29..11f6b4a2656 100644 --- a/src/sage/matroids/linear_matroid.pyx +++ b/src/sage/matroids/linear_matroid.pyx @@ -578,7 +578,7 @@ cdef class LinearMatroid(BasisExchangeMatroid): sage: M.representation(lift_map=lift_map('sru')) # needs sage.rings.finite_rings [ 1 0 0 1 0 1 1 1] [ 0 1 0 -z + 1 1 0 0 1] - [ 0 0 1 0 -z z 1 0] + [ 0 0 1 0 1 -1 z - 1 0] """ cdef LeanMatrix A if order is None: diff --git a/src/sage/matroids/matroid.pyx b/src/sage/matroids/matroid.pyx index 3caac5bea62..dcee87ea49c 100644 --- a/src/sage/matroids/matroid.pyx +++ b/src/sage/matroids/matroid.pyx @@ -6114,8 +6114,8 @@ cdef class Matroid(SageObject): entries = [(e, f, (e, f)) for e in basis for f in self._fundamental_cocircuit(basis, e).difference([e])] G = Graph(entries) T = set() - for C in G.connected_components(): - T.update(G.subgraph(C).min_spanning_tree()) + for C in G.connected_components_subgraphs(): + T.update(C.min_spanning_tree()) for edge in T: e,f = edge[2] A.set(bdx[e],idx[f], 1) diff --git a/src/sage/matroids/utilities.py b/src/sage/matroids/utilities.py index 6351c536413..a8612dab29f 100644 --- a/src/sage/matroids/utilities.py +++ b/src/sage/matroids/utilities.py @@ -392,8 +392,8 @@ def spanning_forest(M): G.add_edge(x + m, y) T = [] # find spanning tree in each component - for component in G.connected_components(): - spanning_tree = kruskal(G.subgraph(component)) + for component in G.connected_components_subgraphs(): + spanning_tree = kruskal(component) for (x, y, z) in spanning_tree: if x < m: t = x @@ -546,9 +546,9 @@ def lift_cross_ratios(A, lift_map=None): [0 6 3 6 0] sage: Z = lift_cross_ratios(A, to_sixth_root_of_unity) # needs sage.rings.finite_rings sage.rings.number_field sage: Z # needs sage.rings.finite_rings sage.rings.number_field - [ 1 0 1 1 1] - [ 1 1 0 0 z] - [ 0 -1 z 1 0] + [ 1 0 1 1 1] + [-z + 1 1 0 0 1] + [ 0 -1 1 -z + 1 0] sage: M = LinearMatroid(reduced_matrix=A) sage: sorted(M.cross_ratios()) [3, 5] @@ -573,8 +573,8 @@ def lift_cross_ratios(A, lift_map=None): G = Graph([((r, 0), (c, 1), (r, c)) for r, c in A.nonzero_positions()]) # write the entries of (a scaled version of) A as products of cross ratios of A T = set() - for C in G.connected_components(): - T.update(G.subgraph(C).min_spanning_tree()) + for C in G.connected_components_subgraphs(): + T.update(C.min_spanning_tree()) # - fix a tree of the support graph G to units (= empty dict, product of 0 terms) F = {entry[2]: dict() for entry in T} W = set(G.edge_iterator()) - set(T) diff --git a/src/sage/misc/latex_standalone.py b/src/sage/misc/latex_standalone.py index cd42519c5ec..3ed2476fe9a 100644 --- a/src/sage/misc/latex_standalone.py +++ b/src/sage/misc/latex_standalone.py @@ -156,35 +156,36 @@ tikzpicture code generated by Sage from some polyhedron:: sage: from sage.misc.latex_standalone import TikzPicture - sage: V = [[1,0,1],[1,0,0],[1,1,0],[0,0,-1],[0,1,0],[-1,0,0],[0,1,1],[0,0,1],[0,-1,0]] - sage: P = Polyhedron(vertices=V).polar() - sage: s = P.projection().tikz([674,108,-731],112, output_type='LatexExpr') - sage: t = TikzPicture(s) + sage: V = [[1,0,1], [1,0,0], [1,1,0], [0,0,-1], + ....: [0,1,0], [-1,0,0], [0,1,1], [0,0,1], [0,-1,0]] + sage: P = Polyhedron(vertices=V).polar() # needs sage.geometry.polyhedron + sage: s1 = P.projection().tikz([674,108,-731],112, output_type='LatexExpr') # needs sage.geometry.polyhedron sage.plot + sage: t1 = TikzPicture(s1) # needs sage.geometry.polyhedron sage.plot Open the image in a viewer (the returned value is a string giving the absolute path to the file in some temporary directory):: - sage: path_to_file = t.pdf() # not tested + sage: path_to_file = t1.pdf() # not tested # needs sage.geometry.polyhedron sage.plot Instead, you may save a pdf of the tikzpicture into a file of your choice (but this does not open the viewer):: - sage: _ = t.pdf('tikz_polytope.pdf') # not tested + sage: _ = t1.pdf('tikz_polytope.pdf') # not tested # needs sage.geometry.polyhedron sage.plot Opening the image in a viewer can be turned off:: - sage: _ = t.pdf(view=False) # long time (2s) # optional latex + sage: _ = t1.pdf(view=False) # long time (2s), optional - latex # needs sage.geometry.polyhedron sage.plot The same can be done with png format (translated from pdf with convert command which needs the installation of imagemagick):: - sage: _ = t.png(view=False) # long time (2s) # optional latex imagemagick + sage: _ = t1.png(view=False) # long time (2s), optional - imagemagick latex, needs sage.geometry.polyhedron sage.plot The string representation gives the header (5 lines) and tail (5 lines) of the tikzpicture. In Jupyter, it will instead use rich representation and show the image directly below the cell in png or svg format:: - sage: t + sage: t1 # needs sage.geometry.polyhedron sage.plot \documentclass[tikz]{standalone} \begin{document} \begin{tikzpicture}% @@ -201,21 +202,24 @@ Use ``print(t)`` to see the complete content of the file:: - sage: print(t) # not tested + sage: print(t1) # not tested # needs sage.geometry.polyhedron sage.plot Adding a border in the options avoids cropping the vertices of a graph:: - sage: g = graphs.PetersenGraph() # optional - sage.graphs - sage: s = latex(g) # takes 3s but the result is cached # optional latex sage.graphs - sage: t = TikzPicture(s, standalone_config=["border=4mm"], usepackage=['tkz-graph']) # optional latex sage.graphs - sage: _ = t.pdf() # not tested + sage: # needs sage.graphs + sage: g = graphs.PetersenGraph() + sage: s2 = latex(g) # takes 3s but the result is cached # optional - latex + sage: t2 = TikzPicture(s2, standalone_config=["border=4mm"], # optional - latex + ....: usepackage=['tkz-graph']) + sage: _ = t2.pdf() # not tested The current latex representation of a transducer is a tikzpicture using the tikz library automata. The string can be used as input:: - sage: s = latex(transducers.GrayCode()) # optional sage.combinat - sage: t = TikzPicture(s, usetikzlibrary=['automata']) # optional sage.combinat - sage: _ = t.pdf(view=False) # long time (2s) # optional sage.combinat latex + sage: # needs sage.graphs sage.modules + sage: s3 = latex(transducers.GrayCode()) + sage: t3 = TikzPicture(s3, usetikzlibrary=['automata']) + sage: _ = t3.pdf(view=False) # long time (2s) # optional - latex AUTHORS: @@ -274,7 +278,8 @@ class Standalone(SageObject): :: - sage: t = Standalone(content, standalone_config=["border=4mm"], usepackage=['amsmath']) + sage: t = Standalone(content, standalone_config=["border=4mm"], + ....: usepackage=['amsmath']) sage: t \documentclass{standalone} \standaloneconfig{border=4mm} @@ -645,28 +650,28 @@ def pdf(self, filename=None, view=True, program=None): sage: from sage.misc.latex_standalone import Standalone sage: t = Standalone('Hello World') - sage: _ = t.pdf(view=False) # long time (1s) # optional latex + sage: _ = t.pdf(view=False) # long time (1s) # optional - latex Same for instances of :class:`TikzPicture`:: sage: from sage.misc.latex_standalone import TikzPicture sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: t = TikzPicture(s) - sage: _ = t.pdf(view=False) # not tested + sage: _ = t.pdf(view=False) # not tested A filename may be provided where to save the file, in which case the viewer does not open the file:: sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp','.pdf') - sage: path_to_file = t.pdf(filename) # long time (1s) # optional latex - sage: path_to_file[-4:] # long time (fast) # optional latex + sage: path_to_file = t.pdf(filename) # long time (1s) # optional - latex + sage: path_to_file[-4:] # long time (fast) # optional - latex '.pdf' The filename may contain spaces:: sage: filename = tmp_filename('filename with spaces','.pdf') - sage: path_to_file = t.pdf(filename) # long time (1s) # optional latex + sage: path_to_file = t.pdf(filename) # long time (1s) # optional - latex TESTS: @@ -675,7 +680,7 @@ def pdf(self, filename=None, view=True, program=None): sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: s_missing_last_character = s[:-1] sage: t = TikzPicture(s_missing_last_character) - sage: _ = t.pdf() # optional latex + sage: _ = t.pdf() # optional - latex Traceback (most recent call last): ... CalledProcessError: Command '['...latex', '-interaction=nonstopmode', @@ -769,28 +774,28 @@ def dvi(self, filename=None, view=True, program='latex'): sage: from sage.misc.latex_standalone import Standalone sage: t = Standalone('Hello World') - sage: _ = t.dvi(view=False) # long time (1s) # optional latex + sage: _ = t.dvi(view=False) # long time (1s) # optional - latex Same for instances of :class:`TikzPicture`:: sage: from sage.misc.latex_standalone import TikzPicture sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: t = TikzPicture(s) - sage: _ = t.dvi(view=False) # not tested + sage: _ = t.dvi(view=False) # not tested A filename may be provided where to save the file, in which case the viewer does not open the file:: sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp','.dvi') - sage: path_to_file = t.dvi(filename) # long time (1s) # optional latex - sage: path_to_file[-4:] # long time (fast) # optional latex + sage: path_to_file = t.dvi(filename) # long time (1s) # optional - latex + sage: path_to_file[-4:] # long time (fast) # optional - latex '.dvi' The filename may contain spaces:: sage: filename = tmp_filename('filename with spaces','.dvi') - sage: path_to_file = t.dvi(filename) # long time (1s) # optional latex + sage: path_to_file = t.dvi(filename) # long time (1s) # optional - latex TESTS: @@ -799,7 +804,7 @@ def dvi(self, filename=None, view=True, program='latex'): sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: s_missing_last_character = s[:-1] sage: t = TikzPicture(s_missing_last_character) - sage: _ = t.dvi() # optional latex + sage: _ = t.dvi() # optional - latex Traceback (most recent call last): ... CalledProcessError: Command '['latex', '-interaction=nonstopmode', @@ -897,21 +902,21 @@ def png(self, filename=None, density=150, view=True): sage: from sage.misc.latex_standalone import Standalone sage: t = Standalone('Hello World') - sage: _ = t.png(view=False) # long time (1s) # optional latex imagemagick + sage: _ = t.png(view=False) # long time (1s) # optional - latex imagemagick Same for instances of :class:`TikzPicture`:: sage: from sage.misc.latex_standalone import TikzPicture sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: t = TikzPicture(s) - sage: _ = t.png(view=False) # not tested + sage: _ = t.png(view=False) # not tested :: sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp','.png') - sage: path_to_file = t.png(filename) # long time (1s) # optional latex imagemagick - sage: path_to_file[-4:] # long time (fast) # optional latex imagemagick + sage: path_to_file = t.png(filename) # long time (1s) # optional - latex imagemagick + sage: path_to_file[-4:] # long time (fast) # optional - latex imagemagick '.png' """ @@ -999,11 +1004,13 @@ def svg(self, filename=None, view=True, program='pdftocairo'): sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp', '.svg') - sage: path_to_file = t.svg(filename, program='pdf2svg') # long time (1s) # optional latex pdf2svg - sage: path_to_file[-4:] # long time (fast) # optional latex pdf2svg + sage: path_to_file = t.svg(filename, # long time (1s) # optional - latex pdf2svg + ....: program='pdf2svg') + sage: path_to_file[-4:] # long time (fast) # optional - latex pdf2svg '.svg' - sage: path_to_file = t.svg(filename, program='pdftocairo') # long time (1s) # optional latex pdftocairo - sage: path_to_file[-4:] # long time (fast) # optional latex pdftocairo + sage: path_to_file = t.svg(filename, # long time (1s) # optional - latex pdftocairo + ....: program='pdftocairo') + sage: path_to_file[-4:] # long time (fast) # optional - latex pdftocairo '.svg' """ @@ -1099,11 +1106,13 @@ def eps(self, filename=None, view=True, program='dvips'): sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp', '.eps') - sage: path_to_file = t.eps(filename, program='dvips') # long time (1s) # optional latex dvips - sage: path_to_file[-4:] # long time (fast) # optional latex dvips + sage: path_to_file = t.eps(filename, # long time (1s) # optional - latex dvips + ....: program='dvips') + sage: path_to_file[-4:] # long time (fast) # optional - latex dvips '.eps' - sage: path_to_file = t.eps(filename, program='pdftocairo') # long time (1s) # optional latex pdftocairo - sage: path_to_file[-4:] # long time (fast) # optional latex pdftocairo + sage: path_to_file = t.eps(filename, # long time (1s) # optional - latex pdftocairo + ....: program='pdftocairo') + sage: path_to_file[-4:] # long time (fast) # optional - latex pdftocairo '.eps' TESTS: @@ -1278,9 +1287,9 @@ def save(self, filename, **kwds): sage: from sage.misc.latex_standalone import Standalone sage: t = Standalone('Hello World') sage: filename = tmp_filename('temp','.pdf') - sage: t.save(filename) # long time (1s) # optional latex + sage: t.save(filename) # long time (1s) # optional - latex sage: filename = tmp_filename('temp','.eps') - sage: t.save(filename) # long time (1s) # optional latex dvips + sage: t.save(filename) # long time (1s) # optional - latex dvips """ ext = os.path.splitext(filename)[1].lower() @@ -1344,19 +1353,22 @@ class TikzPicture(Standalone): below methods return a string providing the path to the filename, which is by default in a temporary folder:: - sage: _ = t.pdf() # not tested - sage: _ = t.png() # not tested - sage: _ = t.svg() # not tested - sage: _ = t.tex() # not tested - sage: _ = t.pdf(filename='abc.pdf') # not tested + sage: # not tested + sage: _ = t.pdf() + sage: _ = t.png() + sage: _ = t.svg() + sage: _ = t.tex() + sage: _ = t.pdf(filename='abc.pdf') Here we create a tikzpicture for the latex representation of a graph. This is using tkz-graph tex library:: - sage: g = graphs.PetersenGraph() # optional sage.graphs - sage: s = latex(g) # optional sage.graphs latex - sage: t = TikzPicture(s, standalone_config=["border=4mm"], usepackage=['tkz-graph']) # optional sage.graphs latex - sage: _ = t.pdf(view=False) # long time (2s) # optional - sage.graphs latex latex_package_tkz_graph + sage: # needs sage.graphs + sage: g = graphs.PetersenGraph() + sage: s = latex(g) # optional - latex + sage: t = TikzPicture(s, standalone_config=["border=4mm"], # optional - latex + ....: usepackage=['tkz-graph']) + sage: _ = t.pdf(view=False) # long time (2s), optional - latex latex_package_tkz_graph Here are standalone configurations, packages, tikz libraries and macros that can be set:: @@ -1371,7 +1383,7 @@ class TikzPicture(Standalone): sage: s = "\\begin{tikzpicture}\n\\draw (0,0) -- (1,1);\n\\end{tikzpicture}" sage: t = TikzPicture(s, standalone_config=options, usepackage=usepackage, ....: usetikzlibrary=tikzlib, macros=macros) - sage: _ = t.pdf(view=False) # long time (2s) # optional latex + sage: _ = t.pdf(view=False) # long time (2s), optional - latex """ def __init__(self, content, standalone_config=None, usepackage=None, usetikzlibrary=None, macros=None, use_sage_preamble=False): @@ -1453,30 +1465,34 @@ def from_dot_string(cls, dotdata, prog='dot'): EXAMPLES:: + sage: # needs sage.graphs sage: from sage.misc.latex_standalone import TikzPicture - sage: G = graphs.PetersenGraph() # optional sage.graphs - sage: dotdata = G.graphviz_string() # optional sage.graphs - sage: tikz = TikzPicture.from_dot_string(dotdata) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: G = graphs.PetersenGraph() + sage: dotdata = G.graphviz_string() + sage: tikz = TikzPicture.from_dot_string(dotdata) # long time (3s), optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested :: - sage: dotdata = G.graphviz_string(labels='latex') # optional sage.graphs - sage: tikz = TikzPicture.from_dot_string(dotdata) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: # needs sage.graphs + sage: dotdata = G.graphviz_string(labels='latex') + sage: tikz = TikzPicture.from_dot_string(dotdata) # long time (3s), optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested :: + sage: # needs sage.combinat sage.graphs sage.groups sage: W = CoxeterGroup(["A",2]) - sage: G = W.cayley_graph() # optional sage.graphs - sage: dotdata = G.graphviz_string() # optional sage.graphs - sage: tikz = TikzPicture.from_dot_string(dotdata) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: G = W.cayley_graph() + sage: dotdata = G.graphviz_string() + sage: tikz = TikzPicture.from_dot_string(dotdata) # long time (3s), optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested :: - sage: dotdata = G.graphviz_string(labels='latex') # optional sage.graphs - sage: tikz = TikzPicture.from_dot_string(dotdata) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: # needs sage.combinat sage.graphs sage.groups + sage: dotdata = G.graphviz_string(labels='latex') + sage: tikz = TikzPicture.from_dot_string(dotdata) # long time (3s), optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested """ @@ -1538,9 +1554,10 @@ def from_graph(cls, graph, merge_multiedges=True, EXAMPLES:: + sage: # needs sage.graphs sage: from sage.misc.latex_standalone import TikzPicture - sage: g = graphs.PetersenGraph() # optional sage.graphs - sage: tikz = TikzPicture.from_graph(g) # optional sage.graphs dot2tex graphviz + sage: g = graphs.PetersenGraph() + sage: tikz = TikzPicture.from_graph(g) # optional - dot2tex graphviz doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. See https://github.com/sagemath/sage/issues/20343 for details. @@ -1548,28 +1565,33 @@ def from_graph(cls, graph, merge_multiedges=True, Using ``prog``:: - sage: tikz = TikzPicture.from_graph(g, prog='neato', color_by_label=True) # optional sage.graphs dot2tex graphviz # long time (3s) + sage: # needs sage.graphs + sage: tikz = TikzPicture.from_graph(g, prog='neato', # long time (3s), optional - dot2tex graphviz + ....: color_by_label=True) sage: _ = tikz.pdf() # not tested Using ``rankdir``:: - sage: tikz = TikzPicture.from_graph(g, rankdir='right') # optional sage.graphs dot2tex graphviz # long time (3s) + sage: tikz = TikzPicture.from_graph(g, rankdir='right') # long time (3s), optional - dot2tex graphviz, needs sage.graphs sage: _ = tikz.pdf() # not tested Using ``merge_multiedges``:: + sage: # needs sage.graphs sage.modules sage.symbolic sage: alpha = var('alpha') - sage: m = matrix(2,range(4)); m.set_immutable() - sage: G = DiGraph([(0,1,alpha), (0,1,0), (0,2,9), (0,2,m)], multiedges=True) # optional sage.graphs - sage: tikz = TikzPicture.from_graph(G, merge_multiedges=True) # optional sage.graphs dot2tex graphviz + sage: m = matrix(2, range(4)); m.set_immutable() + sage: G = DiGraph([(0,1,alpha), (0,1,0), (0,2,9), (0,2,m)], + ....: multiedges=True) + sage: tikz = TikzPicture.from_graph(G, merge_multiedges=True) # optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested Using ``merge_multiedges`` with ``merge_label_function``:: + sage: # needs sage.graphs sage: fn = lambda L: LatexExpr(','.join(map(str, L))) sage: edges = [(0,1,'a'), (0,1,'b'), (0,2,'c'), (0,2,'d')] - sage: G = DiGraph(edges, multiedges=True) # optional sage.graphs - sage: tikz = TikzPicture.from_graph(G, # optional sage.graphs dot2tex graphviz + sage: G = DiGraph(edges, multiedges=True) + sage: tikz = TikzPicture.from_graph(G, # optional - dot2tex graphviz ....: merge_multiedges=True, merge_label_function=fn) sage: _ = tikz.pdf() # not tested @@ -1581,24 +1603,25 @@ def from_graph(cls, graph, merge_multiedges=True, sage: a = S((0,1,3,0,0)) sage: b = S((0,2,4,1,0)) sage: roots = [I] - sage: succ = lambda v:[v*a,v*b,a*v,b*v] + sage: succ = lambda v: [v*a,v*b,a*v,b*v] sage: R = RecursivelyEnumeratedSet(roots, succ) - sage: G = R.to_digraph() # optional sage.graphs - sage: G # optional sage.graphs + sage: G = R.to_digraph() # needs sage.graphs + sage: G # needs sage.graphs Looped multi-digraph on 27 vertices - sage: C = G.strongly_connected_components() # optional sage.graphs - sage: tikz = TikzPicture.from_graph(G, # optional sage.graphs dot2tex graphviz + sage: C = G.strongly_connected_components() # needs sage.graphs + sage: tikz = TikzPicture.from_graph(G, # optional - dot2tex graphviz, needs sage.graphs ....: merge_multiedges=False, subgraph_clusters=C) sage: _ = tikz.pdf() # not tested An example coming from ``graphviz_string`` documentation in SageMath:: - sage: f(x) = -1 / x # optional sage.symbolic - sage: g(x) = 1 / (x + 1) # optional sage.symbolic - sage: G = DiGraph() # optional sage.symbolic sage.graphs - sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) # optional sage.symbolic sage.graphs - sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) # optional sage.symbolic sage.graphs - sage: tikz = TikzPicture.from_graph(G) # optional sage.symbolic sage.graphs dot2tex graphviz + sage: # needs sage.graphs sage.symbolic + sage: f(x) = -1 / x + sage: g(x) = 1 / (x + 1) + sage: G = DiGraph() + sage: G.add_edges((i, f(i), f) for i in (1, 2, 1/2, 1/4)) + sage: G.add_edges((i, g(i), g) for i in (1, 2, 1/2, 1/4)) + sage: tikz = TikzPicture.from_graph(G) # optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested sage: def edge_options(data): ....: u, v, label = data @@ -1608,7 +1631,7 @@ def from_graph(cls, graph, merge_multiedges=True, ....: if (u,v) == (1, -1): options["label_style"] = "latex" ....: if (u,v) == (1, 1/2): options["dir"] = "back" ....: return options - sage: tikz = TikzPicture.from_graph(G, edge_options=edge_options) # optional sage.symbolic sage.graphs dot2tex graphviz + sage: tikz = TikzPicture.from_graph(G, edge_options=edge_options) # optional - dot2tex graphviz sage: _ = tikz.pdf() # not tested """ @@ -1668,20 +1691,23 @@ def from_graph_with_pos(cls, graph, scale=1, merge_multiedges=True, EXAMPLES:: sage: from sage.misc.latex_standalone import TikzPicture - sage: g = graphs.PetersenGraph() # optional sage.graphs - sage: tikz = TikzPicture.from_graph_with_pos(g) # optional sage.graphs + + sage: # needs sage.graphs + sage: g = graphs.PetersenGraph() + sage: tikz = TikzPicture.from_graph_with_pos(g) doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. See https://github.com/sagemath/sage/issues/20343 for details. :: + sage: # needs sage.graphs sage: edges = [(0,0,'a'),(0,1,'b'),(0,1,'c')] sage: kwds = dict(format='list_of_edges', loops=True, multiedges=True) - sage: G = DiGraph(edges, **kwds) # optional sage.graphs - sage: G.set_pos({0:(0,0), 1:(1,0)}) # optional sage.graphs - sage: f = lambda label:','.join(label) # optional sage.graphs - sage: TikzPicture.from_graph_with_pos(G, merge_label_function=f) # optional sage.graphs + sage: G = DiGraph(edges, **kwds) + sage: G.set_pos({0:(0,0), 1:(1,0)}) + sage: f = lambda label:','.join(label) + sage: TikzPicture.from_graph_with_pos(G, merge_label_function=f) \documentclass[tikz]{standalone} \standaloneconfig{border=4mm} \begin{document} @@ -1699,10 +1725,11 @@ def from_graph_with_pos(cls, graph, scale=1, merge_multiedges=True, TESTS:: + sage: # needs sage.graphs sage: edges = [(0,0,'a'),(0,1,'b'),(0,1,'c')] sage: kwds = dict(format='list_of_edges', loops=True, multiedges=True) - sage: G = DiGraph(edges, **kwds) # optional sage.graphs - sage: TikzPicture.from_graph_with_pos(G) # optional sage.graphs + sage: G = DiGraph(edges, **kwds) + sage: TikzPicture.from_graph_with_pos(G) Traceback (most recent call last): ... ValueError: vertex positions need to be set first @@ -1796,21 +1823,25 @@ def from_poset(cls, poset, **kwds): EXAMPLES:: sage: from sage.misc.latex_standalone import TikzPicture - sage: P = posets.PentagonPoset() # optional sage.combinat - sage: tikz = TikzPicture.from_poset(P) # optional sage.combinat dot2tex graphviz + + sage: # needs sage.graphs sage.modules + sage: P = posets.PentagonPoset() + sage: tikz = TikzPicture.from_poset(P) # optional - dot2tex graphviz doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. See https://github.com/sagemath/sage/issues/20343 for details. :: - sage: tikz = TikzPicture.from_poset(P, prog='neato', color_by_label=True) # optional sage.combinat dot2tex # long time (3s) + sage: tikz = TikzPicture.from_poset(P, prog='neato', # long time (3s), optional - dot2tex, needs sage.graphs sage.modules + ....: color_by_label=True) :: - sage: P = posets.SymmetricGroupWeakOrderPoset(4) # optional sage.combinat - sage: tikz = TikzPicture.from_poset(P) # optional sage.combinat dot2tex graphviz # long time (4s) - sage: tikz = TikzPicture.from_poset(P, prog='neato') # optional sage.combinat dot2tex graphviz # long time (4s) + sage: # needs sage.graphs + sage: P = posets.SymmetricGroupWeakOrderPoset(4) + sage: tikz = TikzPicture.from_poset(P) # long time (4s), optional - dot2tex graphviz + sage: tikz = TikzPicture.from_poset(P, prog='neato') # long time (4s), optional - dot2tex graphviz """ graph = poset.hasse_diagram() return cls.from_graph(graph, **kwds) diff --git a/src/sage/misc/profiler.py b/src/sage/misc/profiler.py index 1f1673e5edc..a8e3592d935 100644 --- a/src/sage/misc/profiler.py +++ b/src/sage/misc/profiler.py @@ -43,11 +43,12 @@ class Profiler: You can create a checkpoints without a string; ``Profiler`` will use the source code instead:: - sage: p() # not tested - sage: y = factor(25) # not tested - sage: p("last step") # not tested - sage: z = factor(35) # not tested - sage: p() # not tested + sage: # not tested + sage: p() + sage: y = factor(25) + sage: p("last step") + sage: z = factor(35) + sage: p() This will give a nice list of timings between checkpoints:: diff --git a/src/sage/misc/sagedoc.py b/src/sage/misc/sagedoc.py index 5a69dbcb0cc..78a6e1fe3d3 100644 --- a/src/sage/misc/sagedoc.py +++ b/src/sage/misc/sagedoc.py @@ -41,6 +41,7 @@ # **************************************************************************** import os import re +import shutil import sys import pydoc from sage.misc.temporary_file import tmp_dir @@ -589,6 +590,35 @@ def process_mathtt(s): return s +def process_optional_doctest_tags(s): + r""" + Remove ``# optional/needs`` doctest tags for present features from docstring ``s``. + + EXAMPLES: + + sage: from sage.misc.sagedoc import process_optional_doctest_tags + sage: process_optional_doctest_tags("sage: # needs sage.rings.finite_rings\nsage: K. = FunctionField(GF(5^2,'a')); K\nRational function field in x over Finite Field in a of size 5^2") # needs sage.rings.finite_rings + "sage: K. = FunctionField(GF(5^2,'a')); K\nRational function field in x over Finite Field in a of size 5^2" + """ + import io + from sage.doctest.external import available_software + from sage.doctest.parsing import parse_optional_tags, update_optional_tags + + start = 0 + with io.StringIO() as output: + for m in re.finditer('( *sage: *.*#.*)\n', s): + output.write(s[start:m.start(0)]) + line = m.group(1) + tags = [tag for tag in parse_optional_tags(line) + if tag not in available_software] + line = update_optional_tags(line, tags=tags) + if not re.fullmatch(' *sage: *', line): + print(line, file=output) + start = m.end(0) + output.write(s[start:]) + return output.getvalue() + + def format(s, embedded=False): r"""noreplace Format Sage documentation ``s`` for viewing with IPython. @@ -768,6 +798,10 @@ def format(s, embedded=False): s = process_mathtt(s) s = process_extlinks(s, embedded=embedded) s = detex(s, embedded=embedded) + + if not embedded: + s = process_optional_doctest_tags(s) + return s diff --git a/src/sage/quivers/algebra_elements.pyx b/src/sage/quivers/algebra_elements.pyx index cc1cdab8da6..3d05ba7e270 100644 --- a/src/sage/quivers/algebra_elements.pyx +++ b/src/sage/quivers/algebra_elements.pyx @@ -89,15 +89,16 @@ cdef class PathAlgebraElement(RingElement): faster, but the timing for path algebra elements has improved by about 20%:: - sage: timeit('pF^5+3*pF^3') # not tested + sage: # not tested + sage: timeit('pF^5+3*pF^3') 1 loops, best of 3: 338 ms per loop - sage: timeit('pP^5+3*pP^3') # not tested + sage: timeit('pP^5+3*pP^3') 100 loops, best of 3: 2.55 ms per loop - sage: timeit('pF2^7') # not tested + sage: timeit('pF2^7') 10000 loops, best of 3: 513 ms per loop - sage: timeit('pL2^7') # not tested + sage: timeit('pL2^7') 125 loops, best of 3: 1.99 ms per loop - sage: timeit('pP2^7') # not tested + sage: timeit('pP2^7') 10000 loops, best of 3: 1.54 ms per loop So, if one is merely interested in basic arithmetic operations for diff --git a/src/sage/repl/user_globals.py b/src/sage/repl/user_globals.py index 92de7d87e0c..73c13c44690 100644 --- a/src/sage/repl/user_globals.py +++ b/src/sage/repl/user_globals.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.modules r""" User-interface globals diff --git a/src/sage/rings/big_oh.py b/src/sage/rings/big_oh.py index ad43b8fbc84..21938a2256b 100644 --- a/src/sage/rings/big_oh.py +++ b/src/sage/rings/big_oh.py @@ -57,44 +57,45 @@ def O(*x, **kwds): This is also useful to create `p`-adic numbers:: - sage: O(7^6) # optional - sage.rings.padics + sage: O(7^6) # needs sage.rings.padics O(7^6) - sage: 1/3 + O(7^6) # optional - sage.rings.padics + sage: 1/3 + O(7^6) # needs sage.rings.padics 5 + 4*7 + 4*7^2 + 4*7^3 + 4*7^4 + 4*7^5 + O(7^6) It behaves well with respect to adding negative powers of `p`:: - sage: a = O(11^-32); a # optional - sage.rings.padics + sage: a = O(11^-32); a # needs sage.rings.padics O(11^-32) - sage: a.parent() # optional - sage.rings.padics + sage: a.parent() # needs sage.rings.padics 11-adic Field with capped relative precision 20 There are problems if you add a rational with very negative valuation to an `O`-Term:: - sage: 11^-12 + O(11^15) # optional - sage.rings.padics + sage: 11^-12 + O(11^15) # needs sage.rings.padics 11^-12 + O(11^8) The reason that this fails is that the constructor doesn't know the right precision cap to use. If you cast explicitly or use other means of element creation, you can get around this issue:: - sage: K = Qp(11, 30) # optional - sage.rings.padics - sage: K(11^-12) + O(11^15) # optional - sage.rings.padics + sage: # needs sage.rings.padics + sage: K = Qp(11, 30) + sage: K(11^-12) + O(11^15) 11^-12 + O(11^15) - sage: 11^-12 + K(O(11^15)) # optional - sage.rings.padics + sage: 11^-12 + K(O(11^15)) 11^-12 + O(11^15) - sage: K(11^-12, absprec=15) # optional - sage.rings.padics + sage: K(11^-12, absprec=15) 11^-12 + O(11^15) - sage: K(11^-12, 15) # optional - sage.rings.padics + sage: K(11^-12, 15) 11^-12 + O(11^15) We can also work with `asymptotic expansions`_:: - sage: A. = AsymptoticRing(growth_group='QQ^n * n^QQ * log(n)^QQ', # optional - sage.symbolic + sage: A. = AsymptoticRing(growth_group='QQ^n * n^QQ * log(n)^QQ', # needs sage.symbolic ....: coefficient_ring=QQ); A Asymptotic Ring over Rational Field - sage: O(n) # optional - sage.symbolic + sage: O(n) # needs sage.symbolic O(n) Application with Puiseux series:: @@ -108,17 +109,17 @@ def O(*x, **kwds): TESTS:: - sage: var('x, y') # optional - sage.symbolic + sage: var('x, y') # needs sage.symbolic (x, y) - sage: O(x) # optional - sage.symbolic + sage: O(x) # needs sage.symbolic Traceback (most recent call last): ... ArithmeticError: O(x) not defined - sage: O(y) # optional - sage.symbolic + sage: O(y) # needs sage.symbolic Traceback (most recent call last): ... ArithmeticError: O(y) not defined - sage: O(x, y) # optional - sage.symbolic + sage: O(x, y) # needs sage.symbolic Traceback (most recent call last): ... ArithmeticError: O(x, y) not defined diff --git a/src/sage/rings/cfinite_sequence.py b/src/sage/rings/cfinite_sequence.py index 14ae02423e4..733fbcbd6a7 100644 --- a/src/sage/rings/cfinite_sequence.py +++ b/src/sage/rings/cfinite_sequence.py @@ -1265,10 +1265,11 @@ def guess(self, sequence, algorithm='sage'): r""" .. TODO:: - sage: CFiniteSequence(x+x^2+x^3+x^4+x^5+O(x^6)) # not implemented - sage: latex(r) # not implemented + sage: # not implemented + sage: CFiniteSequence(x+x^2+x^3+x^4+x^5+O(x^6)) + sage: latex(r) \big\{a_{n\ge0}\big|a_{n+2}=\sum_{i=0}^{1}c_ia_{n+i}, c=\{1,1\}, a_{n<2}=\{0,0,0,1\}\big\} - sage: r.egf() # not implemented + sage: r.egf() exp(2*x) - sage: r = CFiniteSequence(1/(1-y-x*y), x) # not implemented + sage: r = CFiniteSequence(1/(1-y-x*y), x) """ diff --git a/src/sage/rings/function_field/constructor.py b/src/sage/rings/function_field/constructor.py index 37aa8494a9e..8e5ecd281ee 100644 --- a/src/sage/rings/function_field/constructor.py +++ b/src/sage/rings/function_field/constructor.py @@ -57,10 +57,10 @@ class FunctionFieldFactory(UniqueFactory): sage: K. = FunctionField(QQ); K Rational function field in x over Rational Field - sage: L. = FunctionField(GF(7)); L # optional - sage.rings.finite_rings + sage: L. = FunctionField(GF(7)); L Rational function field in y over Finite Field of size 7 - sage: R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^7 - z - y); M # optional - sage.rings.finite_rings sage.rings.function_field + sage: R. = L[] + sage: M. = L.extension(z^7 - z - y); M # needs sage.rings.finite_rings sage.rings.function_field Function field in z defined by z^7 + 6*z + 6*y TESTS:: @@ -69,8 +69,8 @@ class FunctionFieldFactory(UniqueFactory): sage: L. = FunctionField(QQ) sage: K is L True - sage: M. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: K is M # optional - sage.rings.finite_rings + sage: M. = FunctionField(GF(7)) + sage: K is M False sage: N. = FunctionField(QQ) sage: K is N @@ -136,9 +136,9 @@ class FunctionFieldExtensionFactory(UniqueFactory): sage: y2 = y*1 sage: y2 is y False - sage: L. = K.extension(x - y^2) # optional - sage.rings.function_field - sage: M. = K.extension(x - y2^2) # optional - sage.rings.function_field - sage: L is M # optional - sage.rings.function_field + sage: L. = K.extension(x - y^2) # needs sage.rings.function_field + sage: M. = K.extension(x - y2^2) # needs sage.rings.function_field + sage: L is M # needs sage.rings.function_field True """ def create_key(self,polynomial,names): @@ -150,7 +150,7 @@ def create_key(self,polynomial,names): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(x - y^2) # indirect doctest # optional - sage.rings.function_field + sage: L. = K.extension(x - y^2) # indirect doctest # needs sage.rings.function_field TESTS: @@ -158,12 +158,12 @@ def create_key(self,polynomial,names): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field - sage: R. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z - 1) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: R. = L[] # needs sage.rings.function_field + sage: M. = L.extension(z - 1) # needs sage.rings.function_field sage: R. = K[] - sage: N. = K.extension(z - 1) # optional - sage.rings.function_field - sage: M is N # optional - sage.rings.function_field + sage: N. = K.extension(z - 1) # needs sage.rings.function_field + sage: M is N # needs sage.rings.function_field False """ @@ -182,10 +182,10 @@ def create_object(self,version,key,**extra_args): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(x - y^2) # indirect doctest # optional - sage.rings.function_field - sage: y2 = y*1 # optional - sage.rings.function_field - sage: M. = K.extension(x - y2^2) # indirect doctest # optional - sage.rings.function_field - sage: L is M # optional - sage.rings.function_field + sage: L. = K.extension(x - y^2) # indirect doctest # needs sage.rings.function_field + sage: y2 = y*1 + sage: M. = K.extension(x - y2^2) # indirect doctest # needs sage.rings.function_field + sage: L is M # needs sage.rings.function_field True """ from . import function_field_polymod, function_field_rational diff --git a/src/sage/rings/function_field/derivations.py b/src/sage/rings/function_field/derivations.py index d0c34397a46..9a6f0f613a9 100644 --- a/src/sage/rings/function_field/derivations.py +++ b/src/sage/rings/function_field/derivations.py @@ -4,10 +4,10 @@ For global function fields, which have positive characteristics, the higher derivation is available:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: h = L.higher_derivation() # optional - sage.rings.finite_rings sage.rings.function_field - sage: h(y^2, 2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: h = L.higher_derivation() # needs sage.rings.function_field + sage: h(y^2, 2) # needs sage.rings.function_field ((x^7 + 1)/x^2)*y^2 + x^3*y AUTHORS: diff --git a/src/sage/rings/function_field/derivations_polymod.py b/src/sage/rings/function_field/derivations_polymod.py index cc23d4c2461..2e0fe91a968 100644 --- a/src/sage/rings/function_field/derivations_polymod.py +++ b/src/sage/rings/function_field/derivations_polymod.py @@ -176,24 +176,24 @@ def __init__(self, parent, u=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: d = L.derivation() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() This also works for iterated non-monic extensions:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - 1/x) # optional - sage.rings.finite_rings - sage: R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^2*y - x^3) # optional - sage.rings.finite_rings - sage: M.derivation() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - 1/x) + sage: R. = L[] + sage: M. = L.extension(z^2*y - x^3) + sage: M.derivation() d/dz We can also create a multiple of the canonical derivation:: - sage: M.derivation([x]) # optional - sage.rings.finite_rings + sage: M.derivation([x]) x*d/dz """ FunctionFieldDerivation.__init__(self, parent) @@ -219,15 +219,15 @@ def _call_(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: d = L.derivation() # optional - sage.rings.finite_rings - sage: d(x) # indirect doctest # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() + sage: d(x) # indirect doctest 0 - sage: d(y) # optional - sage.rings.finite_rings + sage: d(y) 1 - sage: d(y^2) # optional - sage.rings.finite_rings + sage: d(y^2) 0 """ @@ -242,13 +242,13 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 - x) # optional - sage.rings.finite_rings - sage: d = L.derivation() # optional - sage.rings.finite_rings - sage: d # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: R. = K[] + sage: L. = K.extension(y^3 - x) + sage: d = L.derivation() + sage: d d/dy - sage: d + d # optional - sage.rings.finite_rings + sage: d + d 2*d/dy """ return type(self)(self.parent(), [self._u + other._u]) @@ -259,13 +259,13 @@ def _lmul_(self, factor): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: d = L.derivation() # optional - sage.rings.finite_rings - sage: d # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() + sage: d d/dy - sage: y * d # optional - sage.rings.finite_rings + sage: y * d y*d/dy """ return type(self)(self.parent(), [factor * self._u]) @@ -281,8 +281,8 @@ class FunctionFieldHigherDerivation(Map): EXAMPLES:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: F.higher_derivation() # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: F.higher_derivation() Higher derivation map: From: Rational function field in x over Finite Field of size 2 To: Rational function field in x over Finite Field of size 2 @@ -293,9 +293,9 @@ def __init__(self, field): TESTS:: - sage: F. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: TestSuite(h).run(skip='_test_category') # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(4)) # needs sage.rings.finite_rings + sage: h = F.higher_derivation() # needs sage.rings.finite_rings + sage: TestSuite(h).run(skip='_test_category') # needs sage.rings.finite_rings """ Map.__init__(self, Hom(field, field, Sets())) self._field = field @@ -311,9 +311,9 @@ def _repr_type(self) -> str: EXAMPLES:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: h # indirect doctest # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: h = F.higher_derivation() + sage: h # indirect doctest Higher derivation map: From: Rational function field in x over Finite Field of size 2 To: Rational function field in x over Finite Field of size 2 @@ -326,9 +326,9 @@ def __eq__(self, other) -> bool: TESTS:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: loads(dumps(h)) == h # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: h = F.higher_derivation() + sage: loads(dumps(h)) == h True """ if isinstance(other, FunctionFieldHigherDerivation): @@ -344,9 +344,9 @@ def _pth_root_in_prime_field(e): sage: from sage.rings.function_field.derivations_polymod import _pth_root_in_prime_field sage: p = 5 - sage: F. = GF(p) # optional - sage.rings.finite_rings - sage: e = F.random_element() # optional - sage.rings.finite_rings - sage: _pth_root_in_prime_field(e)^p == e # optional - sage.rings.finite_rings + sage: F. = GF(p) + sage: e = F.random_element() + sage: _pth_root_in_prime_field(e)^p == e True """ return e @@ -360,9 +360,9 @@ def _pth_root_in_finite_field(e): sage: from sage.rings.function_field.derivations_polymod import _pth_root_in_finite_field sage: p = 3 - sage: F. = GF(p^2) # optional - sage.rings.finite_rings - sage: e = F.random_element() # optional - sage.rings.finite_rings - sage: _pth_root_in_finite_field(e)^p == e # optional - sage.rings.finite_rings + sage: F. = GF(p^2) # needs sage.rings.finite_rings + sage: e = F.random_element() # needs sage.rings.finite_rings + sage: _pth_root_in_finite_field(e)^p == e # needs sage.rings.finite_rings True """ return e.pth_root() @@ -378,13 +378,13 @@ class RationalFunctionFieldHigherDerivation_global(FunctionFieldHigherDerivation EXAMPLES:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: h # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: h = F.higher_derivation() + sage: h Higher derivation map: From: Rational function field in x over Finite Field of size 2 To: Rational function field in x over Finite Field of size 2 - sage: h(x^2, 2) # optional - sage.rings.finite_rings + sage: h(x^2, 2) 1 """ def __init__(self, field): @@ -393,9 +393,9 @@ def __init__(self, field): TESTS:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: TestSuite(h).run(skip='_test_category') # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: h = F.higher_derivation() + sage: TestSuite(h).run(skip='_test_category') """ FunctionFieldHigherDerivation.__init__(self, field) @@ -408,9 +408,9 @@ def _call_with_args(self, f, args=(), kwds={}): EXAMPLES:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: h(x^2, 2) # indirect doctest # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: h = F.higher_derivation() + sage: h(x^2, 2) # indirect doctest 1 """ return self._derive(f, *args, **kwds) @@ -424,17 +424,17 @@ def _derive(self, f, i, separating_element=None): EXAMPLES:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: h._derive(x^3, 0) # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: h = F.higher_derivation() + sage: h._derive(x^3, 0) x^3 - sage: h._derive(x^3, 1) # optional - sage.rings.finite_rings + sage: h._derive(x^3, 1) x^2 - sage: h._derive(x^3, 2) # optional - sage.rings.finite_rings + sage: h._derive(x^3, 2) x - sage: h._derive(x^3, 3) # optional - sage.rings.finite_rings + sage: h._derive(x^3, 3) 1 - sage: h._derive(x^3, 4) # optional - sage.rings.finite_rings + sage: h._derive(x^3, 4) 0 """ F = self._field @@ -491,11 +491,11 @@ def _prime_power_representation(self, f, separating_element=None): EXAMPLES:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: h._prime_power_representation(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: h = F.higher_derivation() + sage: h._prime_power_representation(x^2 + x + 1) [x + 1, 1] - sage: x^2 + x + 1 == _[0]^2 + _[1]^2 * x # optional - sage.rings.finite_rings + sage: x^2 + x + 1 == _[0]^2 + _[1]^2 * x True """ F = self._field @@ -541,9 +541,9 @@ def _pth_root(self, c): EXAMPLES:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: h = F.higher_derivation() # optional - sage.rings.finite_rings - sage: h._pth_root((x^2+1)^2) # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: h = F.higher_derivation() + sage: h._pth_root((x^2+1)^2) x^2 + 1 """ K = self._field @@ -570,14 +570,14 @@ class FunctionFieldHigherDerivation_global(FunctionFieldHigherDerivation): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings - sage: h = L.higher_derivation() # optional - sage.rings.finite_rings - sage: h # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: h Higher derivation map: From: Function field in y defined by y^3 + x^3*y + x To: Function field in y defined by y^3 + x^3*y + x - sage: h(y^2, 2) # optional - sage.rings.finite_rings + sage: h(y^2, 2) ((x^7 + 1)/x^2)*y^2 + x^3*y """ @@ -587,10 +587,10 @@ def __init__(self, field): TESTS:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings - sage: h = L.higher_derivation() # optional - sage.rings.finite_rings - sage: TestSuite(h).run(skip=['_test_category']) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: TestSuite(h).run(skip=['_test_category']) """ from sage.matrix.constructor import matrix @@ -615,10 +615,10 @@ def _call_with_args(self, f, args, kwds): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings - sage: h = L.higher_derivation() # optional - sage.rings.finite_rings - sage: h(y^2, 2) # indirect doctest # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: h(y^2, 2) # indirect doctest ((x^7 + 1)/x^2)*y^2 + x^3*y """ return self._derive(f, *args, **kwds) @@ -632,20 +632,20 @@ def _derive(self, f, i, separating_element=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings - sage: h = L.higher_derivation() # optional - sage.rings.finite_rings - sage: y^3 # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: y^3 x^3*y + x - sage: h._derive(y^3, 0) # optional - sage.rings.finite_rings + sage: h._derive(y^3, 0) x^3*y + x - sage: h._derive(y^3, 1) # optional - sage.rings.finite_rings + sage: h._derive(y^3, 1) x^4*y^2 + 1 - sage: h._derive(y^3, 2) # optional - sage.rings.finite_rings + sage: h._derive(y^3, 2) x^10*y^2 + (x^8 + x)*y - sage: h._derive(y^3, 3) # optional - sage.rings.finite_rings + sage: h._derive(y^3, 3) (x^9 + x^2)*y^2 + x^7*y - sage: h._derive(y^3, 4) # optional - sage.rings.finite_rings + sage: h._derive(y^3, 4) (x^22 + x)*y^2 + ((x^21 + x^14 + x^7 + 1)/x)*y """ F = self._field @@ -731,11 +731,11 @@ def _prime_power_representation(self, f, separating_element=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings - sage: h = L.higher_derivation() # optional - sage.rings.finite_rings - sage: b = h._prime_power_representation(y) # optional - sage.rings.finite_rings - sage: y == b[0]^2 + b[1]^2 * x # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: b = h._prime_power_representation(y) + sage: y == b[0]^2 + b[1]^2 * x True """ F = self._field @@ -777,10 +777,10 @@ def _pth_root(self, c): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings - sage: h = L.higher_derivation() # optional - sage.rings.finite_rings - sage: h._pth_root((x^2 + y^2)^2) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: h._pth_root((x^2 + y^2)^2) y^2 + x^2 """ from sage.modules.free_module_element import vector diff --git a/src/sage/rings/function_field/differential.py b/src/sage/rings/function_field/differential.py index c3eaa240b7a..03b327b1abf 100644 --- a/src/sage/rings/function_field/differential.py +++ b/src/sage/rings/function_field/differential.py @@ -9,35 +9,38 @@ The module of differentials on a function field forms an one-dimensional vector space over the function field:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = x + y # optional - sage.rings.finite_rings sage.rings.function_field - sage: g = 1 / y # optional - sage.rings.finite_rings sage.rings.function_field - sage: df = f.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: dg = g.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: dfdg = f.derivative() / g.derivative() # optional - sage.rings.finite_rings sage.rings.function_field - sage: df == dfdg * dg # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: f = x + y + sage: g = 1 / y + sage: df = f.differential() + sage: dg = g.differential() + sage: dfdg = f.derivative() / g.derivative() + sage: df == dfdg * dg True - sage: df # optional - sage.rings.finite_rings sage.rings.function_field + sage: df (x*y^2 + 1/x*y + 1) d(x) - sage: df.parent() # optional - sage.rings.finite_rings sage.rings.function_field + sage: df.parent() Space of differentials of Function field in y defined by y^3 + x^3*y + x We can compute a canonical divisor:: - sage: k = df.divisor() # optional - sage.rings.finite_rings sage.rings.function_field - sage: k.degree() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: k = df.divisor() + sage: k.degree() 4 - sage: k.degree() == 2 * L.genus() - 2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: k.degree() == 2 * L.genus() - 2 True Exact differentials vanish and logarithmic differentials are stable under the Cartier operation:: - sage: df.cartier() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: df.cartier() 0 - sage: w = 1/f * df # optional - sage.rings.finite_rings sage.rings.function_field - sage: w.cartier() == w # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = 1/f * df + sage: w.cartier() == w True AUTHORS: @@ -90,10 +93,10 @@ class FunctionFieldDifferential(ModuleElement): :: sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.function_field - sage: L(x).differential() # optional - sage.rings.function_field + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: L(x).differential() # needs sage.rings.function_field d(x) - sage: y.differential() # optional - sage.rings.function_field + sage: y.differential() # needs sage.rings.function_field ((21/4*x/(x^7 + 27/4))*y^2 + ((3/2*x^7 + 9/4)/(x^8 + 27/4*x))*y + 7/2*x^4/(x^7 + 27/4)) d(x) """ def __init__(self, parent, f, t=None): @@ -102,10 +105,10 @@ def __init__(self, parent, f, t=None): TESTS:: - sage: F. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: f = x/(x^2 + x + 1) # optional - sage.rings.finite_rings - sage: w = f.differential() # optional - sage.rings.finite_rings - sage: TestSuite(w).run() # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(7)) + sage: f = x/(x^2 + x + 1) + sage: w = f.differential() + sage: TestSuite(w).run() """ ModuleElement.__init__(self, parent) @@ -120,9 +123,9 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3+x+x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3+x+x^3*Y) # needs sage.rings.finite_rings sage.rings.function_field + sage: y.differential() # needs sage.rings.finite_rings sage.rings.function_field (x*y^2 + 1/x*y) d(x) sage: F. = FunctionField(QQ) @@ -146,10 +149,11 @@ def _latex_(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings - sage: w = y.differential() # optional - sage.rings.finite_rings - sage: latex(w) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: w = y.differential() + sage: latex(w) \left( x y^{2} + \frac{1}{x} y \right)\, dx """ if self._f.is_zero(): # zero differential @@ -168,11 +172,11 @@ def __hash__(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: {x.differential(): 1} # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: {x.differential(): 1} {d(x): 1} - sage: {y.differential(): 1} # optional - sage.rings.finite_rings sage.rings.function_field + sage: {y.differential(): 1} # needs sage.rings.function_field {(x*y^2 + 1/x*y) d(x): 1} """ return hash((self.parent(), self._f)) @@ -190,16 +194,17 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w2 = L(x).differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w3 = (x*y).differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 < w2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: w1 = y.differential() + sage: w2 = L(x).differential() + sage: w3 = (x*y).differential() + sage: w1 < w2 False - sage: w2 < w1 # optional - sage.rings.finite_rings sage.rings.function_field + sage: w2 < w1 True - sage: w3 == x * w1 + y * w2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: w3 == x * w1 + y * w2 True sage: F. = FunctionField(QQ) @@ -224,11 +229,12 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w2 = (1/y).differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 + w2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: w1 = y.differential() + sage: w2 = (1/y).differential() + sage: w1 + w2 (((x^3 + 1)/x^2)*y^2 + 1/x*y) d(x) sage: F. = FunctionField(QQ) @@ -252,11 +258,12 @@ def _div_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w2 = (1/y).differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 / w2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: w1 = y.differential() + sage: w2 = (1/y).differential() + sage: w1 / w2 y^2 sage: F. = FunctionField(QQ) @@ -276,11 +283,12 @@ def _neg_(self): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w2 = (-y).differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: -w1 == w2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: w1 = y.differential() + sage: w2 = (-y).differential() + sage: -w1 == w2 True sage: F. = FunctionField(QQ) @@ -303,11 +311,12 @@ def _rmul_(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 = (1/y).differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w2 = (-1/y^2) * y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w1 == w2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: w1 = (1/y).differential() + sage: w2 = (-1/y^2) * y.differential() + sage: w1 == w2 True sage: F. = FunctionField(QQ) @@ -332,26 +341,28 @@ def _acted_upon_(self, f, self_on_left): EXAMPLES:: - sage: K. = FunctionField(GF(31)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x); _. = L[] # optional - sage.rings.finite_rings sage.rings.function_field - sage: M. = L.extension(Z^2 - y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: z.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(31)); _. = K[] + sage: L. = K.extension(Y^2 - x); _. = L[] + sage: M. = L.extension(Z^2 - y) + sage: z.differential() (8/x*z) d(x) - sage: 1/(2*z) * y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: 1/(2*z) * y.differential() (8/x*z) d(x) - sage: z * x.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: z * x.differential() (z) d(x) - sage: z * (y^2).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: z * (y^2).differential() (z) d(x) - sage: z * (z^4).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: z * (z^4).differential() (z) d(x) :: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: y * x.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.finite_rings sage.rings.function_field + sage: y * x.differential() # needs sage.rings.finite_rings sage.rings.function_field (y) d(x) """ F = f.parent() @@ -367,10 +378,10 @@ def divisor(self): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w = (1/y) * y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: w = (1/y) * y.differential() # needs sage.rings.function_field + sage: w.divisor() # needs sage.rings.function_field - Place (1/x, 1/x^3*y^2 + 1/x) - Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) - Place (x, y) @@ -381,7 +392,7 @@ def divisor(self): sage: F. = FunctionField(QQ) sage: w = (1/x).differential() - sage: w.divisor() + sage: w.divisor() # needs sage.libs.pari -2*Place (x) """ F = self.parent().function_field() @@ -398,10 +409,10 @@ def valuation(self, place): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w = (1/y) * y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: [w.valuation(p) for p in L.places()] # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: w = (1/y) * y.differential() # needs sage.rings.function_field + sage: [w.valuation(p) for p in L.places()] # needs sage.rings.function_field [-1, -1, -1, 0, 1, 0] """ F = self.parent().function_field() @@ -425,39 +436,42 @@ def residue(self, place): We verify the residue theorem in a rational function field:: - sage: F. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: f = 0 # optional - sage.rings.finite_rings - sage: while f == 0: # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = FunctionField(GF(4)) + sage: f = 0 + sage: while f == 0: ....: f = F.random_element() - sage: w = 1/f * f.differential() # optional - sage.rings.finite_rings - sage: d = f.divisor() # optional - sage.rings.finite_rings - sage: s = d.support() # optional - sage.rings.finite_rings - sage: sum([w.residue(p).trace() for p in s]) # optional - sage.rings.finite_rings + sage: w = 1/f * f.differential() + sage: d = f.divisor() + sage: s = d.support() + sage: sum([w.residue(p).trace() for p in s]) # needs sage.rings.function_field 0 and in an extension field:: - sage: K. = FunctionField(GF(7)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = 0 # optional - sage.rings.finite_rings sage.rings.function_field - sage: while f == 0: # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: f = 0 + sage: while f == 0: ....: f = L.random_element() - sage: w = 1/f * f.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: d = f.divisor() # optional - sage.rings.finite_rings sage.rings.function_field - sage: s = d.support() # optional - sage.rings.finite_rings sage.rings.function_field - sage: sum([w.residue(p).trace() for p in s]) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = 1/f * f.differential() + sage: d = f.divisor() + sage: s = d.support() + sage: sum([w.residue(p).trace() for p in s]) 0 and also in a function field of characteristic zero:: + sage: # needs sage.rings.function_field sage: R. = FunctionField(QQ) sage: L. = R[] - sage: F. = R.extension(Y^2 - x^4 - 4*x^3 - 2*x^2 - 1) # optional - sage.rings.function_field - sage: a = 6*x^2 + 5*x + 7 # optional - sage.rings.function_field - sage: b = 2*x^6 + 8*x^5 + 3*x^4 - 4*x^3 - 1 # optional - sage.rings.function_field - sage: w = y*a/b*x.differential() # optional - sage.rings.function_field - sage: d = w.divisor() # optional - sage.rings.function_field - sage: sum([QQ(w.residue(p)) for p in d.support()]) # optional - sage.rings.function_field + sage: F. = R.extension(Y^2 - x^4 - 4*x^3 - 2*x^2 - 1) + sage: a = 6*x^2 + 5*x + 7 + sage: b = 2*x^6 + 8*x^5 + 3*x^4 - 4*x^3 - 1 + sage: w = y*a/b*x.differential() + sage: d = w.divisor() + sage: sum([QQ(w.residue(p)) for p in d.support()]) 0 """ @@ -485,12 +499,12 @@ def monomial_coefficients(self, copy=True): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: d = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: d # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: d = y.differential() # needs sage.rings.function_field + sage: d # needs sage.rings.function_field ((4*x/(x^7 + 3))*y^2 + ((4*x^7 + 1)/(x^8 + 3*x))*y + x^4/(x^7 + 3)) d(x) - sage: d.monomial_coefficients() # optional - sage.rings.finite_rings sage.rings.function_field + sage: d.monomial_coefficients() # needs sage.rings.function_field {0: (4*x/(x^7 + 3))*y^2 + ((4*x^7 + 1)/(x^8 + 3*x))*y + x^4/(x^7 + 3)} """ return {0: self._f} @@ -502,16 +516,16 @@ class FunctionFieldDifferential_global(FunctionFieldDifferential): EXAMPLES:: - sage: F. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: f = x/(x^2 + x + 1) # optional - sage.rings.finite_rings - sage: f.differential() # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(7)) + sage: f = x/(x^2 + x + 1) + sage: f.differential() ((6*x^2 + 1)/(x^4 + 2*x^3 + 3*x^2 + 2*x + 1)) d(x) :: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.finite_rings sage.rings.function_field + sage: y.differential() # needs sage.rings.finite_rings sage.rings.function_field (x*y^2 + 1/x*y) d(x) """ def cartier(self): @@ -531,19 +545,21 @@ def cartier(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = x/y # optional - sage.rings.finite_rings sage.rings.function_field - sage: w = 1/f*f.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w.cartier() == w # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: f = x/y + sage: w = 1/f*f.differential() + sage: w.cartier() == w True :: - sage: F. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: f = x/(x^2 + x + 1) # optional - sage.rings.finite_rings - sage: w = 1/f*f.differential() # optional - sage.rings.finite_rings - sage: w.cartier() == w # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = FunctionField(GF(4)) + sage: f = x/(x^2 + x + 1) + sage: w = 1/f*f.differential() + sage: w.cartier() == w # needs sage.rings.function_field True """ W = self.parent() @@ -563,9 +579,9 @@ class DifferentialsSpace(UniqueRepresentation, Parent): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.finite_rings sage.rings.function_field + sage: L.space_of_differentials() # needs sage.rings.finite_rings sage.rings.function_field Space of differentials of Function field in y defined by y^3 + x^3*y + x The space of differentials is a one-dimensional module over the function @@ -575,14 +591,15 @@ class DifferentialsSpace(UniqueRepresentation, Parent): element is automatically found and used to generate the base differential relative to which other differentials are denoted:: - sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^5 - 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L(x).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(5)) + sage: R. = K[] + sage: L. = K.extension(y^5 - 1/x) + sage: L(x).differential() 0 - sage: y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: y.differential() d(y) - sage: (y^2).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: (y^2).differential() (2*y) d(y) """ Element = FunctionFieldDifferential @@ -593,10 +610,11 @@ def __init__(self, field, category=None): TESTS:: - sage: K. = FunctionField(GF(4)); _.=K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: W = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field - sage: TestSuite(W).run() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: W = L.space_of_differentials() + sage: TestSuite(W).run() """ Parent.__init__(self, base=field, category=Modules(field).FiniteDimensional().WithBasis().or_subcategory(category)) @@ -619,10 +637,11 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field - sage: w.parent() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: w = y.differential() + sage: w.parent() Space of differentials of Function field in y defined by y^3 + x^3*y + x """ return "Space of differentials of {}".format(self.base()) @@ -637,14 +656,15 @@ def _element_constructor_(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: S = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field - sage: S(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: S = L.space_of_differentials() + sage: S(y) (x*y^2 + 1/x*y) d(x) - sage: S(y) in S # optional - sage.rings.finite_rings sage.rings.function_field + sage: S(y) in S True - sage: S(1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: S(1) 0 """ if f in self.base(): @@ -662,8 +682,8 @@ def _coerce_map_from_(self, S): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: L.space_of_differentials().coerce_map_from(K.space_of_differentials()) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: L.space_of_differentials().coerce_map_from(K.space_of_differentials()) # needs sage.rings.function_field Inclusion morphism: From: Space of differentials of Rational function field in x over Rational Field To: Space of differentials of Function field in y defined by y^2 - x*y + 4*x^3 @@ -679,10 +699,11 @@ def function_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: S = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field - sage: S.function_field() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: S = L.space_of_differentials() + sage: S.function_field() Function field in y defined by y^3 + x^3*y + x """ return self.base() @@ -693,10 +714,11 @@ def _an_element_(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: S = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field - sage: S.an_element() # random # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: S = L.space_of_differentials() + sage: S.an_element() # random (x*y^2 + 1/x*y) d(x) """ F = self.base() @@ -708,10 +730,11 @@ def basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: S = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field - sage: S.basis() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: S = L.space_of_differentials() + sage: S.basis() Family (d(x),) """ return Family([self.element_class(self, self.base().one())]) @@ -727,9 +750,9 @@ class DifferentialsSpace_global(DifferentialsSpace): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.finite_rings sage.rings.function_field + sage: L.space_of_differentials() # needs sage.rings.finite_rings sage.rings.function_field Space of differentials of Function field in y defined by y^3 + x^3*y + x """ Element = FunctionFieldDifferential_global @@ -742,10 +765,10 @@ class DifferentialsSpaceInclusion(Morphism): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: OK = K.space_of_differentials() # optional - sage.rings.function_field - sage: OL = L.space_of_differentials() # optional - sage.rings.function_field - sage: OL.coerce_map_from(OK) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: OK = K.space_of_differentials() + sage: OL = L.space_of_differentials() # needs sage.rings.function_field + sage: OL.coerce_map_from(OK) # needs sage.rings.function_field Inclusion morphism: From: Space of differentials of Rational function field in x over Rational Field To: Space of differentials of Function field in y defined by y^2 - x*y + 4*x^3 @@ -758,10 +781,10 @@ def _repr_(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: OK = K.space_of_differentials() # optional - sage.rings.function_field - sage: OL = L.space_of_differentials() # optional - sage.rings.function_field - sage: OL.coerce_map_from(OK) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: OK = K.space_of_differentials() + sage: OL = L.space_of_differentials() # needs sage.rings.function_field + sage: OL.coerce_map_from(OK) # needs sage.rings.function_field Inclusion morphism: From: Space of differentials of Rational function field in x over Rational Field To: Space of differentials of Function field in y defined by y^2 - x*y + 4*x^3 @@ -778,10 +801,10 @@ def is_injective(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: OK = K.space_of_differentials() # optional - sage.rings.function_field - sage: OL = L.space_of_differentials() # optional - sage.rings.function_field - sage: OL.coerce_map_from(OK).is_injective() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: OK = K.space_of_differentials() + sage: OL = L.space_of_differentials() # needs sage.rings.function_field + sage: OL.coerce_map_from(OK).is_injective() # needs sage.rings.function_field True """ return True @@ -792,16 +815,17 @@ def is_surjective(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: OK = K.space_of_differentials() # optional - sage.rings.function_field - sage: OL = L.space_of_differentials() # optional - sage.rings.function_field - sage: OL.coerce_map_from(OK).is_surjective() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: OK = K.space_of_differentials() + sage: OL = L.space_of_differentials() + sage: OL.coerce_map_from(OK).is_surjective() False - sage: S. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z - 1) # optional - sage.rings.function_field - sage: OM = M.space_of_differentials() # optional - sage.rings.function_field - sage: OM.coerce_map_from(OL).is_surjective() # optional - sage.rings.function_field + sage: S. = L[] + sage: M. = L.extension(z - 1) + sage: OM = M.space_of_differentials() + sage: OM.coerce_map_from(OL).is_surjective() True """ K = self.domain().function_field() @@ -818,12 +842,13 @@ def _call_(self, v): EXAMPLES:: - sage: K. = FunctionField(QQbar); _. = K[] # optional - sage.rings.number_field - sage: L. = K.extension(Y^2 - x*Y + 4*x^3) # optional - sage.rings.function_field sage.rings.number_field - sage: OK = K.space_of_differentials() # optional - sage.rings.function_field sage.rings.number_field - sage: OL = L.space_of_differentials() # optional - sage.rings.function_field sage.rings.number_field - sage: mor = OL.coerce_map_from(OK) # optional - sage.rings.function_field sage.rings.number_field - sage: mor(x.differential()).parent() # optional - sage.rings.function_field sage.rings.number_field + sage: # needs sage.rings.function_field sage.rings.number_field + sage: K. = FunctionField(QQbar); _. = K[] + sage: L. = K.extension(Y^2 - x*Y + 4*x^3) + sage: OK = K.space_of_differentials() + sage: OL = L.space_of_differentials() + sage: mor = OL.coerce_map_from(OK) + sage: mor(x.differential()).parent() Space of differentials of Function field in y defined by y^2 - x*y + 4*x^3 """ domain = self.domain() diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index ed827c3ab09..ed9994f25ac 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -11,8 +11,9 @@ AUTHORS: -- Antoine Leudière (2022-04) -- Xavier Caruso (2022-06) +- Antoine Leudière (2022-04): initial version +- Xavier Caruso (2022-06): initial version +- David Ayotte (2023-03): added basic `j`-invariants """ # ***************************************************************************** @@ -25,23 +26,26 @@ # http://www.gnu.org/licenses/ # ***************************************************************************** +from sage.arith.misc import gcd from sage.categories.drinfeld_modules import DrinfeldModules from sage.categories.homset import Hom +from sage.geometry.polyhedron.constructor import Polyhedron from sage.misc.cachefunc import cached_method from sage.misc.latex import latex from sage.misc.latex import latex_variable_name from sage.misc.lazy_import import lazy_import from sage.misc.lazy_string import _LazyString +from sage.misc.misc_c import prod from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.polynomial_ring import PolynomialRing_general -from sage.rings.ring_extension import RingExtension_generic from sage.structure.parent import Parent from sage.structure.sage_object import SageObject from sage.structure.sequence import Sequence from sage.structure.unique_representation import UniqueRepresentation +lazy_import('sage.rings.ring_extension', 'RingExtension_generic') lazy_import('sage.rings.lazy_series_ring', 'LazyPowerSeriesRing') @@ -172,7 +176,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: rho(T) == rho_T True - Images under the Drinfeld module are computed by calling the object:: + Images under the Drinfeld module are computed by calling the + object:: sage: phi(T) # phi_T, the generator of the Drinfeld module t^2 + t + z @@ -204,7 +209,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: The base field of a Drinfeld module - The base field of the Drinfeld module is retrieved using :meth:`base`:: + The base field of the Drinfeld module is retrieved using + :meth:`base`:: sage: phi.base() Finite Field in z of size 3^12 over its base @@ -217,8 +223,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 over its base Defn: T |--> z - Note that the base field is *not* the field `K`. Rather, it is a ring - extension (see :class:`sage.rings.ring_extension.RingExtension`) whose + Note that the base field is *not* the field `K`. Rather, it is a + ring extension + (see :class:`sage.rings.ring_extension.RingExtension`) whose underlying ring is `K` and whose base is the base morphism:: sage: phi.base() is K @@ -274,7 +281,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.height() 1 - As well as the j-invariant if the rank is two:: + As well as the j-invariant:: sage: phi.j_invariant() # j-invariant 1 @@ -899,6 +906,239 @@ def action(self): from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction return DrinfeldModuleAction(self) + def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): + r""" + Return the list of basic `j`-invariant parameters. + + See the method :meth:`j_invariant` for definitions. + + INPUT: + + - ``coeff_indices`` (list or tuple, or NoneType; default: + ``None``) -- indices of the Drinfeld module generator + coefficients to be considered in the computation. If the + parameter is ``None`` (default), all the coefficients are + involved. + + - ``nonzero`` (boolean, default: ``False``) -- if this flag + is set to ``True``, then only the parameters for which the + corresponding basic `j`-invariant is nonzero are returned. + + .. WARNING:: + + The usage of this method can be computationally + expensive e.g. if the rank is greater than four, + or if `q` is large. Setting the ``nonzero`` flag to ``True`` + can speed up the computation considerably if the Drinfeld + module generator possesses multiple zero coefficients. + + EXAMPLES:: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) + sage: phi.basic_j_invariant_parameters() + [((1,), (31, 1)), + ((1, 2), (1, 5, 1)), + ((1, 2), (7, 4, 1)), + ((1, 2), (8, 9, 2)), + ((1, 2), (9, 14, 3)), + ((1, 2), (10, 19, 4)), + ((1, 2), (11, 24, 5)), + ((1, 2), (12, 29, 6)), + ((1, 2), (13, 3, 1)), + ((1, 2), (15, 13, 3)), + ((1, 2), (17, 23, 5)), + ((1, 2), (19, 2, 1)), + ((1, 2), (20, 7, 2)), + ((1, 2), (22, 17, 4)), + ((1, 2), (23, 22, 5)), + ((1, 2), (25, 1, 1)), + ((1, 2), (27, 11, 3)), + ((1, 2), (29, 21, 5)), + ((1, 2), (31, 31, 7)), + ((2,), (31, 6))] + + Use the ``nonzero=True`` flag to display only the parameters + whose `j`-invariant value is nonzero:: + + sage: phi.basic_j_invariant_parameters(nonzero=True) + [((2,), (31, 6))] + + + One can specify the list of coefficients indices to be + considered in the computation:: + + sage: A = GF(2)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T, 1, T]) + sage: phi.basic_j_invariant_parameters([1, 2]) + [((1,), (7, 1)), + ((1, 2), (1, 2, 1)), + ((1, 2), (4, 1, 1)), + ((1, 2), (5, 3, 2)), + ((1, 2), (6, 5, 3)), + ((1, 2), (7, 7, 4)), + ((2,), (7, 3))] + + TESTS:: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) + sage: phi.basic_j_invariant_parameters([1, 'x']) + Traceback (most recent call last): + ... + TypeError: coefficients indices must be integers + + :: + + sage: phi.basic_j_invariant_parameters([1, 10]) + Traceback (most recent call last): + ... + ValueError: indices must be > 0 and < 3 + + :: + + sage: phi.basic_j_invariant_parameters([1, 1]) + Traceback (most recent call last): + ... + ValueError: indices must be distinct and sorted + + :: + + sage: phi.basic_j_invariant_parameters([2, 1]) + Traceback (most recent call last): + ... + ValueError: indices must be distinct and sorted + + :: + + sage: phi.basic_j_invariant_parameters('x') + Traceback (most recent call last): + ... + TypeError: indices must be None, a tuple or a list + """ + r = self._gen.degree() + if coeff_indices is None: + if nonzero: + coeff_indices = [k for k, g in enumerate( + self.coefficients(sparse=False)[1:-1], start=1) if g] + else: + coeff_indices = list(range(1, r)) + # Check if coeff_indices is valid: + elif isinstance(coeff_indices, (tuple, list)): + coeff_indices = list(coeff_indices) + if not all(isinstance(k, (int, Integer)) for k in coeff_indices): + raise TypeError('coefficients indices must be integers') + if max(coeff_indices) >= r or min(coeff_indices) <= 0: + raise ValueError(f'indices must be > 0 and < {r}') + if not all(coeff_indices[i] < coeff_indices[i+1] for i in + range(len(coeff_indices) - 1)): + raise ValueError('indices must be distinct and sorted') + if nonzero: + coeff_indices = [k for k in coeff_indices if self._gen[k]] + else: + raise TypeError('indices must be None, a tuple or a list') + # Create the equation and inequalities for the polyhedron: + q = self._Fq.order() + equation = [0] + inequalities = [] + # Create the equation: + # d_1 (q - 1) + ... + d_{r-1} (q^{r-1} - 1) + # = d_r (q^r - 1) + for idx, i in enumerate(coeff_indices): + equation.append(q**i - 1) + # Create inequalities of the form 0 <= delta_i + lower_bounds = [0] * (len(coeff_indices) + 2) + lower_bounds[idx + 1] = 1 + # Create inequalities of the form + # delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) + upper_bounds = [Integer((q**r - 1) / (q**(gcd(i, r)) - 1))]\ + + [0]*(len(coeff_indices) + 1) + upper_bounds[idx + 1] = -1 + inequalities.extend((lower_bounds, upper_bounds)) + equation.append(1 - q**r) + # Create the polyhedron defined by the equation and the + # inequalities. + polyhedron = Polyhedron(ieqs=inequalities, eqns=[equation]) + # Compute its integral points + integral_points = polyhedron.integral_points() + # Format the result + parameters = [] + for p in integral_points: + if gcd(p) != 1: + continue + ks = list(coeff_indices) + ds = p.list() + i = 0 + while i < len(ks): + if ds[i] == 0: + del ds[i] + del ks[i] + else: + i += 1 + parameters.append((tuple(ks), tuple(ds))) + parameters.sort() + return parameters + + def basic_j_invariants(self, nonzero=False): + r""" + Return a dictionary whose keys are all the basic `j`-invariants + parameters and values are the corresponding `j`-invariant. + + See the method :meth:`j_invariant` for definitions. + + INPUT: + + - ``nonzero`` (boolean, default: ``False``) -- if this flag + is set to ``True``, then only the parameters for which the + corresponding basic `j`-invariant is nonzero are returned. + + .. WARNING:: + + The usage of this method can be computationally + expensive e.g. if the rank is greater than four, + or if `q` is large. Setting the ``nonzero`` flag to ``True`` + can speed up the computation considerably if the Drinfeld + module generator possesses multiple zero coefficients. + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.basic_j_invariants() + {((1,), (26, 1)): z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2} + + :: + + sage: phi = DrinfeldModule(A, [p_root, 0, 1, z12]) + sage: phi.basic_j_invariants(nonzero=True) + {((2,), (651, 26)): z12^11 + 3*z12^10 + 4*z12^9 + 3*z12^8 + z12^7 + 2*z12^6 + 3*z12^4 + 2*z12^3 + z12^2 + 4*z12} + + :: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T + 2, T+1, 1]) + sage: J_phi = phi.basic_j_invariants(); J_phi + {((1,), (31, 1)): T^31 + 2*T^30 + 2*T^26 + 4*T^25 + 2*T^6 + 4*T^5 + 4*T + 3, + ((1, 2), (1, 5, 1)): T^6 + 2*T^5 + T + 2, + ((1, 2), (7, 4, 1)): T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3, + ((1, 2), (8, 9, 2)): T^17 + 2*T^15 + T^14 + 4*T^13 + 4*T^11 + 4*T^10 + 3*T^9 + 2*T^8 + 3*T^7 + 2*T^6 + 3*T^5 + 2*T^4 + 3*T^3 + 4*T^2 + 3*T + 1, + ((1, 2), (9, 14, 3)): T^23 + 2*T^22 + 2*T^21 + T^19 + 4*T^18 + T^17 + 4*T^16 + T^15 + 4*T^14 + 2*T^12 + 4*T^11 + 4*T^10 + 2*T^8 + 4*T^7 + 4*T^6 + 2*T^4 + T^2 + 2*T + 2, + ((1, 2), (10, 19, 4)): T^29 + 4*T^28 + T^27 + 4*T^26 + T^25 + 2*T^24 + 3*T^23 + 2*T^22 + 3*T^21 + 2*T^20 + 4*T^19 + T^18 + 4*T^17 + T^16 + 4*T^15 + T^9 + 4*T^8 + T^7 + 4*T^6 + T^5 + 4*T^4 + T^3 + 4*T^2 + T + 4, + ... + ((2,), (31, 6)): T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1} + sage: J_phi[((1, 2), (7, 4, 1))] + T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3 + """ + return {parameter: self.j_invariant(parameter, check=False) + for parameter in self.basic_j_invariant_parameters(nonzero=nonzero)} + def coefficient(self, n): r""" Return the `n`-th coefficient of the generator. @@ -1364,17 +1604,70 @@ def is_finite(self) -> bool: from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return isinstance(self, FiniteDrinfeldModule) - def j_invariant(self): + def j_invariant(self, parameter=None, check=True): r""" - Return the j-invariant of the Drinfeld module if the rank is - two; raise a NotImplementedError otherwise. + Return the `j`-invariant of the Drinfeld + `\mathbb{F}_q[T]`-module for the given parameter. - Assume the rank is two. Write the generator `\phi_T = \omega + - g\tau + \Delta\tau^2`. The j-invariant is defined by - `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base field - of the function ring. In our case, this field is always finite. + Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r` with + `g_r \neq 0`. Then the + `((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r))`-`j`-*invariant* + of `\phi` is defined by - OUTPUT: an element in the base codomain + .. MATH:: + + j_{k_1, \ldots, k_n}^{d_1, \ldots, d_n, d_r}(\phi) + := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} + + where `1\leqslant k_1 < k_2 < \ldots < k_n \leqslant r - 1` and + the integers `d_i` satisfy the *weight-0 condition*: + + .. MATH:: + + d_1 (q^{k_1} - 1) + d_2 (q^{k_2} - 1) + + \cdots + d_{n} (q^{k_n} - 1) = d_r (q^r - 1). + + Furthermore, if `\gcd(d_1,\ldots, d_n, d_r) = 1` and + + .. MATH:: + + 0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), + \quad 1 \leq i \leq n, + + then the `j`-invariant is called *basic*. See the method + :meth:`basic_j_invariant_parameters` for computing the list of + all basic `j`-invariant parameters. + + INPUT: + + - ``parameter`` (tuple or list, integer or NoneType; default: + ``None``) -- the `j`-invariant parameter: + + - If ``parameter`` is a list or a tuple, then it must be of + the form: + `((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))`, + where the `k_i` and `d_i` are integers satisfying the + weight-0 condition described above. + + - If ``parameter`` is an integer `k` then the method returns + the ``j``-invariant associated to the parameter + `((k,), (d_k, d_r))`; + + - If ``parameter`` is ``None`` and the rank of the Drinfeld + module is 2, then the method returns its usual + `j`-invariant, that is the `j`-invariant for the parameter + `((1,), (q+1, 1))`. + + - ``check`` (bool, default: ``True``) -- if this flag is set to + ``False`` then the code will not check if the given parameter + is valid and satisfy the weight-0 condition. + + OUTPUT: the `j`-invariant of ``self`` for the given parameter. + + REFERENCE: + + The notion of basic `j`-invariant was introduced by Potemine in + [Pot1998]_. EXAMPLES:: @@ -1392,19 +1685,209 @@ def j_invariant(self): sage: rho.j_invariant() 0 - The rank must be two:: + :: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T^2, 1, T + 1, T^3]) + sage: phi.j_invariant(1) + T^309 + sage: phi.j_invariant(2) + 1/T^3 + sage: phi.j_invariant(3) + (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93 + + The parameter can either be a tuple or a list:: - sage: sigma = DrinfeldModule(A, [p_root, 1, 0]) - sage: sigma.j_invariant() + sage: Fq. = GF(7) + sage: A. = Fq[] + sage: phi = DrinfeldModule(A, [a, a^2 + a, 0, 3*a, a^2+1]) + sage: J = phi.j_invariant(((1, 3), (267, 269, 39))); J + 5 + sage: J == (phi.coefficient(1)**267)*(phi.coefficient(3)**269)/(phi.coefficient(4)**39) + True + sage: phi.j_invariant([[3], [400, 57]]) + 4 + sage: phi.j_invariant([[3], [400, 57]]) == phi.j_invariant(3) + True + + The list of all basic `j`-invariant parameters can be retrieved + using the method :meth:`basic_j_invariant_parameters`:: + + sage: A = GF(3)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T^2 + T + 1, 0, T^4 + 1, T - 1]) + sage: param = phi.basic_j_invariant_parameters(nonzero=True) + sage: phi.j_invariant(param[1]) + T^13 + 2*T^12 + T + 2 + sage: phi.j_invariant(param[2]) + T^35 + 2*T^31 + T^27 + 2*T^8 + T^4 + 2 + + TESTS:: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T^2, 1, T + 1, T^3]) + sage: phi.j_invariant() Traceback (most recent call last): ... - NotImplementedError: rank must be 2 + TypeError: parameter must not be None if the rank is greater than 2 + + :: + + sage: phi.j_invariant(-1) + Traceback (most recent call last): + ... + ValueError: integer parameter must be >= 1 and < the rank (=4) + + :: + + sage: phi.j_invariant('x') + Traceback (most recent call last): + ... + TypeError: parameter must be a tuple or a list of length 2 or an integer + + :: + + sage: phi.j_invariant((1, 2, 3)) + Traceback (most recent call last): + ... + ValueError: list or tuple parameter must be of length 2 + + :: + + sage: phi.j_invariant(('x', (1, 2, 3))) + Traceback (most recent call last): + ... + TypeError: list or tuple parameter must contain tuples or lists + + :: + + sage: phi.j_invariant(((1, 2), 'x')) + Traceback (most recent call last): + ... + TypeError: list or tuple parameter must contain tuples or lists + + :: + + sage: phi.j_invariant(((1, 2, 3, 4, 5), (2, 1))) + Traceback (most recent call last): + ... + ValueError: components of tuple or list parameter have incorrect length + + :: + + sage: phi.j_invariant(((1, 'x'), (2, 3, 8))) + Traceback (most recent call last): + ... + TypeError: components of tuple or list parameter must contain only integers + + :: + + sage: phi.j_invariant(((1, 2), (2, 3, 'x'))) + Traceback (most recent call last): + ... + TypeError: components of tuple or list parameter must contain only integers + + :: + + sage: phi.j_invariant(((1, 2), (4, 3, 7))) + Traceback (most recent call last): + ... + ValueError: parameter does not satisfy the weight-0 condition + + :: + + sage: phi.j_invariant(((1, 2), (4, 3, 7)), check=False) + 1/T^13 """ - self._check_rank_two() - g = self.coefficient(1) - delta = self.coefficient(2) + r = self._gen.degree() q = self._Fq.order() - return (g**(q+1)) / delta + if parameter is None: + if r != 2: + raise TypeError("parameter must not be None " + "if the rank is greater than 2") + return self._gen[1]**(q+1)/self._gen[2] + if parameter in ZZ: + parameter = ZZ(parameter) + if parameter <= 0 or parameter >= r: + raise ValueError("integer parameter must be >= 1 and < the " + f"rank (={r})") + dk = Integer((q**r - 1)/(q**gcd(parameter, r) - 1)) + dr = Integer((q**parameter - 1)/(q**gcd(parameter, r) - 1)) + return self._gen[parameter]**dk / self._gen[-1]**dr + elif isinstance(parameter, (tuple, list)): + if len(parameter) != 2: + raise ValueError("list or tuple parameter must be of length 2") + if not isinstance(parameter[0], (tuple, list)) \ + or not isinstance(parameter[1], (tuple, list)): + raise TypeError("list or tuple parameter must contain tuples " + "or lists") + if not len(parameter[0]) < r or\ + not len(parameter[1]) == len(parameter[0]) + 1: + raise ValueError("components of tuple or list parameter have " + "incorrect length") + try: # Check parameter's type + parameter_0 = [ZZ(p) for p in parameter[0]] + parameter_1 = [ZZ(p) for p in parameter[1]] + except TypeError: + raise TypeError("components of tuple or list parameter must " + "contain only integers") + # Check that the weight-0 condition is satisfied: + # d_1 (q - 1) + ... + d_{r-1} (q^{r-1} - 1) + # = d_r (q^r - 1) + if check: + right = parameter_1[-1]*(q**r - 1) + left = sum(parameter_1[i]*(q**(parameter_0[i]) - 1) for i in + range(len(parameter_0))) + if left != right: + raise ValueError("parameter does not satisfy the " + "weight-0 condition") + else: + raise TypeError("parameter must be a tuple or a list of " + "length 2 or an integer") + num = prod(self._gen[k]**d + for k, d in zip(parameter_0, parameter_1[:-1])) + return num / (self._gen[-1]**parameter_1[-1]) + + def jk_invariants(self): + r""" + Return a dictionary whose keys are all the integers + `1 \leqslant k \leqslant r-1` and the values are the + corresponding `j_k`-invariants + + Recall that the `j_k`-invariant of self is defined by: + + .. MATH:: + + j_k := \frac{g_k^{(q^r - 1)/(\mathrm{gcd}(k, r) - 1)}}{g_r^{(q^k - 1)/(\mathrm{gcd}(k, r) - 1)}} + + where `g_i` is the `i`-th coefficient of the generator of self. + + EXAMPLES:: + + sage: A = GF(3)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, 1, T+1, T^3, T^6]) + sage: jk_inv = phi.jk_invariants(); jk_inv + {1: 1/T^6, 2: (T^10 + T^9 + T + 1)/T^6, 3: T^42} + sage: jk_inv[2] + (T^10 + T^9 + T + 1)/T^6 + + :: + + sage: F = GF(7**2) + sage: A = F['T'] + sage: E. = F.extension(4) + sage: phi = DrinfeldModule(A, [z^2, 1, z+1, z^2, z, z+1]) + sage: phi.jk_invariants() + {1: 5*z^7 + 2*z^6 + 5*z^5 + 2*z^4 + 5*z^3 + z^2 + z + 2, + 2: 3*z^7 + 4*z^6 + 5*z^5 + 6*z^4 + 4*z, + 3: 5*z^7 + 6*z^6 + 6*z^5 + 4*z^3 + z^2 + 2*z + 1, + 4: 3*z^6 + 2*z^5 + 4*z^4 + 2*z^3 + 4*z^2 + 6*z + 2} + """ + r = self._gen.degree() # rank of self + return {k: self.j_invariant(k) for k in range(1, r)} @cached_method def _compute_coefficient_log(self, k): diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index b144cd20d22..9a82b611d18 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -20,16 +20,16 @@ Arithmetic with rational functions:: Derivatives of elements in separable extensions:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: (y^3 + x).derivative() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings sage.rings.function_field + sage: (y^3 + x).derivative() # needs sage.rings.finite_rings sage.rings.function_field ((x^2 + 1)/x^2)*y + (x^4 + x^3 + 1)/x^3 The divisor of an element of a global function field:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: y.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: y.divisor() # needs sage.rings.function_field - Place (1/x, 1/x*y) - Place (x, x*y) + 2*Place (x + 1, x*y) @@ -141,8 +141,8 @@ cdef class FunctionFieldElement(FieldElement): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(b^2 - a) # optional - sage.rings.function_field - sage: b.__pari__() # optional - sage.rings.function_field + sage: L. = K.extension(b^2 - a) # needs sage.rings.function_field + sage: b.__pari__() # needs sage.rings.function_field Traceback (most recent call last): ... NotImplementedError: PARI does not support general function field elements. @@ -180,55 +180,58 @@ cdef class FunctionFieldElement(FieldElement): A rational function field:: sage: K. = FunctionField(QQ) - sage: t.matrix() # optional - sage.modules + sage: t.matrix() # needs sage.modules [t] - sage: (1/(t+1)).matrix() # optional - sage.modules + sage: (1/(t+1)).matrix() # needs sage.modules [1/(t + 1)] Now an example in a nontrivial extension of a rational function field:: + sage: # needs sage.modules sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: y.matrix() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: y.matrix() [ 0 1] [-4*x^3 x] - sage: y.matrix().charpoly('Z') # optional - sage.modules sage.rings.function_field + sage: y.matrix().charpoly('Z') Z^2 - x*Z + 4*x^3 An example in a relative extension, where neither function field is rational:: + sage: # needs sage.modules sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: M. = L[] # optional - sage.rings.function_field - sage: Z. = L.extension(T^3 - y^2*T + x) # optional - sage.rings.function_field - sage: alpha.matrix() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: M. = L[] + sage: Z. = L.extension(T^3 - y^2*T + x) + sage: alpha.matrix() [ 0 1 0] [ 0 0 1] [ -x x*y - 4*x^3 0] - sage: alpha.matrix(K) # optional - sage.modules sage.rings.function_field + sage: alpha.matrix(K) [ 0 0 1 0 0 0] [ 0 0 0 1 0 0] [ 0 0 0 0 1 0] [ 0 0 0 0 0 1] [ -x 0 -4*x^3 x 0 0] [ 0 -x -4*x^4 -4*x^3 + x^2 0 0] - sage: alpha.matrix(Z) # optional - sage.modules sage.rings.function_field + sage: alpha.matrix(Z) [alpha] We show that this matrix does indeed work as expected when making a vector space from a function field:: + sage: # needs sage.modules sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # optional - sage.rings.function_field - sage: V, from_V, to_V = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: y5 = to_V(y^5); y5 # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: V, from_V, to_V = L.vector_space() + sage: y5 = to_V(y^5); y5 ((x^4 + 1)/x, 2*x, 0, 0, 0) - sage: y4y = to_V(y^4) * y.matrix(); y4y # optional - sage.modules sage.rings.function_field + sage: y4y = to_V(y^4) * y.matrix(); y4y ((x^4 + 1)/x, 2*x, 0, 0, 0) - sage: y5 == y4y # optional - sage.modules sage.rings.function_field + sage: y5 == y4y True """ # multiply each element of the vector space isomorphic to the parent @@ -250,8 +253,8 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: y.trace() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: y.trace() # needs sage.modules sage.rings.function_field x """ return self.matrix().trace() @@ -263,18 +266,18 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: y.norm() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: y.norm() # needs sage.modules sage.rings.function_field 4*x^3 The norm is relative:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z^3 - y^2*z + x) # optional - sage.rings.function_field - sage: z.norm() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # needs sage.rings.function_field + sage: M. = L.extension(z^3 - y^2*z + x) # needs sage.rings.function_field + sage: z.norm() # needs sage.modules sage.rings.function_field -x - sage: z.norm().parent() # optional - sage.modules sage.rings.function_field + sage: z.norm().parent() # needs sage.modules sage.rings.function_field Function field in y defined by y^2 - x*y + 4*x^3 """ return self.matrix().determinant() @@ -323,13 +326,13 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z^3 - y^2*z + x) # optional - sage.rings.function_field - sage: x.characteristic_polynomial('W') # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # needs sage.rings.function_field + sage: M. = L.extension(z^3 - y^2*z + x) # needs sage.rings.function_field + sage: x.characteristic_polynomial('W') # needs sage.modules W - x - sage: y.characteristic_polynomial('W') # optional - sage.modules sage.rings.function_field + sage: y.characteristic_polynomial('W') # needs sage.modules sage.rings.function_field W^2 - x*W + 4*x^3 - sage: z.characteristic_polynomial('W') # optional - sage.modules sage.rings.function_field + sage: z.characteristic_polynomial('W') # needs sage.modules sage.rings.function_field W^3 + (-x*y + 4*x^3)*W + x """ return self.matrix().characteristic_polynomial(*args, **kwds) @@ -344,13 +347,13 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z^3 - y^2*z + x) # optional - sage.rings.function_field - sage: x.minimal_polynomial('W') # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # needs sage.rings.function_field + sage: M. = L.extension(z^3 - y^2*z + x) # needs sage.rings.function_field + sage: x.minimal_polynomial('W') # needs sage.modules W - x - sage: y.minimal_polynomial('W') # optional - sage.modules sage.rings.function_field + sage: y.minimal_polynomial('W') # needs sage.modules sage.rings.function_field W^2 - x*W + 4*x^3 - sage: z.minimal_polynomial('W') # optional - sage.modules sage.rings.function_field + sage: z.minimal_polynomial('W') # needs sage.modules sage.rings.function_field W^3 + (-x*y + 4*x^3)*W + x """ return self.matrix().minimal_polynomial(*args, **kwds) @@ -363,17 +366,18 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: + sage: # needs sage.modules sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: y.is_integral() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: y.is_integral() True - sage: (y/x).is_integral() # optional - sage.modules sage.rings.function_field + sage: (y/x).is_integral() True - sage: (y/x)^2 - (y/x) + 4*x # optional - sage.modules sage.rings.function_field + sage: (y/x)^2 - (y/x) + 4*x 0 - sage: (y/x^2).is_integral() # optional - sage.modules sage.rings.function_field + sage: (y/x^2).is_integral() False - sage: (y/x).minimal_polynomial('W') # optional - sage.modules sage.rings.function_field + sage: (y/x).minimal_polynomial('W') W^2 - W + 4*x """ R = self.parent().base_field().maximal_order() @@ -387,29 +391,29 @@ cdef class FunctionFieldElement(FieldElement): sage: K. = FunctionField(QQ) sage: f = 1 / t - sage: f.differential() # optional - sage.modules + sage: f.differential() # needs sage.modules (-1/t^2) d(t) - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x +1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: (y^3 + x).differential() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x +1/x) # needs sage.rings.finite_rings sage.rings.function_field + sage: (y^3 + x).differential() # needs sage.modules sage.rings.finite_rings sage.rings.function_field (((x^2 + 1)/x^2)*y + (x^4 + x^3 + 1)/x^3) d(x) TESTS: Verify that :trac:`27712` is resolved:: - sage: K. = FunctionField(GF(31)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: R. = L[] # optional - sage.rings.finite_rings sage.rings.function_field - sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(31)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: R. = L[] # needs sage.rings.function_field + sage: M. = L.extension(z^2 - y) # needs sage.rings.function_field - sage: x.differential() # optional - sage.rings.finite_rings sage.modules + sage: x.differential() # needs sage.modules d(x) - sage: y.differential() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: y.differential() # needs sage.modules sage.rings.function_field (16/x*y) d(x) - sage: z.differential() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: z.differential() # needs sage.modules sage.rings.function_field (8/x*z) d(x) """ F = self.parent() @@ -427,12 +431,12 @@ cdef class FunctionFieldElement(FieldElement): sage: K. = FunctionField(QQ) sage: f = (t + 1) / (t^2 - 1/3) - sage: f.derivative() # optional - sage.modules + sage: f.derivative() # needs sage.modules (-t^2 - 2*t - 1/3)/(t^4 - 2/3*t^2 + 1/9) - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: (y^3 + x).derivative() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings sage.rings.function_field + sage: (y^3 + x).derivative() # needs sage.modules sage.rings.finite_rings sage.rings.function_field ((x^2 + 1)/x^2)*y + (x^4 + x^3 + 1)/x^3 """ D = self.parent().derivation() @@ -452,16 +456,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: f = t^2 # optional - sage.rings.finite_rings - sage: f.higher_derivative(2) # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)) + sage: f = t^2 + sage: f.higher_derivative(2) # needs sage.modules sage.rings.function_field 1 :: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: (y^3 + x).higher_derivative(2) # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings sage.rings.function_field + sage: (y^3 + x).higher_derivative(2) # needs sage.modules sage.rings.finite_rings sage.rings.function_field 1/x^3*y + (x^6 + x^4 + x^3 + x^2 + x + 1)/x^5 """ D = self.parent().higher_derivation() @@ -474,18 +478,18 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings - sage: f.divisor() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)) + sage: f = 1/(x^3 + x^2 + x) + sage: f.divisor() # needs sage.libs.pari sage.modules 3*Place (1/x) - Place (x) - Place (x^2 + x + 1) :: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: y.divisor() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: y.divisor() # needs sage.modules sage.rings.function_field - Place (1/x, 1/x*y) - Place (x, x*y) + 2*Place (x + 1, x*y) @@ -504,16 +508,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings - sage: f.divisor_of_zeros() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)) + sage: f = 1/(x^3 + x^2 + x) + sage: f.divisor_of_zeros() # needs sage.libs.pari sage.modules 3*Place (1/x) :: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: (x/y).divisor_of_zeros() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings sage.rings.function_field + sage: (x/y).divisor_of_zeros() # needs sage.modules sage.rings.finite_rings sage.rings.function_field 3*Place (x, x*y) """ if self.is_zero(): @@ -530,17 +534,17 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings - sage: f.divisor_of_poles() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)) + sage: f = 1/(x^3 + x^2 + x) + sage: f.divisor_of_poles() # needs sage.libs.pari sage.modules Place (x) + Place (x^2 + x + 1) :: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: (x/y).divisor_of_poles() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings sage.rings.function_field + sage: (x/y).divisor_of_poles() # needs sage.modules sage.rings.finite_rings sage.rings.function_field Place (1/x, 1/x*y) + 2*Place (x + 1, x*y) """ if self.is_zero(): @@ -557,16 +561,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings - sage: f.zeros() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)) + sage: f = 1/(x^3 + x^2 + x) + sage: f.zeros() # needs sage.libs.pari sage.modules [Place (1/x)] :: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: (x/y).zeros() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings sage.rings.function_field + sage: (x/y).zeros() # needs sage.modules sage.rings.finite_rings sage.rings.function_field [Place (x, x*y)] """ return self.divisor_of_zeros().support() @@ -577,16 +581,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings - sage: f.poles() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)) + sage: f = 1/(x^3 + x^2 + x) + sage: f.poles() # needs sage.libs.pari sage.modules [Place (x), Place (x^2 + x + 1)] :: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: (x/y).poles() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings sage.rings.function_field + sage: (x/y).poles() # needs sage.modules sage.rings.finite_rings sage.rings.function_field [Place (1/x, 1/x*y), Place (x + 1, x*y)] """ return self.divisor_of_poles().support() @@ -601,19 +605,20 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_infinite()[0] # optional - sage.rings.finite_rings sage.modules sage.rings.function_field - sage: y.valuation(p) # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_infinite()[0] # needs sage.modules sage.rings.function_field + sage: y.valuation(p) # needs sage.modules sage.rings.function_field -1 :: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.function_field sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.function_field sage.rings.function_field - sage: p = O.ideal(x - 1).place() # optional - sage.rings.function_field sage.rings.function_field - sage: y.valuation(p) # optional - sage.rings.function_field sage.rings.function_field + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: p = O.ideal(x - 1).place() + sage: y.valuation(p) 0 """ prime = place.prime_ideal() @@ -636,23 +641,24 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: p = K.place_infinite() # optional - sage.rings.finite_rings - sage: f = 1/t^2 + 3 # optional - sage.rings.finite_rings - sage: f.evaluate(p) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(5)) + sage: p = K.place_infinite() + sage: f = 1/t^2 + 3 + sage: f.evaluate(p) 3 :: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p, = L.places_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: p, = L.places_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: (y + x).evaluate(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p, = L.places_infinite() + sage: p, = L.places_infinite() + sage: (y + x).evaluate(p) Traceback (most recent call last): ... ValueError: has a pole at the place - sage: (y/x + 1).evaluate(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: (y/x + 1).evaluate(p) 1 """ R, _, to_R = place._residue_field() @@ -685,9 +691,9 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: f = (x+1)/(x-1) # optional - sage.rings.finite_rings - sage: f.is_nth_power(2) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: f = (x+1)/(x-1) + sage: f.is_nth_power(2) False """ raise NotImplementedError("is_nth_power() not implemented for generic elements") @@ -711,10 +717,10 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L(y^27).nth_root(27) # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(3)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: L(y^27).nth_root(27) # needs sage.rings.function_field y """ raise NotImplementedError("nth_root() not implemented for generic elements") diff --git a/src/sage/rings/function_field/element_polymod.pyx b/src/sage/rings/function_field/element_polymod.pyx index e797531eea9..9e198d0b042 100644 --- a/src/sage/rings/function_field/element_polymod.pyx +++ b/src/sage/rings/function_field/element_polymod.pyx @@ -276,22 +276,22 @@ cdef class FunctionFieldElement_polymod(FunctionFieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: L(y^3).nth_root(3) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: L(y^3).nth_root(3) y - sage: L(y^9).nth_root(-9) # optional - sage.rings.finite_rings + sage: L(y^9).nth_root(-9) 1/x*y This also works for inseparable extensions:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 - x^2) # optional - sage.rings.finite_rings - sage: L(x).nth_root(3)^3 # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: R. = K[] + sage: L. = K.extension(y^3 - x^2) + sage: L(x).nth_root(3)^3 x - sage: L(x^9).nth_root(-27)^-27 # optional - sage.rings.finite_rings + sage: L(x^9).nth_root(-27)^-27 x^9 """ @@ -337,12 +337,13 @@ cdef class FunctionFieldElement_polymod(FunctionFieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: y.is_nth_power(2) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: y.is_nth_power(2) False - sage: L(x).is_nth_power(2) # optional - sage.rings.finite_rings + sage: L(x).is_nth_power(2) True """ @@ -374,10 +375,10 @@ cdef class FunctionFieldElement_polymod(FunctionFieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: (y^3).nth_root(3) # indirect doctest # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: (y^3).nth_root(3) # indirect doctest y """ cdef Py_ssize_t deg = self._parent.degree() diff --git a/src/sage/rings/function_field/element_rational.pyx b/src/sage/rings/function_field/element_rational.pyx index a21fbe397e2..0d306d6826e 100644 --- a/src/sage/rings/function_field/element_rational.pyx +++ b/src/sage/rings/function_field/element_rational.pyx @@ -59,7 +59,7 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: ((a+1)/(a-1)).__pari__() # optional - sage.rings.finite_rings + sage: ((a+1)/(a-1)).__pari__() # needs sage.libs.pari (a + 1)/(a - 1) """ @@ -71,16 +71,16 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: t.element() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: t.element() t - sage: type(t.element()) # optional - sage.rings.finite_rings + sage: type(t.element()) # needs sage.rings.finite_rings <... 'sage.rings.fraction_field_FpT.FpTElement'> - sage: K. = FunctionField(GF(131101)) # optional - sage.rings.finite_rings - sage: t.element() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(131101)) # needs sage.libs.pari + sage: t.element() t - sage: type(t.element()) # optional - sage.rings.finite_rings + sage: type(t.element()) <... 'sage.rings.fraction_field_element.FractionFieldElement_1poly_field'> """ return self._x @@ -297,9 +297,9 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): sage: f.valuation(t^2 - 1/3) -3 - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: p = K.places_finite()[0] # optional - sage.rings.finite_rings - sage: (1/x^2).valuation(p) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: p = K.places_finite()[0] # needs sage.libs.pari + sage: (1/x^2).valuation(p) # needs sage.libs.pari -2 """ from .place import FunctionFieldPlace @@ -327,10 +327,10 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): sage: f = 9 * (t+1)^6 / (t^2 - 2*t + 1); f.is_square() True - sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: (-t^2).is_square() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(5)) + sage: (-t^2).is_square() # needs sage.libs.pari True - sage: (-t^2).sqrt() # optional - sage.rings.finite_rings + sage: (-t^2).sqrt() # needs sage.libs.pari 2*t """ return self._x.is_square() @@ -385,15 +385,16 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: f = (x+1)/(x-1) # optional - sage.rings.finite_rings - sage: f.is_nth_power(1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: f = (x+1)/(x-1) + sage: f.is_nth_power(1) True - sage: f.is_nth_power(3) # optional - sage.rings.finite_rings + sage: f.is_nth_power(3) False - sage: (f^3).is_nth_power(3) # optional - sage.rings.finite_rings + sage: (f^3).is_nth_power(3) True - sage: (f^9).is_nth_power(-9) # optional - sage.rings.finite_rings + sage: (f^9).is_nth_power(-9) True """ if n == 1: @@ -436,17 +437,18 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: f = (x+1)/(x+2) # optional - sage.rings.finite_rings - sage: f.nth_root(1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: f = (x+1)/(x+2) + sage: f.nth_root(1) (x + 1)/(x + 2) - sage: f.nth_root(3) # optional - sage.rings.finite_rings + sage: f.nth_root(3) Traceback (most recent call last): ... ValueError: element is not an n-th power - sage: (f^3).nth_root(3) # optional - sage.rings.finite_rings + sage: (f^3).nth_root(3) (x + 1)/(x + 2) - sage: (f^9).nth_root(-9) # optional - sage.rings.finite_rings + sage: (f^9).nth_root(-9) (x + 2)/(x + 1) """ if n == 0: @@ -473,15 +475,16 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): EXAMPLES:: + sage: # needs sage.libs.pari sage: K. = FunctionField(QQ) sage: f = (t+1) / (t^2 - 1/3) - sage: f.factor() # optional - sage.rings.finite_rings + sage: f.factor() (t + 1) * (t^2 - 1/3)^-1 - sage: (7*f).factor() # optional - sage.rings.finite_rings + sage: (7*f).factor() (7) * (t + 1) * (t^2 - 1/3)^-1 - sage: ((7*f).factor()).unit() # optional - sage.rings.finite_rings + sage: ((7*f).factor()).unit() 7 - sage: (f^3).factor() # optional - sage.rings.finite_rings + sage: (f^3).factor() (t + 1)^3 * (t^2 - 1/3)^-3 """ P = self.parent() diff --git a/src/sage/rings/function_field/extensions.py b/src/sage/rings/function_field/extensions.py index 10a81cc3c1e..9e42e5bff6b 100644 --- a/src/sage/rings/function_field/extensions.py +++ b/src/sage/rings/function_field/extensions.py @@ -11,36 +11,37 @@ Constant field extension of the rational function field over rational numbers:: sage: K. = FunctionField(QQ) - sage: N. = QuadraticField(2) # optional - sage.rings.number_field - sage: L = K.extension_constant_field(N) # optional - sage.rings.number_field - sage: L # optional - sage.rings.number_field + sage: N. = QuadraticField(2) # needs sage.rings.number_field + sage: L = K.extension_constant_field(N) # needs sage.rings.number_field + sage: L # needs sage.rings.number_field Rational function field in x over Number Field in a with defining polynomial x^2 - 2 with a = 1.4142... over its base - sage: d = (x^2 - 2).divisor() # optional - sage.rings.number_field - sage: d # optional - sage.rings.number_field + sage: d = (x^2 - 2).divisor() # needs sage.libs.pari sage.modules + sage: d # needs sage.libs.pari sage.modules -2*Place (1/x) + Place (x^2 - 2) - sage: L.conorm_divisor(d) # optional - sage.rings.number_field + sage: L.conorm_divisor(d) # needs sage.libs.pari sage.modules sage.rings.number_field -2*Place (1/x) + Place (x - a) + Place (x + a) Constant field extension of a function field over a finite field:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: E = F.extension_constant_field(GF(2^3)) + sage: E Function field in y defined by y^3 + x^6 + x^4 + x^2 over its base - sage: p = F.get_place(3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E.conorm_place(p) # random # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = F.get_place(3) + sage: E.conorm_place(p) # random Place (x + z3, y + z3^2 + z3) + Place (x + z3^2, y + z3) + Place (x + z3^2 + z3, y + z3^2) - sage: q = F.get_place(2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E.conorm_place(q) # random # optional - sage.rings.finite_rings sage.rings.function_field + sage: q = F.get_place(2) + sage: E.conorm_place(q) # random Place (x + 1, y^2 + y + 1) - sage: E.conorm_divisor(p + q) # random # optional - sage.rings.finite_rings sage.rings.function_field + sage: E.conorm_divisor(p + q) # random Place (x + 1, y^2 + y + 1) + Place (x + z3, y + z3^2 + z3) + Place (x + z3^2, y + z3) @@ -90,10 +91,11 @@ def __init__(self, F, k_ext): TESTS:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: TestSuite(E).run(skip=['_test_elements', '_test_pickling']) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: E = F.extension_constant_field(GF(2^3)) + sage: TestSuite(E).run(skip=['_test_elements', '_test_pickling']) """ k = F.constant_base_field() F_base = F.base_field() @@ -129,10 +131,11 @@ def top(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E.top() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: E = F.extension_constant_field(GF(2^3)) + sage: E.top() Function field in y defined by y^3 + x^6 + x^4 + x^2 """ return self._F_ext @@ -145,10 +148,11 @@ def defining_morphism(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E.defining_morphism() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: E = F.extension_constant_field(GF(2^3)) + sage: E.defining_morphism() Function Field morphism: From: Function field in y defined by y^3 + x^6 + x^4 + x^2 To: Function field in y defined by y^3 + x^6 + x^4 + x^2 @@ -170,16 +174,17 @@ def conorm_place(self, p): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = F.get_place(3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: d = E.conorm_place(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: [pl.degree() for pl in d.support()] # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: E = F.extension_constant_field(GF(2^3)) + sage: p = F.get_place(3) + sage: d = E.conorm_place(p) + sage: [pl.degree() for pl in d.support()] [1, 1, 1] - sage: p = F.get_place(2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: d = E.conorm_place(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: [pl.degree() for pl in d.support()] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = F.get_place(2) + sage: d = E.conorm_place(p) + sage: [pl.degree() for pl in d.support()] [2] """ embedF = self.defining_morphism() @@ -206,15 +211,16 @@ def conorm_divisor(self, d): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1 = F.get_place(3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p2 = F.get_place(2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: c = E.conorm_divisor(2*p1 + 3*p2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: c1 = E.conorm_place(p1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: c2 = E.conorm_place(p2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: c == 2*c1 + 3*c2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: E = F.extension_constant_field(GF(2^3)) + sage: p1 = F.get_place(3) + sage: p2 = F.get_place(2) + sage: c = E.conorm_divisor(2*p1 + 3*p2) + sage: c1 = E.conorm_place(p1) + sage: c2 = E.conorm_place(p2) + sage: c == 2*c1 + 3*c2 True """ div_top = self.divisor_group() diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 75c8fc1e0b0..973d9a8f505 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -9,122 +9,129 @@ We create a rational function field:: - sage: K. = FunctionField(GF(5^2,'a')); K # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(5^2,'a')); K Rational function field in x over Finite Field in a of size 5^2 - sage: K.genus() # optional - sage.rings.finite_rings + sage: K.genus() 0 - sage: f = (x^2 + x + 1) / (x^3 + 1) # optional - sage.rings.finite_rings - sage: f # optional - sage.rings.finite_rings + sage: f = (x^2 + x + 1) / (x^3 + 1) + sage: f (x^2 + x + 1)/(x^3 + 1) - sage: f^3 # optional - sage.rings.finite_rings + sage: f^3 (x^6 + 3*x^5 + x^4 + 2*x^3 + x^2 + 3*x + 1)/(x^9 + 3*x^6 + 3*x^3 + 1) Then we create an extension of the rational function field, and do some simple arithmetic in it:: - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)); L # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: R. = K[] + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)); L Function field in y defined by y^3 + 3*x*y + (4*x^4 + 4)/x - sage: y^2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: y^2 y^2 - sage: y^3 # optional - sage.rings.finite_rings sage.rings.function_field + sage: y^3 2*x*y + (x^4 + 1)/x - sage: a = 1/y; a # optional - sage.rings.finite_rings sage.rings.function_field + sage: a = 1/y; a (x/(x^4 + 1))*y^2 + 3*x^2/(x^4 + 1) - sage: a * y # optional - sage.rings.finite_rings sage.rings.function_field + sage: a * y 1 We next make an extension of the above function field, illustrating that arithmetic with a tower of three fields is fully supported:: - sage: S. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(t^2 - x*y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: M # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: S. = L[] + sage: M. = L.extension(t^2 - x*y) + sage: M Function field in t defined by t^2 + 4*x*y - sage: t^2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: t^2 x*y - sage: 1/t # optional - sage.rings.finite_rings sage.rings.function_field + sage: 1/t ((1/(x^4 + 1))*y^2 + 3*x/(x^4 + 1))*t - sage: M.base_field() # optional - sage.rings.finite_rings sage.rings.function_field + sage: M.base_field() Function field in y defined by y^3 + 3*x*y + (4*x^4 + 4)/x - sage: M.base_field().base_field() # optional - sage.rings.finite_rings sage.rings.function_field + sage: M.base_field().base_field() Rational function field in x over Finite Field in a of size 5^2 It is also possible to construct function fields over an imperfect base field:: - sage: N. = FunctionField(K) # optional - sage.rings.finite_rings + sage: N. = FunctionField(K) # needs sage.rings.finite_rings and inseparable extension function fields:: - sage: J. = FunctionField(GF(5)); J # optional - sage.rings.finite_rings + sage: J. = FunctionField(GF(5)); J Rational function field in x over Finite Field of size 5 - sage: T. = J[] # optional - sage.rings.finite_rings - sage: O. = J.extension(v^5 - x); O # optional - sage.rings.finite_rings sage.rings.function_field + sage: T. = J[] + sage: O. = J.extension(v^5 - x); O # needs sage.rings.function_field Function field in v defined by v^5 + 4*x Function fields over the rational field are supported:: + sage: # needs sage.rings.function_field sage: F. = FunctionField(QQ) sage: R. = F[] - sage: L. = F.extension(Y^2 - x^8 - 1) # optional - sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.function_field - sage: I = O.ideal(x, y - 1) # optional - sage.rings.function_field - sage: P = I.place() # optional - sage.rings.function_field - sage: D = P.divisor() # optional - sage.rings.function_field - sage: D.basis_function_space() # optional - sage.rings.function_field + sage: L. = F.extension(Y^2 - x^8 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(x, y - 1) + sage: P = I.place() + sage: D = P.divisor() + sage: D.basis_function_space() [1] - sage: (2*D).basis_function_space() # optional - sage.rings.function_field + sage: (2*D).basis_function_space() [1] - sage: (3*D).basis_function_space() # optional - sage.rings.function_field + sage: (3*D).basis_function_space() [1] - sage: (4*D).basis_function_space() # optional - sage.rings.function_field + sage: (4*D).basis_function_space() [1, 1/x^4*y + 1/x^4] + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: I.divisor() # optional - sage.rings.function_field + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: I.divisor() 2*Place (x, y, (1/(x^3 + x^2 + x))*y^2) + 2*Place (x^2 + x + 1, y, (1/(x^3 + x^2 + x))*y^2) + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: I.divisor() # optional - sage.rings.function_field + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: I.divisor() - Place (x, x*y) + Place (x^2 + 1, x*y) Function fields over the algebraic field are supported:: - sage: K. = FunctionField(QQbar); _. = K[] # optional - sage.rings.number_field - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.function_field sage.rings.number_field - sage: O = L.maximal_order() # optional - sage.rings.function_field sage.rings.number_field - sage: I = O.ideal(y) # optional - sage.rings.function_field sage.rings.number_field - sage: I.divisor() # optional - sage.rings.function_field sage.rings.number_field + sage: # needs sage.rings.function_field sage.rings.number_field + sage: K. = FunctionField(QQbar); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: I.divisor() Place (x - I, x*y) - Place (x, x*y) + Place (x + I, x*y) - sage: pl = I.divisor().support()[0] # optional - sage.rings.function_field sage.rings.number_field - sage: m = L.completion(pl, prec=5) # optional - sage.rings.function_field sage.rings.number_field - sage: m(x) # optional - sage.rings.function_field sage.rings.number_field + sage: pl = I.divisor().support()[0] + sage: m = L.completion(pl, prec=5) + sage: m(x) I + s + O(s^5) - sage: m(y) # long time (4s) # optional - sage.rings.function_field sage.rings.number_field + sage: m(y) # long time (4s) -2*s + (-4 - I)*s^2 + (-15 - 4*I)*s^3 + (-75 - 23*I)*s^4 + (-413 - 154*I)*s^5 + O(s^6) - sage: m(y)^2 + m(y) + m(x) + 1/m(x) # long time (8s) # optional - sage.rings.function_field sage.rings.number_field + sage: m(y)^2 + m(y) + m(x) + 1/m(x) # long time (8s) O(s^5) TESTS:: - sage: TestSuite(J).run() # optional - sage.rings.finite_rings - sage: TestSuite(K).run(max_runs=256) # long time (10s) # optional - sage.rings.number_field - sage: TestSuite(L).run(max_runs=8) # long time (25s) # optional - sage.rings.function_field sage.rings.number_field - sage: TestSuite(M).run(max_runs=8) # long time (35s) - sage: TestSuite(N).run(max_runs=8, skip = '_test_derivation') # long time (15s) - sage: TestSuite(O).run() # optional - sage.rings.function_field sage.rings.number_field - sage: TestSuite(R).run() - sage: TestSuite(S).run() # long time (4s) # optional - sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(J).run() + sage: TestSuite(K).run(max_runs=256) # long time (10s) # needs sage.rings.number_field + sage: TestSuite(L).run(max_runs=8) # long time (25s) # needs sage.rings.function_field sage.rings.number_field + sage: TestSuite(M).run(max_runs=8) # long time (35s) # needs sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(N).run(max_runs=8, skip='_test_derivation') # long time (15s), needs sage.rings.finite_rings + sage: TestSuite(O).run() + sage: TestSuite(R).run() # needs sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(S).run() # long time (4s) # needs sage.rings.finite_rings sage.rings.function_field Global function fields ---------------------- @@ -139,31 +146,33 @@ of its maximal order and maximal infinite order, and then do arithmetic with ideals of those maximal orders:: - sage: K. = FunctionField(GF(3)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.basis() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(3)); _. = K[] + sage: L. = K.extension(t^4 + t - x^5) + sage: O = L.maximal_order() + sage: O.basis() (1, y, 1/x*y^2 + 1/x*y, 1/x^3*y^3 + 2/x^3*y^2 + 1/x^3*y) - sage: I = O.ideal(x,y); I # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(x,y); I Ideal (x, y) of Maximal order of Function field in y defined by y^4 + y + 2*x^5 - sage: J = I^-1 # optional - sage.rings.finite_rings sage.rings.function_field - sage: J.basis_matrix() # optional - sage.rings.finite_rings sage.rings.function_field + sage: J = I^-1 + sage: J.basis_matrix() [ 1 0 0 0] [1/x 1/x 0 0] [ 0 0 1 0] [ 0 0 0 1] - sage: L.maximal_order_infinite().basis() # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.maximal_order_infinite().basis() (1, 1/x^2*y, 1/x^3*y^2, 1/x^4*y^3) As an example of the most sophisticated computations that Sage can do with a global function field, we compute all the Weierstrass places of the Klein quartic over `\GF{2}` and gap numbers for ordinary places:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.genus() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: L.genus() 3 - sage: L.weierstrass_places() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: L.weierstrass_places() # needs sage.modules [Place (1/x, 1/x^3*y^2 + 1/x), Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1), Place (x, y), @@ -174,17 +183,18 @@ Place (x^3 + x^2 + 1, y + x), Place (x^3 + x^2 + 1, y + x^2 + 1), Place (x^3 + x^2 + 1, y + x^2 + x + 1)] - sage: L.gaps() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: L.gaps() # needs sage.modules [1, 2, 3] The gap numbers for Weierstrass places are of course not ordinary:: - sage: p1,p2,p3 = L.weierstrass_places()[:3] # optional - sage.rings.finite_rings sage.modules sage.rings.function_field - sage: p1.gaps() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: # needs sage.modules sage.rings.function_field + sage: p1,p2,p3 = L.weierstrass_places()[:3] + sage: p1.gaps() [1, 2, 4] - sage: p2.gaps() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: p2.gaps() [1, 2, 4] - sage: p3.gaps() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: p3.gaps() [1, 2, 4] AUTHORS: @@ -303,7 +313,7 @@ def is_perfect(self): sage: FunctionField(QQ, 'x').is_perfect() True - sage: FunctionField(GF(2), 'x').is_perfect() # optional - sage.rings.finite_rings + sage: FunctionField(GF(2), 'x').is_perfect() False """ return self.characteristic() == 0 @@ -331,8 +341,8 @@ def some_elements(self): :: sage: R. = K[] - sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field - sage: L.some_elements() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: L.some_elements() # needs sage.rings.function_field [1, y, 1/x*y, @@ -366,15 +376,15 @@ def characteristic(self): sage: K. = FunctionField(QQ) sage: K.characteristic() 0 - sage: K. = FunctionField(QQbar) # optional - sage.rings.number_field - sage: K.characteristic() # optional - sage.rings.number_field + sage: K. = FunctionField(QQbar) # needs sage.rings.number_field + sage: K.characteristic() 0 - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: K.characteristic() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: K.characteristic() 7 - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.characteristic() # optional - sage.rings.finite_rings sage.rings.function_field + sage: R. = K[] + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: L.characteristic() # needs sage.rings.function_field 7 """ return self.constant_base_field().characteristic() @@ -388,8 +398,8 @@ def is_finite(self): sage: R. = FunctionField(QQ) sage: R.is_finite() False - sage: R. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: R.is_finite() # optional - sage.rings.finite_rings + sage: R. = FunctionField(GF(7)) + sage: R.is_finite() False """ return False @@ -404,11 +414,11 @@ def is_global(self): sage: R. = FunctionField(QQ) sage: R.is_global() False - sage: R. = FunctionField(QQbar) # optional - sage.rings.number_field - sage: R.is_global() # optional - sage.rings.number_field + sage: R. = FunctionField(QQbar) # needs sage.rings.number_field + sage: R.is_global() False - sage: R. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: R.is_global() # optional - sage.rings.finite_rings + sage: R. = FunctionField(GF(7)) + sage: R.is_global() True """ return self.constant_base_field().is_finite() @@ -431,18 +441,18 @@ def extension(self, f, names=None): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: K.extension(y^5 - x^3 - 3*x + x*y) # optional - sage.rings.function_field + sage: K.extension(y^5 - x^3 - 3*x + x*y) # needs sage.rings.function_field Function field in y defined by y^5 + x*y - x^3 - 3*x A nonintegral defining polynomial:: sage: K. = FunctionField(QQ); R. = K[] - sage: K.extension(y^3 + (1/t)*y + t^3/(t+1), 'z') # optional - sage.rings.function_field + sage: K.extension(y^3 + (1/t)*y + t^3/(t+1), 'z') # needs sage.rings.function_field Function field in z defined by z^3 + 1/t*z + t^3/(t + 1) The defining polynomial need not be monic or integral:: - sage: K.extension(t*y^3 + (1/t)*y + t^3/(t+1)) # optional - sage.rings.function_field + sage: K.extension(t*y^3 + (1/t)*y + t^3/(t+1)) # needs sage.rings.function_field Function field in y defined by t*y^3 + 1/t*y + t^3/(t + 1) """ from . import constructor @@ -468,29 +478,29 @@ def order_with_basis(self, basis, check=True): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field - sage: O = L.order_with_basis([1, y, y^2]); O # optional - sage.rings.function_field + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # needs sage.rings.function_field + sage: O = L.order_with_basis([1, y, y^2]); O # needs sage.rings.function_field Order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() # optional - sage.rings.function_field + sage: O.basis() # needs sage.rings.function_field (1, y, y^2) Note that 1 does not need to be an element of the basis, as long it is in the module spanned by it:: - sage: O = L.order_with_basis([1+y, y, y^2]); O # optional - sage.rings.function_field + sage: O = L.order_with_basis([1+y, y, y^2]); O # needs sage.rings.function_field Order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() # optional - sage.rings.function_field + sage: O.basis() # needs sage.rings.function_field (y + 1, y, y^2) The following error is raised when the module spanned by the basis is not closed under multiplication:: - sage: O = L.order_with_basis([1, x^2 + x*y, (2/3)*y^2]); O # optional - sage.rings.function_field + sage: O = L.order_with_basis([1, x^2 + x*y, (2/3)*y^2]); O # needs sage.rings.function_field Traceback (most recent call last): ... ValueError: the module generated by basis (1, x*y + x^2, 2/3*y^2) must be closed under multiplication and this happens when the identity is not in the module spanned by the basis:: - sage: O = L.order_with_basis([x, x^2 + x*y, (2/3)*y^2]) # optional - sage.rings.function_field + sage: O = L.order_with_basis([x, x^2 + x*y, (2/3)*y^2]) # needs sage.rings.function_field Traceback (most recent call last): ... ValueError: the identity element must be in the module spanned by basis (x, x*y + x^2, 2/3*y^2) @@ -510,21 +520,22 @@ def order(self, x, check=True): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field - sage: O = L.order(y); O # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) + sage: O = L.order(y); O # needs sage.modules Order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() # optional - sage.modules sage.rings.function_field + sage: O.basis() # needs sage.modules (1, y, y^2) - sage: Z = K.order(x); Z # optional - sage.modules + sage: Z = K.order(x); Z # needs sage.modules sage.rings.function_field Order in Rational function field in x over Rational Field - sage: Z.basis() # optional - sage.modules + sage: Z.basis() # needs sage.modules sage.rings.function_field (1,) Orders with multiple generators are not yet supported:: - sage: Z = K.order([x, x^2]); Z # optional - sage.modules + sage: Z = K.order([x, x^2]); Z # needs sage.rings.function_field Traceback (most recent call last): ... NotImplementedError @@ -555,25 +566,26 @@ def order_infinite_with_basis(self, basis, check=True): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field - sage: O = L.order_infinite_with_basis([1, 1/x*y, 1/x^2*y^2]); O # optional - sage.rings.function_field + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) + sage: O = L.order_infinite_with_basis([1, 1/x*y, 1/x^2*y^2]); O Infinite order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() # optional - sage.rings.function_field + sage: O.basis() (1, 1/x*y, 1/x^2*y^2) Note that 1 does not need to be an element of the basis, as long it is in the module spanned by it:: - sage: O = L.order_infinite_with_basis([1+1/x*y,1/x*y, 1/x^2*y^2]); O # optional - sage.rings.function_field + sage: O = L.order_infinite_with_basis([1+1/x*y,1/x*y, 1/x^2*y^2]); O # needs sage.rings.function_field Infinite order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() # optional - sage.rings.function_field + sage: O.basis() # needs sage.rings.function_field (1/x*y + 1, 1/x*y, 1/x^2*y^2) The following error is raised when the module spanned by the basis is not closed under multiplication:: - sage: O = L.order_infinite_with_basis([1,y, 1/x^2*y^2]); O # optional - sage.rings.function_field + sage: O = L.order_infinite_with_basis([1,y, 1/x^2*y^2]); O # needs sage.rings.function_field Traceback (most recent call last): ... ValueError: the module generated by basis (1, y, 1/x^2*y^2) must be closed under multiplication @@ -581,7 +593,7 @@ def order_infinite_with_basis(self, basis, check=True): and this happens when the identity is not in the module spanned by the basis:: - sage: O = L.order_infinite_with_basis([1/x,1/x*y, 1/x^2*y^2]) # optional - sage.rings.function_field + sage: O = L.order_infinite_with_basis([1/x,1/x*y, 1/x^2*y^2]) # needs sage.rings.function_field Traceback (most recent call last): ... ValueError: the identity element must be in the module spanned by basis (1/x, 1/x*y, 1/x^2*y^2) @@ -602,17 +614,17 @@ def order_infinite(self, x, check=True): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field - sage: L.order_infinite(y) # todo: not implemented # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # needs sage.rings.function_field + sage: L.order_infinite(y) # not implemented # needs sage.modules sage.rings.function_field - sage: Z = K.order(x); Z # optional - sage.modules + sage: Z = K.order(x); Z # needs sage.modules Order in Rational function field in x over Rational Field - sage: Z.basis() # optional - sage.modules + sage: Z.basis() # needs sage.modules (1,) Orders with multiple generators, not yet supported:: - sage: Z = K.order_infinite([x, x^2]); Z # optional - sage.modules + sage: Z = K.order_infinite([x, x^2]); Z Traceback (most recent call last): ... NotImplementedError @@ -638,35 +650,36 @@ def _coerce_map_from_(self, source): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field - sage: L.equation_order() # optional - sage.rings.function_field + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) + sage: L.equation_order() Order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: L._coerce_map_from_(L.equation_order()) # optional - sage.rings.function_field + sage: L._coerce_map_from_(L.equation_order()) Conversion map: From: Order in Function field in y defined by y^3 + x^3 + 4*x + 1 To: Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: L._coerce_map_from_(GF(7)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L._coerce_map_from_(GF(7)) sage: K. = FunctionField(QQ) - sage: L. = FunctionField(GaussianIntegers().fraction_field()) # optional - sage.rings.number_field - sage: L.has_coerce_map_from(K) # optional - sage.rings.number_field + sage: L. = FunctionField(GaussianIntegers().fraction_field()) # needs sage.rings.number_field + sage: L.has_coerce_map_from(K) # needs sage.rings.number_field True sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^3 + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: K. = FunctionField(GaussianIntegers().fraction_field()) # optional - sage.rings.number_field - sage: R. = K[] # optional - sage.rings.number_field - sage: M. = K.extension(y^3 + 1) # optional - sage.rings.function_field - sage: M.has_coerce_map_from(L) # not tested (the constant field including into a function field is not yet known to be injective) # optional - sage.rings.function_field + sage: L. = K.extension(y^3 + 1) # needs sage.rings.function_field + sage: K. = FunctionField(GaussianIntegers().fraction_field()) # needs sage.rings.number_field + sage: R. = K[] + sage: M. = K.extension(y^3 + 1) # needs sage.rings.function_field + sage: M.has_coerce_map_from(L) # not tested (the constant field including into a function field is not yet known to be injective), needs sage.rings.function_field True sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(I^2 + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: M. = FunctionField(GaussianIntegers().fraction_field()) # optional - sage.rings.number_field - sage: M.has_coerce_map_from(L) # optional - sage.rings.finite_rings sage.rings.function_field sage.rings.number_field + sage: L. = K.extension(I^2 + 1) # needs sage.rings.function_field + sage: M. = FunctionField(GaussianIntegers().fraction_field()) # needs sage.rings.number_field + sage: M.has_coerce_map_from(L) # needs sage.rings.function_field sage.rings.number_field True Check that :trac:`31072` is fixed:: @@ -732,7 +745,10 @@ def _test_derivation(self, **options): tester = self._tester(**options) S = tester.some_elements() K = self.constant_base_field().some_elements() - d = self.derivation() + try: + d = self.derivation() + except ImportError: + return from itertools import product # Non-zero tester.assertFalse(d.is_zero()) @@ -764,8 +780,8 @@ def _convert_map_from_(self, R): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field - sage: K(L(x)) # indirect doctest # optional - sage.rings.function_field + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # needs sage.rings.function_field + sage: K(L(x)) # indirect doctest # needs sage.rings.function_field x """ try: @@ -799,28 +815,29 @@ def _intermediate_fields(self, base): [Rational function field in x over Rational Field] sage: R. = K[] - sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field - sage: L._intermediate_fields(K) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: L._intermediate_fields(K) # needs sage.rings.function_field [Function field in y defined by y^2 - x, Rational function field in x over Rational Field] - sage: R. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z^2 - y) # optional - sage.rings.function_field - sage: M._intermediate_fields(L) # optional - sage.rings.function_field + sage: # needs sage.rings.function_field + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: M._intermediate_fields(L) [Function field in z defined by z^2 - y, Function field in y defined by y^2 - x] - sage: M._intermediate_fields(K) # optional - sage.rings.function_field + sage: M._intermediate_fields(K) [Function field in z defined by z^2 - y, Function field in y defined by y^2 - x, Rational function field in x over Rational Field] TESTS:: - sage: K._intermediate_fields(M) # optional - sage.rings.function_field + sage: K._intermediate_fields(M) # needs sage.rings.function_field Traceback (most recent call last): ... ValueError: field has not been constructed as a finite extension of base - sage: K._intermediate_fields(QQ) # optional - sage.rings.function_field + sage: K._intermediate_fields(QQ) Traceback (most recent call last): ... TypeError: base must be a function field @@ -847,13 +864,13 @@ def rational_function_field(self): Rational function field in x over Rational Field sage: R. = K[] - sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field - sage: L.rational_function_field() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: L.rational_function_field() # needs sage.rings.function_field Rational function field in x over Rational Field - sage: R. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z^2 - y) # optional - sage.rings.function_field - sage: M.rational_function_field() # optional - sage.rings.function_field + sage: R. = L[] # needs sage.rings.function_field + sage: M. = L.extension(z^2 - y) # needs sage.rings.function_field + sage: M.rational_function_field() # needs sage.rings.function_field Rational function field in x over Rational Field """ from .function_field_rational import RationalFunctionField @@ -877,49 +894,50 @@ def valuation(self, prime): function field:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(1); v # optional - sage.rings.function_field + sage: v = K.valuation(1); v # needs sage.rings.function_field (x - 1)-adic valuation - sage: v(x) # optional - sage.rings.function_field + sage: v(x) # needs sage.rings.function_field 0 - sage: v(x - 1) # optional - sage.rings.function_field + sage: v(x - 1) # needs sage.rings.function_field 1 A place can also be specified with an irreducible polynomial:: - sage: v = K.valuation(x - 1); v # optional - sage.rings.function_field + sage: v = K.valuation(x - 1); v # needs sage.rings.function_field (x - 1)-adic valuation Similarly, for a finite non-rational place:: - sage: v = K.valuation(x^2 + 1); v # optional - sage.rings.function_field + sage: v = K.valuation(x^2 + 1); v # needs sage.rings.function_field (x^2 + 1)-adic valuation - sage: v(x^2 + 1) # optional - sage.rings.function_field + sage: v(x^2 + 1) # needs sage.rings.function_field 1 - sage: v(x) # optional - sage.rings.function_field + sage: v(x) # needs sage.rings.function_field 0 Or for the infinite place:: - sage: v = K.valuation(1/x); v # optional - sage.rings.function_field + sage: v = K.valuation(1/x); v # needs sage.rings.function_field Valuation at the infinite place - sage: v(x) # optional - sage.rings.function_field + sage: v(x) # needs sage.rings.function_field -1 Instead of specifying a generator of a place, we can define a valuation on a rational function field by giving a discrete valuation on the underlying polynomial ring:: - sage: R. = QQ[] # optional - sage.rings.function_field - sage: u = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) # optional - sage.rings.function_field - sage: w = u.augmentation(x - 1, 1) # optional - sage.rings.function_field - sage: v = K.valuation(w); v # optional - sage.rings.function_field + sage: # needs sage.rings.function_field + sage: R. = QQ[] + sage: u = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) + sage: w = u.augmentation(x - 1, 1) + sage: v = K.valuation(w); v (x - 1)-adic valuation Note that this allows us to specify valuations which do not correspond to a place of the function field:: - sage: w = valuations.GaussValuation(R, QQ.valuation(2)) # optional - sage.rings.function_field - sage: v = K.valuation(w); v # optional - sage.rings.function_field + sage: w = valuations.GaussValuation(R, QQ.valuation(2)) # needs sage.rings.function_field + sage: v = K.valuation(w); v # needs sage.rings.function_field 2-adic valuation The same is possible for valuations with `v(1/x) > 0` by passing in an @@ -929,9 +947,10 @@ def valuation(self, prime): applying the substitution `x \mapsto 1/x` (here, the inverse map is also `x \mapsto 1/x`):: - sage: w = valuations.GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) # optional - sage.rings.function_field - sage: w = K.valuation(w) # optional - sage.rings.function_field - sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v # optional - sage.rings.function_field + sage: # needs sage.rings.function_field + sage: w = valuations.GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) + sage: w = K.valuation(w) + sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) @@ -939,10 +958,11 @@ def valuation(self, prime): Note that classical valuations at finite places or the infinite place are always normalized such that the uniformizing element has valuation 1:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: M. = FunctionField(K) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = M.valuation(x^3 - t) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v(x^3 - t) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(3)) + sage: M. = FunctionField(K) + sage: v = M.valuation(x^3 - t) + sage: v(x^3 - t) 1 However, if such a valuation comes out of a base change of the ground @@ -950,16 +970,17 @@ def valuation(self, prime): extension of ``v`` to ``L`` still has valuation 1 on `x^3 - t` but it has valuation ``1/3`` on its uniformizing element `x - w`:: - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(w^3 - t) # optional - sage.rings.finite_rings sage.rings.function_field - sage: N. = FunctionField(L) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w = v.extension(N) # missing factorization, :trac:`16572` # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: R. = K[] + sage: L. = K.extension(w^3 - t) + sage: N. = FunctionField(L) + sage: w = v.extension(N) # missing factorization, :trac:`16572` Traceback (most recent call last): ... NotImplementedError - sage: w(x^3 - t) # not tested # optional - sage.rings.finite_rings sage.rings.function_field + sage: w(x^3 - t) # not tested 1 - sage: w(x - w) # not tested # optional - sage.rings.finite_rings sage.rings.function_field + sage: w(x - w) # not tested 1/3 There are several ways to create valuations on extensions of rational @@ -967,12 +988,12 @@ def valuation(self, prime): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x); L # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x); L # needs sage.rings.function_field Function field in y defined by y^2 - x A place that has a unique extension can just be defined downstairs:: - sage: v = L.valuation(x); v # optional - sage.rings.function_field + sage: v = L.valuation(x); v # needs sage.rings.function_field (x)-adic valuation """ @@ -986,12 +1007,12 @@ def space_of_differentials(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.space_of_differentials() # optional - sage.modules + sage: K.space_of_differentials() # needs sage.modules Space of differentials of Rational function field in t over Rational Field - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.space_of_differentials() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # needs sage.rings.function_field + sage: L.space_of_differentials() # needs sage.modules sage.rings.function_field Space of differentials of Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) """ @@ -1004,7 +1025,7 @@ def space_of_holomorphic_differentials(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.space_of_holomorphic_differentials() # optional - sage.modules + sage: K.space_of_holomorphic_differentials() # needs sage.libs.pari sage.modules (Vector space of dimension 0 over Rational Field, Linear map: From: Vector space of dimension 0 over Rational Field @@ -1013,9 +1034,9 @@ def space_of_holomorphic_differentials(self): From: Space of differentials of Rational function field in t over Rational Field To: Vector space of dimension 0 over Rational Field) - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.space_of_holomorphic_differentials() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # needs sage.rings.function_field + sage: L.space_of_holomorphic_differentials() # needs sage.modules sage.rings.function_field (Vector space of dimension 4 over Finite Field of size 5, Linear map: From: Vector space of dimension 4 over Finite Field of size 5 @@ -1037,12 +1058,12 @@ def basis_of_holomorphic_differentials(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.basis_of_holomorphic_differentials() # optional - sage.modules + sage: K.basis_of_holomorphic_differentials() # needs sage.libs.pari sage.modules [] - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.basis_of_holomorphic_differentials() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # needs sage.rings.function_field + sage: L.basis_of_holomorphic_differentials() # needs sage.modules sage.rings.function_field [((x/(x^3 + 4))*y) d(x), ((1/(x^3 + 4))*y) d(x), ((x/(x^3 + 4))*y^2) d(x), @@ -1059,17 +1080,17 @@ def divisor_group(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.divisor_group() # optional - sage.modules + sage: K.divisor_group() # needs sage.modules Divisor group of Rational function field in t over Rational Field sage: _. = K[] - sage: L. = K.extension(Y^3 - (t^3 - 1)/(t^3 - 2)) # optional - sage.rings.function_field - sage: L.divisor_group() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(Y^3 - (t^3 - 1)/(t^3 - 2)) # needs sage.rings.function_field + sage: L.divisor_group() # needs sage.modules sage.rings.function_field Divisor group of Function field in y defined by y^3 + (-t^3 + 1)/(t^3 - 2) - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.divisor_group() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # needs sage.rings.function_field + sage: L.divisor_group() # needs sage.modules sage.rings.function_field Divisor group of Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) """ from .divisor import DivisorGroup @@ -1081,17 +1102,17 @@ def place_set(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: K.place_set() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: K.place_set() Set of places of Rational function field in t over Finite Field of size 7 sage: K. = FunctionField(QQ) sage: K.place_set() Set of places of Rational function field in t over Rational Field - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: L.place_set() # needs sage.rings.function_field Set of places of Function field in y defined by y^2 + y + (x^2 + 1)/x """ from .place import PlaceSet @@ -1115,83 +1136,87 @@ def completion(self, place, name=None, prec=None, gen_name=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p); m # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: m = L.completion(p); m Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(x, 10) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(x, 10) s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + s^9 + s^10 + O(s^12) - sage: m(y, 10) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(y, 10) s^-1 + 1 + s^3 + s^5 + s^7 + O(s^9) - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p); m # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: m = L.completion(p); m Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(x, 10) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(x, 10) s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + s^9 + s^10 + O(s^12) - sage: m(y, 10) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(y, 10) s^-1 + 1 + s^3 + s^5 + s^7 + O(s^9) - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: p = K.places_finite()[0]; p # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: p = K.places_finite()[0]; p # needs sage.libs.pari Place (x) - sage: m = K.completion(p); m # optional - sage.rings.finite_rings + sage: m = K.completion(p); m # needs sage.rings.function_field Completion map: From: Rational function field in x over Finite Field of size 2 To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(1/(x+1)) # optional - sage.rings.finite_rings + sage: m(1/(x+1)) # needs sage.rings.function_field 1 + s + s^2 + s^3 + s^4 + s^5 + s^6 + s^7 + s^8 + s^9 + s^10 + s^11 + s^12 + s^13 + s^14 + s^15 + s^16 + s^17 + s^18 + s^19 + O(s^20) - sage: p = K.place_infinite(); p # optional - sage.rings.finite_rings + sage: p = K.place_infinite(); p Place (1/x) - sage: m = K.completion(p); m # optional - sage.rings.finite_rings + sage: m = K.completion(p); m # needs sage.rings.function_field Completion map: From: Rational function field in x over Finite Field of size 2 To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(x) # optional - sage.rings.finite_rings + sage: m(x) # needs sage.rings.function_field s^-1 + O(s^19) - sage: m = K.completion(p, prec=infinity); m # optional - sage.rings.finite_rings + sage: m = K.completion(p, prec=infinity); m # needs sage.rings.function_field Completion map: From: Rational function field in x over Finite Field of size 2 To: Lazy Laurent Series Ring in s over Finite Field of size 2 - sage: f = m(x); f # optional - sage.rings.finite_rings + sage: f = m(x); f # needs sage.rings.function_field s^-1 + ... - sage: f.coefficient(100) # optional - sage.rings.finite_rings + sage: f.coefficient(100) # needs sage.rings.function_field 0 + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 - x) # optional - sage.rings.function_field sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.function_field sage.rings.function_field - sage: decomp = O.decomposition(K.maximal_order().ideal(x - 1)) # optional - sage.rings.function_field sage.rings.function_field - sage: pls = (decomp[0][0].place(), decomp[1][0].place()) # optional - sage.rings.function_field sage.rings.function_field - sage: m = L.completion(pls[0]); m # optional - sage.rings.function_field sage.rings.function_field + sage: L. = K.extension(Y^2 - x) + sage: O = L.maximal_order() + sage: decomp = O.decomposition(K.maximal_order().ideal(x - 1)) + sage: pls = (decomp[0][0].place(), decomp[1][0].place()) + sage: m = L.completion(pls[0]); m Completion map: From: Function field in y defined by y^2 - x To: Laurent Series Ring in s over Rational Field - sage: xe = m(x) # optional - sage.rings.function_field sage.rings.function_field - sage: ye = m(y) # optional - sage.rings.function_field sage.rings.function_field - sage: ye^2 - xe == 0 # optional - sage.rings.function_field sage.rings.function_field + sage: xe = m(x) + sage: ye = m(y) + sage: ye^2 - xe == 0 True - sage: decomp2 = O.decomposition(K.maximal_order().ideal(x^2 + 1)) # optional - sage.rings.function_field sage.rings.function_field - sage: pls2 = decomp2[0][0].place() # optional - sage.rings.function_field sage.rings.function_field - sage: m = L.completion(pls2); m # optional - sage.rings.function_field sage.rings.function_field + sage: # needs sage.rings.function_field + sage: decomp2 = O.decomposition(K.maximal_order().ideal(x^2 + 1)) + sage: pls2 = decomp2[0][0].place() + sage: m = L.completion(pls2); m Completion map: From: Function field in y defined by y^2 - x To: Laurent Series Ring in s over Number Field in a with defining polynomial x^4 + 2*x^2 + 4*x + 2 - sage: xe = m(x) # optional - sage.rings.function_field sage.rings.function_field - sage: ye = m(y) # optional - sage.rings.function_field sage.rings.function_field - sage: ye^2 - xe == 0 # optional - sage.rings.function_field sage.rings.function_field + sage: xe = m(x) + sage: ye = m(y) + sage: ye^2 - xe == 0 True """ from .maps import FunctionFieldCompletion @@ -1207,12 +1232,13 @@ def extension_constant_field(self, k): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E = F.extension_constant_field(GF(2^4)) # optional - sage.rings.finite_rings sage.rings.function_field - sage: E # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(Y^2 + Y + x + 1/x) + sage: E = F.extension_constant_field(GF(2^4)) + sage: E Function field in y defined by y^2 + y + (x^2 + 1)/x over its base - sage: E.constant_base_field() # optional - sage.rings.finite_rings sage.rings.function_field + sage: E.constant_base_field() Finite Field in z4 of size 2^4 """ from .extensions import ConstantFieldExtension diff --git a/src/sage/rings/function_field/function_field_polymod.py b/src/sage/rings/function_field/function_field_polymod.py index fa8731848ee..cf0dfc439c5 100644 --- a/src/sage/rings/function_field/function_field_polymod.py +++ b/src/sage/rings/function_field/function_field_polymod.py @@ -491,9 +491,9 @@ def constant_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^5 - x) # optional - sage.rings.finite_rings - sage: L.constant_field() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(5)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^5 - x) # needs sage.rings.finite_rings + sage: L.constant_field() # needs sage.rings.finite_rings Traceback (most recent call last): ... NotImplementedError @@ -631,28 +631,31 @@ def is_separable(self, base=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: L.is_separable() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: L.is_separable() False - sage: R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^3 - y) # optional - sage.rings.finite_rings - sage: M.is_separable() # optional - sage.rings.finite_rings + sage: R. = L[] + sage: M. = L.extension(z^3 - y) + sage: M.is_separable() True - sage: M.is_separable(K) # optional - sage.rings.finite_rings + sage: M.is_separable(K) False - sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # optional - sage.rings.finite_rings - sage: L.is_separable() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(5)) + sage: R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.is_separable() True - sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^5 - 1) # optional - sage.rings.finite_rings - sage: L.is_separable() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(5)) + sage: R. = K[] + sage: L. = K.extension(y^5 - 1) + sage: L.is_separable() False """ @@ -719,48 +722,49 @@ def free_module(self, base=None, basis=None, map=True): We get the vector spaces, and maps back and forth:: - sage: V, from_V, to_V = L.free_module() # optional - sage.modules - sage: V # optional - sage.modules + sage: # needs sage.modules + sage: V, from_V, to_V = L.free_module() + sage: V Vector space of dimension 5 over Rational function field in x over Rational Field - sage: from_V # optional - sage.modules + sage: from_V Isomorphism: From: Vector space of dimension 5 over Rational function field in x over Rational Field To: Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - sage: to_V # optional - sage.modules + sage: to_V Isomorphism: From: Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x To: Vector space of dimension 5 over Rational function field in x over Rational Field We convert an element of the vector space back to the function field:: - sage: from_V(V.1) # optional - sage.modules + sage: from_V(V.1) # needs sage.modules y We define an interesting element of the function field:: - sage: a = 1/L.0; a # optional - sage.modules + sage: a = 1/L.0; a # needs sage.modules (x/(x^4 + 1))*y^4 - 2*x^2/(x^4 + 1) We convert it to the vector space, and get a vector over the base field:: - sage: to_V(a) # optional - sage.modules + sage: to_V(a) # needs sage.modules (-2*x^2/(x^4 + 1), 0, 0, 0, x/(x^4 + 1)) We convert to and back, and get the same element:: - sage: from_V(to_V(a)) == a # optional - sage.modules + sage: from_V(to_V(a)) == a # needs sage.modules True In the other direction:: - sage: v = x*V.0 + (1/x)*V.1 # optional - sage.modules - sage: to_V(from_V(v)) == v # optional - sage.modules + sage: v = x*V.0 + (1/x)*V.1 # needs sage.modules + sage: to_V(from_V(v)) == v # needs sage.modules True And we show how it works over an extension of an extension field:: sage: R2. = L[]; M. = L.extension(z^2 - y) - sage: M.free_module() # optional - sage.modules + sage: M.free_module() # needs sage.modules (Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x, Isomorphism: From: Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x To: Function field in z defined by z^2 - y, Isomorphism: @@ -769,7 +773,7 @@ def free_module(self, base=None, basis=None, map=True): We can also get the vector space of ``M`` over ``K``:: - sage: M.free_module(K) # optional - sage.modules + sage: M.free_module(K) # needs sage.modules (Vector space of dimension 10 over Rational function field in x over Rational Field, Isomorphism: From: Vector space of dimension 10 over Rational function field in x over Rational Field To: Function field in z defined by z^2 - y, Isomorphism: @@ -815,14 +819,14 @@ def maximal_order_infinite(self): sage: L.maximal_order_infinite() Maximal infinite order of Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # needs sage.rings.finite_rings + sage: F.maximal_order_infinite() # needs sage.rings.finite_rings Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings + sage: L.maximal_order_infinite() # needs sage.rings.finite_rings Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ from .order_polymod import FunctionFieldMaximalOrderInfinite_polymod @@ -834,9 +838,9 @@ def different(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: F.different() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # needs sage.rings.finite_rings + sage: F.different() # needs sage.rings.finite_rings 2*Place (x, (1/(x^3 + x^2 + x))*y^2) + 2*Place (x^2 + x + 1, (1/(x^3 + x^2 + x))*y^2) """ @@ -1079,12 +1083,13 @@ def _simple_model(self, name='v'): Check that this also works for inseparable extensions:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings - sage: M._simple_model() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: M._simple_model() (Function field in v defined by v^4 + x, Function Field morphism: From: Function field in v defined by v^4 + x @@ -1099,12 +1104,13 @@ def _simple_model(self, name='v'): An example where the generator of the last extension does not generate the extension of the rational function field:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^3 - 1) # optional - sage.rings.finite_rings - sage: M._simple_model() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: R. = L[] + sage: M. = L.extension(z^3 - 1) + sage: M._simple_model() (Function field in v defined by v^6 + x*v^4 + x^2*v^2 + x^3 + 1, Function Field morphism: From: Function field in v defined by v^6 + x*v^4 + x^2*v^2 + x^3 + 1 @@ -1239,10 +1245,11 @@ def simple_model(self, name=None): An example with higher degrees:: - sage: K. = FunctionField(GF(3)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^5 - x); R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^3 - x) # optional - sage.rings.finite_rings - sage: M.simple_model() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3)); R. = K[] + sage: L. = K.extension(y^5 - x); R. = L[] + sage: M. = L.extension(z^3 - x) + sage: M.simple_model() (Function field in z defined by z^15 + x*z^12 + x^2*z^9 + 2*x^3*z^6 + 2*x^4*z^3 + 2*x^5 + 2*x^3, Function Field morphism: From: Function field in z defined by z^15 + x*z^12 + x^2*z^9 + 2*x^3*z^6 + 2*x^4*z^3 + 2*x^5 + 2*x^3 @@ -1257,10 +1264,11 @@ def simple_model(self, name=None): This also works for inseparable extensions:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x); R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings - sage: M.simple_model() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x); R. = L[] + sage: M. = L.extension(z^2 - y) + sage: M.simple_model() (Function field in z defined by z^4 + x, Function Field morphism: From: Function field in z defined by z^4 + x To: Function field in z defined by z^2 + y @@ -1330,12 +1338,13 @@ def primitive_element(self): This also works for inseparable extensions:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x) # optional - sage.rings.finite_rings - sage: R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(Z^2 - y) # optional - sage.rings.finite_rings - sage: M.primitive_element() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(Y^2 - x) + sage: R. = L[] + sage: M. = L.extension(Z^2 - y) + sage: M.primitive_element() z """ N, f, t = self.simple_model() @@ -1371,10 +1380,11 @@ def separable_model(self, names=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3) # optional - sage.rings.finite_rings - sage: L.separable_model(('t','w')) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x^3) + sage: L.separable_model(('t','w')) (Function field in t defined by t^3 + w^2, Function Field morphism: From: Function field in t defined by t^3 + w^2 @@ -1389,10 +1399,11 @@ def separable_model(self, names=None): This also works for non-integral polynomials:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2/x - x^2) # optional - sage.rings.finite_rings - sage: L.separable_model() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2/x - x^2) + sage: L.separable_model() (Function field in y_ defined by y_^3 + x_^2, Function Field morphism: From: Function field in y_ defined by y_^3 + x_^2 @@ -1407,13 +1418,14 @@ def separable_model(self, names=None): If the base field is not perfect this is only implemented in trivial cases:: - sage: k. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: k.is_perfect() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = FunctionField(GF(2)) + sage: k.is_perfect() False - sage: K. = FunctionField(k) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 - t) # optional - sage.rings.finite_rings - sage: L.separable_model() # optional - sage.rings.finite_rings + sage: K. = FunctionField(k) + sage: R. = K[] + sage: L. = K.extension(y^3 - t) + sage: L.separable_model() (Function field in y defined by y^3 + t, Function Field endomorphism of Function field in y defined by y^3 + t Defn: y |--> y @@ -1425,9 +1437,9 @@ def separable_model(self, names=None): Some other cases for which a separable model could be constructed are not supported yet:: - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - t) # optional - sage.rings.finite_rings - sage: L.separable_model() # optional - sage.rings.finite_rings + sage: R. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(y^2 - t) # needs sage.rings.finite_rings + sage: L.separable_model() # needs sage.rings.finite_rings Traceback (most recent call last): ... NotImplementedError: constructing a separable model is only implemented for function fields over a perfect constant base field @@ -1450,12 +1462,13 @@ def separable_model(self, names=None): Check that this works for towers of inseparable extensions:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings - sage: M.separable_model() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: M.separable_model() (Function field in z_ defined by z_ + x_^4, Function Field morphism: From: Function field in z_ defined by z_ + x_^4 @@ -1471,12 +1484,13 @@ def separable_model(self, names=None): Check that this also works if only the first extension is inseparable:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings - sage: R. = L[] # optional - sage.rings.finite_rings - sage: M. = L.extension(z^3 - y) # optional - sage.rings.finite_rings - sage: M.separable_model() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: R. = L[] + sage: M. = L.extension(z^3 - y) + sage: M.separable_model() (Function field in z_ defined by z_ + x_^6, Function Field morphism: From: Function field in z_ defined by z_ + x_^6 To: Function field in z defined by z^3 + y @@ -1655,9 +1669,9 @@ def _inversion_isomorphism(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: F._inversion_isomorphism() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # needs sage.rings.finite_rings + sage: F._inversion_isomorphism() # needs sage.rings.finite_rings (Function field in s defined by s^3 + x^16 + x^14 + x^12, Composite map: From: Function field in s defined by s^3 + x^16 + x^14 + x^12 To: Function field in y defined by y^3 + x^6 + x^4 + x^2 @@ -1712,9 +1726,9 @@ def places_above(self, p): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: all(q.place_below() == p # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # needs sage.rings.finite_rings + sage: all(q.place_below() == p # needs sage.rings.finite_rings ....: for p in K.places() for q in F.places_above(p)) True @@ -1726,12 +1740,13 @@ def places_above(self, p): ....: for p in pls for q in F.places_above(p)) True - sage: K. = FunctionField(QQbar); _. = K[] # optional - sage.rings.number_field - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.number_field - sage: O = K.maximal_order() # optional - sage.rings.number_field - sage: pls = [O.ideal(x - QQbar(sqrt(c))).place() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: K. = FunctionField(QQbar); _. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: O = K.maximal_order() + sage: pls = [O.ideal(x - QQbar(sqrt(c))).place() ....: for c in [-2, -1, 0, 1, 2]] - sage: all(q.place_below() == p # long time (4s) # optional - sage.rings.number_field + sage: all(q.place_below() == p # long time (4s) ....: for p in pls for q in F.places_above(p)) True """ @@ -1754,9 +1769,9 @@ def constant_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(3)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # optional - sage.rings.finite_rings - sage: L.constant_field() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # needs sage.rings.finite_rings + sage: L.constant_field() # needs sage.rings.finite_rings Finite Field of size 3 """ return self.exact_constant_field()[0] @@ -1771,17 +1786,18 @@ def exact_constant_field(self, name='t'): EXAMPLES:: - sage: K. = FunctionField(GF(3)); _. = K[] # optional - sage.rings.finite_rings - sage: f = Y^2 - x*Y + x^2 + 1 # irreducible but not absolutely irreducible # optional - sage.rings.finite_rings - sage: L. = K.extension(f) # optional - sage.rings.finite_rings - sage: L.genus() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3)); _. = K[] + sage: f = Y^2 - x*Y + x^2 + 1 # irreducible but not absolutely irreducible + sage: L. = K.extension(f) + sage: L.genus() 0 - sage: L.exact_constant_field() # optional - sage.rings.finite_rings + sage: L.exact_constant_field() (Finite Field in t of size 3^2, Ring morphism: From: Finite Field in t of size 3^2 To: Function field in y defined by y^2 + 2*x*y + x^2 + 1 Defn: t |--> y + x) - sage: (y+x).divisor() # optional - sage.rings.finite_rings + sage: (y+x).divisor() 0 """ # A basis of the full constant field is obtained from @@ -1816,12 +1832,13 @@ def genus(self): EXAMPLES:: - sage: F. = GF(16) # optional - sage.rings.finite_rings - sage: K. = FunctionField(F); K # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = GF(16) + sage: K. = FunctionField(F); K Rational function field in x over Finite Field in a of size 2^4 - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings - sage: L.genus() # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4 + t - x^5) + sage: L.genus() 6 The genus is computed by the Hurwitz genus formula. @@ -1855,24 +1872,25 @@ def residue_field(self, place, name=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings - sage: R, fr_R, to_R = L.residue_field(p) # optional - sage.rings.finite_rings - sage: R # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: R, fr_R, to_R = L.residue_field(p) + sage: R Finite Field of size 2 - sage: f = 1 + y # optional - sage.rings.finite_rings - sage: f.valuation(p) # optional - sage.rings.finite_rings + sage: f = 1 + y + sage: f.valuation(p) -1 - sage: to_R(f) # optional - sage.rings.finite_rings + sage: to_R(f) Traceback (most recent call last): ... TypeError: ... - sage: (1+1/f).valuation(p) # optional - sage.rings.finite_rings + sage: (1+1/f).valuation(p) 0 - sage: to_R(1 + 1/f) # optional - sage.rings.finite_rings + sage: to_R(1 + 1/f) 1 - sage: [fr_R(e) for e in R] # optional - sage.rings.finite_rings + sage: [fr_R(e) for e in R] [0, 1] """ return place.residue_field(name=name) @@ -1905,7 +1923,7 @@ def higher_derivation(self): sage: K. = FunctionField(QQ); _. = K[] sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L.higher_derivation() # optional - sage.modules + sage: L.higher_derivation() # needs sage.modules Higher derivation map: From: Function field in y defined by y^3 + (-x^3 + 1)/(x^3 - 2) To: Function field in y defined by y^3 + (-x^3 + 1)/(x^3 - 2) @@ -1926,23 +1944,23 @@ class FunctionField_global(FunctionField_simple): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings - sage: L # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(5)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # needs sage.rings.finite_rings + sage: L # needs sage.rings.finite_rings Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) The defining equation needs not be monic:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension((1 - x)*Y^7 - x^3) # optional - sage.rings.finite_rings - sage: L.gaps() # long time (6s) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension((1 - x)*Y^7 - x^3) # needs sage.rings.finite_rings + sage: L.gaps() # long time (6s) # needs sage.rings.finite_rings [1, 2, 3] or may define a trivial extension:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y-1) # optional - sage.rings.finite_rings - sage: L.genus() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(5)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y-1) # needs sage.rings.finite_rings + sage: L.genus() # needs sage.rings.finite_rings 0 """ _differentials_space = LazyImport('sage.rings.function_field.differential', 'DifferentialsSpace_global') @@ -1953,9 +1971,9 @@ def __init__(self, polynomial, names): TESTS:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings - sage: TestSuite(L).run() # long time (7s) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(5)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # needs sage.rings.finite_rings + sage: TestSuite(L).run() # long time (7s) # needs sage.rings.finite_rings """ FunctionField_polymod.__init__(self, polynomial, names) @@ -1965,11 +1983,12 @@ def maximal_order(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: O.basis() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = PolynomialRing(K) + sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) + sage: O = F.maximal_order() + sage: O.basis() (1, 1/x^4*y, 1/x^11*y^2 + 1/x^2, 1/x^15*y^3 + 1/x^6*y) """ from .order_polymod import FunctionFieldMaximalOrder_global @@ -1987,9 +2006,9 @@ def higher_derivation(self): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings - sage: L.higher_derivation() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(5)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # needs sage.rings.finite_rings + sage: L.higher_derivation() # needs sage.modules sage.rings.finite_rings Higher derivation map: From: Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) To: Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) @@ -2009,25 +2028,26 @@ def get_place(self, degree): EXAMPLES:: - sage: F. = GF(2) # optional - sage.rings.finite_rings - sage: K. = FunctionField(F) # optional - sage.rings.finite_rings - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^4 + Y - x^5) # optional - sage.rings.finite_rings - sage: L.get_place(1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(Y^4 + Y - x^5) + sage: L.get_place(1) Place (x, y) - sage: L.get_place(2) # optional - sage.rings.finite_rings + sage: L.get_place(2) Place (x, y^2 + y + 1) - sage: L.get_place(3) # optional - sage.rings.finite_rings + sage: L.get_place(3) Place (x^3 + x^2 + 1, y + x^2 + x) - sage: L.get_place(4) # optional - sage.rings.finite_rings + sage: L.get_place(4) Place (x + 1, x^5 + 1) - sage: L.get_place(5) # optional - sage.rings.finite_rings + sage: L.get_place(5) Place (x^5 + x^3 + x^2 + x + 1, y + x^4 + 1) - sage: L.get_place(6) # optional - sage.rings.finite_rings + sage: L.get_place(6) Place (x^3 + x^2 + 1, y^2 + y + x^2) - sage: L.get_place(7) # optional - sage.rings.finite_rings + sage: L.get_place(7) Place (x^7 + x + 1, y + x^6 + x^5 + x^4 + x^3 + x) - sage: L.get_place(8) # optional - sage.rings.finite_rings + sage: L.get_place(8) """ for p in self._places_finite(degree): @@ -2048,11 +2068,12 @@ def places(self, degree=1): EXAMPLES:: - sage: F. = GF(2) # optional - sage.rings.finite_rings - sage: K. = FunctionField(F) # optional - sage.rings.finite_rings - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings - sage: L.places(1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4 + t - x^5) + sage: L.places(1) [Place (1/x, 1/x^4*y^3), Place (x, y), Place (x, y + 1)] """ return self.places_infinite(degree) + self.places_finite(degree) @@ -2067,11 +2088,12 @@ def places_finite(self, degree=1): EXAMPLES:: - sage: F. = GF(2) # optional - sage.rings.finite_rings - sage: K. = FunctionField(F) # optional - sage.rings.finite_rings - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings - sage: L.places_finite(1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4 + t - x^5) + sage: L.places_finite(1) [Place (x, y), Place (x, y + 1)] """ return list(self._places_finite(degree)) @@ -2086,11 +2108,12 @@ def _places_finite(self, degree): EXAMPLES:: - sage: F. = GF(2) # optional - sage.rings.finite_rings - sage: K. = FunctionField(F) # optional - sage.rings.finite_rings - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings - sage: L._places_finite(1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4 + t - x^5) + sage: L._places_finite(1) """ O = self.maximal_order() @@ -2115,11 +2138,12 @@ def places_infinite(self, degree=1): EXAMPLES:: - sage: F. = GF(2) # optional - sage.rings.finite_rings - sage: K. = FunctionField(F) # optional - sage.rings.finite_rings - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings - sage: L.places_infinite(1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4 + t - x^5) + sage: L.places_infinite(1) [Place (1/x, 1/x^4*y^3)] """ return list(self._places_infinite(degree)) @@ -2134,11 +2158,12 @@ def _places_infinite(self, degree): EXAMPLES:: - sage: F. = GF(2) # optional - sage.rings.finite_rings - sage: K. = FunctionField(F) # optional - sage.rings.finite_rings - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings - sage: L._places_infinite(1) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4 + t - x^5) + sage: L._places_infinite(1) """ Oinf = self.maximal_order_infinite() @@ -2156,9 +2181,9 @@ def gaps(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings - sage: L.gaps() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # needs sage.rings.finite_rings + sage: L.gaps() # needs sage.modules sage.rings.finite_rings [1, 2, 3] """ return self._weierstrass_places()[1] @@ -2169,9 +2194,9 @@ def weierstrass_places(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings - sage: L.weierstrass_places() # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # needs sage.rings.finite_rings + sage: L.weierstrass_places() # needs sage.modules sage.rings.finite_rings [Place (1/x, 1/x^3*y^2 + 1/x), Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1), Place (x, y), @@ -2193,9 +2218,9 @@ def _weierstrass_places(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings - sage: len(L.weierstrass_places()) # indirect doctest # optional - sage.rings.finite_rings sage.modules + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # needs sage.rings.finite_rings + sage: len(L.weierstrass_places()) # indirect doctest # needs sage.modules sage.rings.finite_rings 10 This method implements Algorithm 30 in [Hes2002b]_. @@ -2241,9 +2266,9 @@ def L_polynomial(self, name='t'): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: F.L_polynomial() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: F. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings + sage: F.L_polynomial() # needs sage.rings.finite_rings 2*t^2 + t + 1 """ from sage.rings.integer_ring import ZZ @@ -2273,11 +2298,12 @@ def number_of_rational_places(self, r=1): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: F.number_of_rational_places() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(Y^2 + Y + x + 1/x) + sage: F.number_of_rational_places() 4 - sage: [F.number_of_rational_places(r) for r in [1..10]] # optional - sage.rings.finite_rings + sage: [F.number_of_rational_places(r) for r in [1..10]] [4, 8, 4, 16, 44, 56, 116, 288, 508, 968] """ from sage.rings.integer_ring import IntegerRing @@ -2368,10 +2394,11 @@ def _maximal_order_basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) # optional - sage.rings.finite_rings - sage: F._maximal_order_basis() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: R. = PolynomialRing(K) + sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) + sage: F._maximal_order_basis() [1, 1/x^4*y, 1/x^11*y^2 + 1/x^2, 1/x^15*y^3 + 1/x^6*y] The basis of the maximal order *always* starts with 1. This is assumed @@ -2466,9 +2493,9 @@ def equation_order(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: F.equation_order() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # needs sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # needs sage.rings.finite_rings + sage: F.equation_order() # needs sage.rings.finite_rings Order in Function field in y defined by y^3 + x^6 + x^4 + x^2 sage: K. = FunctionField(QQ); R. = PolynomialRing(K) @@ -2492,11 +2519,12 @@ def primitive_integal_element_infinite(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: b = F.primitive_integal_element_infinite(); b # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: b = F.primitive_integal_element_infinite(); b 1/x^2*y - sage: b.minimal_polynomial('t') # optional - sage.rings.finite_rings + sage: b.minimal_polynomial('t') t^3 + (x^4 + x^2 + 1)/x^4 """ f = self.polynomial() @@ -2519,9 +2547,9 @@ def equation_order_infinite(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: F.equation_order_infinite() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # needs sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # needs sage.rings.finite_rings + sage: F.equation_order_infinite() # needs sage.rings.finite_rings Infinite order in Function field in y defined by y^3 + x^6 + x^4 + x^2 sage: K. = FunctionField(QQ); R. = PolynomialRing(K) diff --git a/src/sage/rings/function_field/function_field_rational.py b/src/sage/rings/function_field/function_field_rational.py index 88d1c2cc0a3..42796b998bf 100644 --- a/src/sage/rings/function_field/function_field_rational.py +++ b/src/sage/rings/function_field/function_field_rational.py @@ -50,11 +50,11 @@ class RationalFunctionField(FunctionField): EXAMPLES:: - sage: K. = FunctionField(GF(3)); K # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)); K Rational function field in t over Finite Field of size 3 - sage: K.gen() # optional - sage.rings.finite_rings + sage: K.gen() t - sage: 1/t + t^3 + 5 # optional - sage.rings.finite_rings + sage: 1/t + t^3 + 5 (t^4 + 2*t + 1)/t sage: K. = FunctionField(QQ); K @@ -67,14 +67,14 @@ class RationalFunctionField(FunctionField): There are various ways to get at the underlying fields and rings associated to a rational function field:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: K.base_field() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: K.base_field() Rational function field in t over Finite Field of size 7 - sage: K.field() # optional - sage.rings.finite_rings + sage: K.field() Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 - sage: K.constant_field() # optional - sage.rings.finite_rings + sage: K.constant_field() Finite Field of size 7 - sage: K.maximal_order() # optional - sage.rings.finite_rings + sage: K.maximal_order() Maximal order of Rational function field in t over Finite Field of size 7 sage: K. = FunctionField(QQ) @@ -101,25 +101,26 @@ class RationalFunctionField(FunctionField): sage: R. = FunctionField(QQ) sage: L. = R[] - sage: F. = R.extension(y^2 - (x^2+1)) # optional - sage.rings.function_field - sage: (y/x).divisor() # optional - sage.rings.function_field sage.modules + sage: F. = R.extension(y^2 - (x^2+1)) # needs sage.rings.function_field + sage: (y/x).divisor() # needs sage.modules sage.rings.function_field - Place (x, y - 1) - Place (x, y + 1) + Place (x^2 + 1, y) - sage: A. = QQ[] # optional - sage.rings.number_field - sage: NF. = NumberField(z^2 + 1) # optional - sage.rings.number_field - sage: R. = FunctionField(NF) # optional - sage.rings.number_field - sage: L. = R[] # optional - sage.rings.number_field - sage: F. = R.extension(y^2 - (x^2+1)) # optional - sage.rings.function_field sage.modules sage.rings.number_field + sage: # needs sage.rings.number_field + sage: A. = QQ[] + sage: NF. = NumberField(z^2 + 1) + sage: R. = FunctionField(NF) + sage: L. = R[] + sage: F. = R.extension(y^2 - (x^2+1)) # needs sage.modules sage.rings.function_field - sage: (x/y*x.differential()).divisor() # optional - sage.rings.function_field sage.modules sage.rings.number_field + sage: (x/y*x.differential()).divisor() # needs sage.modules sage.rings.function_field sage.rings.number_field -2*Place (1/x, 1/x*y - 1) - 2*Place (1/x, 1/x*y + 1) + Place (x, y - 1) + Place (x, y + 1) - sage: (x/y).divisor() # optional - sage.rings.function_field sage.modules sage.rings.number_field + sage: (x/y).divisor() # needs sage.modules sage.rings.function_field sage.rings.number_field - Place (x - i, y) + Place (x, y - 1) + Place (x, y + 1) @@ -134,11 +135,11 @@ def __init__(self, constant_field, names, category=None): EXAMPLES:: - sage: K. = FunctionField(CC); K + sage: K. = FunctionField(CC); K # needs sage.rings.real_mpfr Rational function field in t over Complex Field with 53 bits of precision sage: TestSuite(K).run() # long time (5s) - sage: FunctionField(QQ[I], 'alpha') # optional - sage.rings.number_field + sage: FunctionField(QQ[I], 'alpha') # needs sage.rings.number_field Rational function field in alpha over Number Field in I with defining polynomial x^2 + 1 with I = 1*I @@ -251,8 +252,8 @@ def _element_constructor_(self, x): Some indirect test of conversion:: sage: S. = K[] - sage: I = S * [x^2 - y^2, y - t] # optional - sage.rings.function_field - sage: I.groebner_basis() # optional - sage.rings.function_field + sage: I = S * [x^2 - y^2, y - t] + sage: I.groebner_basis() # needs sage.rings.function_field [x^2 - t^2, y - t] """ @@ -339,10 +340,10 @@ def _to_bivariate_polynomial(self, f): EXAMPLES:: - sage: R. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: S. = R[] # optional - sage.rings.finite_rings - sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) # optional - sage.rings.finite_rings - sage: R._to_bivariate_polynomial(f) # optional - sage.rings.finite_rings + sage: R. = FunctionField(GF(7)) + sage: S. = R[] + sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) + sage: R._to_bivariate_polynomial(f) (X^7*t^2 - X^4*t^5 - X^3 + t^3, t^3) """ v = f.list() @@ -367,41 +368,43 @@ def _factor_univariate_polynomial(self, f, proof=None): sage: R. = FunctionField(QQ) sage: S. = R[] sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) - sage: f.factor() # indirect doctest # optional - sage.libs.singular + sage: f.factor() # indirect doctest # needs sage.libs.singular (1/t) * (X - t) * (X^2 - 1/t) * (X^2 + 1/t) * (X^2 + t*X + t^2) - sage: f.factor().prod() == f # optional - sage.libs.singular + sage: f.factor().prod() == f # needs sage.libs.singular True We do a factorization over a finite prime field:: - sage: R. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: S. = R[] # optional - sage.rings.finite_rings - sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) # optional - sage.rings.finite_rings - sage: f.factor() # optional - sage.rings.finite_rings + sage: R. = FunctionField(GF(7)) + sage: S. = R[] + sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) + sage: f.factor() # needs sage.libs.pari (1/t) * (X + 3*t) * (X + 5*t) * (X + 6*t) * (X^2 + 1/t) * (X^2 + 6/t) - sage: f.factor().prod() == f # optional - sage.rings.finite_rings + sage: f.factor().prod() == f # needs sage.libs.pari True Factoring over a function field over a non-prime finite field:: - sage: k. = GF(9) # optional - sage.rings.finite_rings - sage: R. = FunctionField(k) # optional - sage.rings.finite_rings - sage: S. = R[] # optional - sage.rings.finite_rings - sage: f = (1/t)*(X^3 - a*t^3) # optional - sage.rings.finite_rings - sage: f.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(9) + sage: R. = FunctionField(k) + sage: S. = R[] + sage: f = (1/t)*(X^3 - a*t^3) + sage: f.factor() (1/t) * (X + (a + 2)*t)^3 - sage: f.factor().prod() == f # optional - sage.rings.finite_rings + sage: f.factor().prod() == f True Factoring over a function field over a tower of finite fields:: - sage: k. = GF(4) # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: l. = k.extension(b^2 + b + a) # optional - sage.rings.finite_rings - sage: K. = FunctionField(l) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: F = t*x # optional - sage.rings.finite_rings - sage: F.factor(proof=False) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(4) + sage: R. = k[] + sage: l. = k.extension(b^2 + b + a) + sage: K. = FunctionField(l) + sage: R. = K[] + sage: F = t*x + sage: F.factor(proof=False) (x) * t """ @@ -450,18 +453,18 @@ def extension(self, f, names=None): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: K.extension(y^5 - x^3 - 3*x + x*y) # optional - sage.rings.function_field + sage: K.extension(y^5 - x^3 - 3*x + x*y) # needs sage.rings.function_field Function field in y defined by y^5 + x*y - x^3 - 3*x A nonintegral defining polynomial:: sage: K. = FunctionField(QQ); R. = K[] - sage: K.extension(y^3 + (1/t)*y + t^3/(t+1)) # optional - sage.rings.function_field + sage: K.extension(y^3 + (1/t)*y + t^3/(t+1)) # needs sage.rings.function_field Function field in y defined by y^3 + 1/t*y + t^3/(t + 1) The defining polynomial need not be monic or integral:: - sage: K.extension(t*y^3 + (1/t)*y + t^3/(t+1)) # optional - sage.rings.function_field + sage: K.extension(t*y^3 + (1/t)*y + t^3/(t+1)) # needs sage.rings.function_field Function field in y defined by t*y^3 + 1/t*y + t^3/(t + 1) """ from . import constructor @@ -517,19 +520,23 @@ def free_module(self, base=None, basis=None, map=True): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.free_module() # optional - sage.modules - (Vector space of dimension 1 over Rational function field in x over Rational Field, Isomorphism: + sage: K.free_module() # needs sage.modules + (Vector space of dimension 1 over Rational function field in x over Rational Field, + Isomorphism: From: Vector space of dimension 1 over Rational function field in x over Rational Field - To: Rational function field in x over Rational Field, Isomorphism: + To: Rational function field in x over Rational Field, + Isomorphism: From: Rational function field in x over Rational Field To: Vector space of dimension 1 over Rational function field in x over Rational Field) TESTS:: - sage: K.free_module() # optional - sage.modules - (Vector space of dimension 1 over Rational function field in x over Rational Field, Isomorphism: + sage: K.free_module() # needs sage.modules + (Vector space of dimension 1 over Rational function field in x over Rational Field, + Isomorphism: From: Vector space of dimension 1 over Rational function field in x over Rational Field - To: Rational function field in x over Rational Field, Isomorphism: + To: Rational function field in x over Rational Field, + Isomorphism: From: Rational function field in x over Rational Field To: Vector space of dimension 1 over Rational function field in x over Rational Field) @@ -625,8 +632,8 @@ def base_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: K.base_field() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: K.base_field() Rational function field in t over Finite Field of size 7 """ return self @@ -652,24 +659,25 @@ def hom(self, im_gens, base_morphism=None): We make a map from a rational function field to itself:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: K.hom((x^4 + 2)/x) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: K.hom((x^4 + 2)/x) Function Field endomorphism of Rational function field in x over Finite Field of size 7 Defn: x |--> (x^4 + 2)/x We construct a map from a rational function field into a non-rational extension field:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = K.hom(y^2 + y + 2); f # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^3 + 6*x^3 + x) + sage: f = K.hom(y^2 + y + 2); f Function Field morphism: From: Rational function field in x over Finite Field of size 7 To: Function field in y defined by y^3 + 6*x^3 + x Defn: x |--> y^2 + y + 2 - sage: f(x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f(x) y^2 + y + 2 - sage: f(x^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f(x^2) 5*y^2 + (x^3 + 6*x + 4)*y + 2*x^3 + 5*x + 4 """ if isinstance(im_gens, CategoryObject): @@ -692,8 +700,8 @@ def field(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: K.field() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: K.field() Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 .. SEEALSO:: @@ -770,7 +778,7 @@ def different(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.different() # optional - sage.modules + sage: K.different() # needs sage.modules 0 """ return self.divisor_group().zero() @@ -845,12 +853,12 @@ def residue_field(self, place, name=None): EXAMPLES:: - sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: p = F.places_finite(2)[0] # optional - sage.rings.finite_rings - sage: R, fr_R, to_R = F.residue_field(p) # optional - sage.rings.finite_rings - sage: R # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(5)) + sage: p = F.places_finite(2)[0] # needs sage.libs.pari + sage: R, fr_R, to_R = F.residue_field(p) # needs sage.libs.pari sage.rings.function_field + sage: R # needs sage.libs.pari sage.rings.function_field Finite Field in z2 of size 5^2 - sage: to_R(x) in R # optional - sage.rings.finite_rings + sage: to_R(x) in R # needs sage.libs.pari sage.rings.function_field True """ return place.residue_field(name=name) @@ -870,10 +878,10 @@ def higher_derivation(self): EXAMPLES:: sage: F. = FunctionField(QQ) - sage: d = F.higher_derivation() # optional - sage.modules - sage: [d(x^5,i) for i in range(10)] # optional - sage.modules + sage: d = F.higher_derivation() # needs sage.libs.singular sage.modules + sage: [d(x^5,i) for i in range(10)] # needs sage.libs.singular sage.modules [x^5, 5*x^4, 10*x^3, 10*x^2, 5*x, 1, 0, 0, 0, 0] - sage: [d(x^9,i) for i in range(10)] # optional - sage.modules + sage: [d(x^9,i) for i in range(10)] # needs sage.libs.singular sage.modules [x^9, 9*x^8, 36*x^7, 84*x^6, 126*x^5, 126*x^4, 84*x^3, 36*x^2, 9*x, 1] """ from .derivations_polymod import FunctionFieldHigherDerivation_char_zero @@ -896,8 +904,8 @@ def places(self, degree=1): EXAMPLES:: - sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: F.places() # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(5)) + sage: F.places() # needs sage.libs.pari [Place (1/x), Place (x), Place (x + 1), @@ -920,8 +928,8 @@ def places_finite(self, degree=1): EXAMPLES:: - sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: F.places_finite() # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(5)) + sage: F.places_finite() # needs sage.libs.pari [Place (x), Place (x + 1), Place (x + 2), Place (x + 3), Place (x + 4)] """ return list(self._places_finite(degree)) @@ -936,8 +944,8 @@ def _places_finite(self, degree=1): EXAMPLES:: - sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: F._places_finite() # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(5)) + sage: F._places_finite() """ O = self.maximal_order() @@ -955,8 +963,8 @@ def place_infinite(self): EXAMPLES:: - sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: F.place_infinite() # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(5)) + sage: F.place_infinite() Place (1/x) """ return self.maximal_order_infinite().prime_ideal().place() @@ -971,17 +979,17 @@ def get_place(self, degree): EXAMPLES:: - sage: F. = GF(2) # optional - sage.rings.finite_rings - sage: K. = FunctionField(F) # optional - sage.rings.finite_rings - sage: K.get_place(1) # optional - sage.rings.finite_rings + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: K.get_place(1) # needs sage.libs.pari Place (x) - sage: K.get_place(2) # optional - sage.rings.finite_rings + sage: K.get_place(2) # needs sage.libs.pari Place (x^2 + x + 1) - sage: K.get_place(3) # optional - sage.rings.finite_rings + sage: K.get_place(3) # needs sage.libs.pari Place (x^3 + x + 1) - sage: K.get_place(4) # optional - sage.rings.finite_rings + sage: K.get_place(4) # needs sage.libs.pari Place (x^4 + x + 1) - sage: K.get_place(5) # optional - sage.rings.finite_rings + sage: K.get_place(5) # needs sage.libs.pari Place (x^5 + x^2 + 1) """ @@ -999,11 +1007,11 @@ def higher_derivation(self): EXAMPLES:: - sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: d = F.higher_derivation() # optional - sage.rings.finite_rings - sage: [d(x^5,i) for i in range(10)] # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(5)) + sage: d = F.higher_derivation() # needs sage.rings.function_field + sage: [d(x^5,i) for i in range(10)] # needs sage.rings.function_field [x^5, 0, 0, 0, 0, 1, 0, 0, 0, 0] - sage: [d(x^7,i) for i in range(10)] # optional - sage.rings.finite_rings + sage: [d(x^7,i) for i in range(10)] # needs sage.rings.function_field [x^7, 2*x^6, x^5, 0, 0, x^2, 2*x, 1, 0, 0] """ from .derivations_polymod import RationalFunctionFieldHigherDerivation_global diff --git a/src/sage/rings/function_field/ideal.py b/src/sage/rings/function_field/ideal.py index 91e9e0519aa..22aa15299b1 100644 --- a/src/sage/rings/function_field/ideal.py +++ b/src/sage/rings/function_field/ideal.py @@ -26,46 +26,48 @@ Ideals in the equation order of an extension of a rational function field:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y); I # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) # needs sage.rings.function_field + sage: O = L.equation_order() # needs sage.rings.function_field + sage: I = O.ideal(y); I # needs sage.rings.function_field Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: I^2 # optional - sage.rings.function_field + sage: I^2 # needs sage.rings.function_field Ideal (x^3 + 1, (-x^3 - 1)*y) of Order in Function field in y defined by y^2 - x^3 - 1 Ideals in the maximal order of a global function field:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I^2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) # needs sage.rings.function_field + sage: O = L.maximal_order() # needs sage.rings.function_field + sage: I = O.ideal(y) # needs sage.rings.function_field + sage: I^2 # needs sage.rings.function_field Ideal (x) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: ~I # optional - sage.rings.finite_rings sage.rings.function_field + sage: ~I # needs sage.rings.function_field Ideal (1/x*y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: ~I * I # optional - sage.rings.finite_rings sage.rings.function_field + sage: ~I * I # needs sage.rings.function_field Ideal (1) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: J = O.ideal(x + y) * I # optional - sage.rings.finite_rings sage.rings.function_field - sage: J.factor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: J = O.ideal(x + y) * I # needs sage.rings.finite_rings sage.rings.function_field + sage: J.factor() # needs sage.rings.finite_rings sage.rings.function_field (Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x)^2 * (Ideal (x^3 + x + 1, y + x) of Maximal order of Function field in y defined by y^2 + x^3*y + x) Ideals in the maximal infinite order of a global function field:: - sage: K. = FunctionField(GF(3^2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings sage.rings.function_field - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I + I == I # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = K[] + sage: F. = K.extension(t^3 + t^2 - x^4) # needs sage.rings.function_field + sage: Oinf = F.maximal_order_infinite() # needs sage.rings.function_field + sage: I = Oinf.ideal(1/y) # needs sage.rings.function_field + sage: I + I == I True - sage: I^2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: I^2 # needs sage.rings.function_field Ideal (1/x^4*y) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: ~I # optional - sage.rings.finite_rings sage.rings.function_field + sage: ~I # needs sage.rings.function_field Ideal (y) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: ~I * I # optional - sage.rings.finite_rings sage.rings.function_field + sage: ~I * I # needs sage.rings.function_field Ideal (1) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: I.factor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.factor() # needs sage.rings.function_field (Ideal (1/x^3*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4)^4 AUTHORS: @@ -113,9 +115,9 @@ class FunctionFieldIdeal(Element): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: O = K.equation_order() # optional - sage.rings.finite_rings - sage: O.ideal(x^3 + 1) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: O = K.equation_order() + sage: O.ideal(x^3 + 1) Ideal (x^3 + 1) of Maximal order of Rational function field in x over Finite Field of size 7 """ def __init__(self, ring): @@ -124,10 +126,11 @@ def __init__(self, ring): TESTS:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: O = K.equation_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^3 + 1) # optional - sage.rings.finite_rings - sage: TestSuite(I).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: O = K.equation_order() + sage: I = O.ideal(x^3 + 1) + sage: TestSuite(I).run() """ Element.__init__(self, ring.ideal_monoid()) self._ring = ring @@ -139,11 +142,12 @@ def _repr_short(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings sage.rings.function_field - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I._repr_short() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = K[] + sage: F. = K.extension(t^3 + t^2 - x^4) # needs sage.rings.function_field + sage: Oinf = F.maximal_order_infinite() # needs sage.rings.function_field + sage: I = Oinf.ideal(1/y) # needs sage.rings.function_field + sage: I._repr_short() # needs sage.rings.function_field '(1/x^4*y^2)' """ if self.is_zero(): @@ -162,42 +166,47 @@ def _repr_(self): sage: I = O.ideal(x, 1/(x+1)); I Ideal (1/(x + 1)) of Maximal order of Rational function field in x over Rational Field + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.ideal(x^2 + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: O.ideal(x^2 + 1) Ideal (x^2 + 1) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y); I # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y); I Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y); I # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y); I Ideal (y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x/(x^2+1)) # optional - sage.rings.finite_rings - sage: I # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x/(x^2+1)) + sage: I Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings sage.rings.function_field - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: Oinf.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: Oinf.ideal(1/y) Ideal (1/x^4*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: Oinf.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.ideal(1/y) Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -212,11 +221,12 @@ def _latex_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: latex(I) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: latex(I) \left(y\right) """ return '\\left(' + ', '.join(latex(g) for g in self.gens_reduced()) + '\\right)' @@ -231,10 +241,10 @@ def _div_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: O = K.equation_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^3 + 1) # optional - sage.rings.finite_rings - sage: I / I # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: O = K.equation_order() + sage: I = O.ideal(x^3 + 1) + sage: I / I Ideal (1) of Maximal order of Rational function field in x over Finite Field of size 7 """ @@ -255,10 +265,10 @@ def gens_reduced(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: O = K.equation_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, x^2, x^2 + x) # optional - sage.rings.finite_rings - sage: I.gens_reduced() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: O = K.equation_order() + sage: I = O.ideal(x, x^2, x^2 + x) + sage: I.gens_reduced() (x,) """ gens = self.gens() @@ -277,10 +287,10 @@ def ring(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: O = K.equation_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, x^2, x^2 + x) # optional - sage.rings.finite_rings - sage: I.ring() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: O = K.equation_order() + sage: I = O.ideal(x, x^2, x^2 + x) + sage: I.ring() Maximal order of Rational function field in x over Finite Field of size 7 """ return self._ring @@ -291,11 +301,12 @@ def base_ring(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field - sage: I.base_ring() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(x^2 + 1) + sage: I.base_ring() Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ring() @@ -306,63 +317,68 @@ def place(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings - sage: I.place() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x^2 + x + 1) + sage: I.place() Traceback (most recent call last): ... TypeError: not a prime ideal - sage: I = O.ideal(x^3 + x + 1) # optional - sage.rings.finite_rings - sage: I.place() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^3 + x + 1) + sage: I.place() Place (x^3 + x + 1) - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings - sage: p = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: p.place() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: p = I.factor()[0][0] + sage: p.place() Place (1/x) - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: [f.place() for f,_ in I.factor()] # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: [f.place() for f,_ in I.factor()] [Place (x, (1/(x^3 + x^2 + x))*y^2), Place (x^2 + x + 1, (1/(x^3 + x^2 + x))*y^2)] - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: [f.place() for f,_ in I.factor()] # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: [f.place() for f,_ in I.factor()] [Place (x, x*y), Place (x + 1, x*y)] - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings sage.rings.function_field - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.factor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x^3*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4)^3 - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: J.is_prime() # optional - sage.rings.finite_rings sage.rings.function_field + sage: J = I.factor()[0][0] + sage: J.is_prime() True - sage: J.place() # optional - sage.rings.finite_rings sage.rings.function_field + sage: J.place() Place (1/x, 1/x^3*y^2) - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.factor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x)^2 - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: J.is_prime() # optional - sage.rings.finite_rings sage.rings.function_field + sage: J = I.factor()[0][0] + sage: J.is_prime() True - sage: J.place() # optional - sage.rings.finite_rings sage.rings.function_field + sage: J.place() Place (1/x, 1/x*y) """ if not self.is_prime(): @@ -380,49 +396,55 @@ def factor(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^3*(x + 1)^2) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x^3*(x + 1)^2) + sage: I.factor() (Ideal (x) of Maximal order of Rational function field in x over Finite Field in z2 of size 2^2)^3 * (Ideal (x + 1) of Maximal order of Rational function field in x over Finite Field in z2 of size 2^2)^2 - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: I.factor() (Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field in z2 of size 2^2)^2 - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(T^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I == I.factor().prod() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(T^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: I == I.factor().prod() True - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: f= 1/x # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = Oinf.ideal(f) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.factor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: Oinf = F.maximal_order_infinite() + sage: f= 1/x + sage: I = Oinf.ideal(f) + sage: I.factor() (Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1/x^2*y + 1) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2) * (Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2) + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: I == I.factor().prod() # optional - sage.rings.function_field + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: I == I.factor().prod() True + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: I == I.factor().prod() # optional - sage.rings.function_field + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: I == I.factor().prod() True """ @@ -434,42 +456,48 @@ def divisor(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) # optional - sage.rings.finite_rings - sage: I.divisor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) + sage: I.divisor() Place (x) + 2*Place (x + 1) - Place (x + z2) - Place (x + z2 + 1) - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings - sage: I.divisor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: I.divisor() 2*Place (1/x) - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(T^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(T^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: I.divisor() 2*Place (x, (1/(x^3 + x^2 + x))*y^2) + 2*Place (x^2 + x + 1, (1/(x^3 + x^2 + x))*y^2) - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(y) + sage: I.divisor() -2*Place (1/x, 1/x^4*y^2 + 1/x^2*y + 1) - 2*Place (1/x, 1/x^2*y + 1) - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: I.divisor() - Place (x, x*y) + 2*Place (x + 1, x*y) - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(y) + sage: I.divisor() - Place (1/x, 1/x*y) """ from .divisor import divisor @@ -487,23 +515,26 @@ def divisor_of_zeros(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) # optional - sage.rings.finite_rings - sage: I.divisor_of_zeros() # optional - sage.rings.finite_rings + sage: # needs sage.modules sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) + sage: I.divisor_of_zeros() Place (x) + 2*Place (x + 1) - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings - sage: I.divisor_of_zeros() # optional - sage.rings.finite_rings + sage: # needs sage.modules + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: I.divisor_of_zeros() 2*Place (1/x) - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.divisor_of_zeros() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: I.divisor_of_zeros() 2*Place (x + 1, x*y) """ from .divisor import divisor @@ -521,23 +552,26 @@ def divisor_of_poles(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) # optional - sage.rings.finite_rings - sage: I.divisor_of_poles() # optional - sage.rings.finite_rings + sage: # needs sage.modules sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) + sage: I.divisor_of_poles() Place (x + z2) + Place (x + z2 + 1) - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings - sage: I.divisor_of_poles() # optional - sage.rings.finite_rings + sage: # needs sage.modules + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: I.divisor_of_poles() 0 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I.divisor_of_poles() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: I.divisor_of_poles() Place (x, x*y) """ from .divisor import divisor @@ -565,13 +599,14 @@ class FunctionFieldIdeal_module(FunctionFieldIdeal, Ideal_generic): An ideal in an extension of a rational function field:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: I # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y) + sage: I Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: I^2 # optional - sage.rings.function_field + sage: I^2 Ideal (x^3 + 1, (-x^3 - 1)*y) of Order in Function field in y defined by y^2 - x^3 - 1 """ def __init__(self, ring, module): @@ -580,11 +615,12 @@ def __init__(self, ring, module): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: TestSuite(I).run() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y) + sage: TestSuite(I).run() """ FunctionFieldIdeal.__init__(self, ring) @@ -603,16 +639,17 @@ def __contains__(self, x): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y); I # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y); I Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: y in I # optional - sage.rings.function_field + sage: y in I True - sage: y/x in I # optional - sage.rings.function_field + sage: y/x in I False - sage: y^2 - 2 in I # optional - sage.rings.function_field + sage: y^2 - 2 in I False """ return self._structure[2](x) in self._module @@ -623,11 +660,12 @@ def __hash__(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: d = {I: 1} # indirect doctest # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y) + sage: d = {I: 1} # indirect doctest """ return hash((self._ring,self._module)) @@ -637,19 +675,20 @@ def _richcmp_(self, other, op): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(x, y); J = O.ideal(y^2 - 2) # optional - sage.rings.function_field - sage: I + J == J + I # indirect test # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(x, y); J = O.ideal(y^2 - 2) + sage: I + J == J + I # indirect test True - sage: I + I == I # indirect doctest # optional - sage.rings.function_field + sage: I + I == I # indirect doctest True - sage: I == J # optional - sage.rings.function_field + sage: I == J False - sage: I < J # optional - sage.rings.function_field + sage: I < J True - sage: J < I # optional - sage.rings.function_field + sage: J < I False """ return richcmp(self.module().basis(), other.module().basis(), op) @@ -668,21 +707,22 @@ def module(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order(); O # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order(); O Order in Function field in y defined by y^2 - x^3 - 1 - sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field - sage: I.gens() # optional - sage.rings.function_field + sage: I = O.ideal(x^2 + 1) + sage: I.gens() (x^2 + 1, (x^2 + 1)*y) - sage: I.module() # optional - sage.rings.function_field + sage: I.module() Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Rational Field Echelon basis matrix: [x^2 + 1 0] [ 0 x^2 + 1] - sage: V, from_V, to_V = L.vector_space(); V # optional - sage.rings.function_field + sage: V, from_V, to_V = L.vector_space(); V Vector space of dimension 2 over Rational function field in x over Rational Field - sage: I.module().is_submodule(V) # optional - sage.rings.function_field + sage: I.module().is_submodule(V) True """ return self._module @@ -693,11 +733,12 @@ def gens(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field - sage: I.gens() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(x^2 + 1) + sage: I.gens() (x^2 + 1, (x^2 + 1)*y) """ return self._gens @@ -708,11 +749,12 @@ def gen(self, i): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field - sage: I.gen(1) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(x^2 + 1) + sage: I.gen(1) (x^2 + 1)*y """ return self._gens[i] @@ -723,11 +765,12 @@ def ngens(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field - sage: I.ngens() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(x^2 + 1) + sage: I.ngens() 2 """ return len(self._gens) @@ -738,12 +781,13 @@ def _add_(self, other): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: J = O.ideal(x+y) # optional - sage.rings.function_field - sage: I + J # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x+y) + sage: I + J Ideal ((-x^2 + x)*y + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ring().ideal(self.gens() + other.gens()) @@ -754,12 +798,13 @@ def _mul_(self, other): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: J = O.ideal(x+y) # optional - sage.rings.function_field - sage: I * J # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x+y) + sage: I * J Ideal ((-x^5 + x^4 - x^2 + x)*y + x^3 + 1, (x^3 - x^2 + 1)*y) of Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ring().ideal([x*y for x in self.gens() for y in other.gens()]) @@ -770,11 +815,12 @@ def _acted_upon_(self, other, on_left): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: x * I # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y) + sage: x * I Ideal (x^4 + x, -x*y) of Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ring().ideal([other * x for x in self.gens()]) @@ -785,15 +831,16 @@ def intersection(self, other): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y^3); J = O.ideal(y^2) # optional - sage.rings.function_field - sage: Z = I.intersection(J); Z # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y^3); J = O.ideal(y^2) + sage: Z = I.intersection(J); Z Ideal (x^6 + 2*x^3 + 1, (-x^3 - 1)*y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: y^2 in Z # optional - sage.rings.function_field + sage: y^2 in Z False - sage: y^3 in Z # optional - sage.rings.function_field + sage: y^3 in Z True """ if not isinstance(other, FunctionFieldIdeal): @@ -815,15 +862,16 @@ def __invert__(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: ~I # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y) + sage: ~I Ideal (-1, (1/(x^3 + 1))*y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: I^-1 # optional - sage.rings.function_field + sage: I^-1 Ideal (-1, (1/(x^3 + 1))*y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: ~I * I # optional - sage.rings.function_field + sage: ~I * I Ideal (1) of Order in Function field in y defined by y^2 - x^3 - 1 """ if len(self.gens()) == 0: @@ -861,10 +909,11 @@ class FunctionFieldIdealInfinite_module(FunctionFieldIdealInfinite, Ideal_generi EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: O.ideal(y) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: O.ideal(y) Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 """ def __init__(self, ring, module): @@ -873,11 +922,12 @@ def __init__(self, ring, module): TESTS:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: I = O.ideal(y) # optional - sage.rings.function_field - sage: TestSuite(I).run() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal(y) + sage: TestSuite(I).run() """ FunctionFieldIdealInfinite.__init__(self, ring) @@ -901,16 +951,17 @@ def __contains__(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal_with_gens_over_base([1, y]); I # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal_with_gens_over_base([1, y]); I Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: y in I # optional - sage.rings.finite_rings sage.rings.function_field + sage: y in I True - sage: y/x in I # optional - sage.rings.finite_rings sage.rings.function_field + sage: y/x in I False - sage: y^2 - 2 in I # optional - sage.rings.finite_rings sage.rings.function_field + sage: y^2 - 2 in I True """ return self._structure[2](x) in self._module @@ -921,11 +972,12 @@ def __hash__(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal_with_gens_over_base([1, y]) # optional - sage.rings.finite_rings sage.rings.function_field - sage: d = {I: 2} # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal_with_gens_over_base([1, y]) + sage: d = {I: 2} # indirect doctest """ return hash((self._ring,self._module)) @@ -939,11 +991,12 @@ def __eq__(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal_with_gens_over_base([1, y]) # optional - sage.rings.finite_rings sage.rings.function_field - sage: I == I + I # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal_with_gens_over_base([1, y]) + sage: I == I + I # indirect doctest True """ if not isinstance(other, FunctionFieldIdeal_module): @@ -967,21 +1020,24 @@ def module(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order(); O # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: O = K.maximal_order(); O Maximal order of Rational function field in x over Finite Field of size 7 - sage: K.polynomial_ring() # optional - sage.rings.finite_rings - Univariate Polynomial Ring in x over Rational function field in x over Finite Field of size 7 - sage: I = O.ideal([x^2 + 1, x*(x^2+1)]) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: K.polynomial_ring() + Univariate Polynomial Ring in x over + Rational function field in x over Finite Field of size 7 + sage: I = O.ideal([x^2 + 1, x*(x^2+1)]) + sage: I.gens() (x^2 + 1,) - sage: I.module() # optional - sage.rings.finite_rings - Free module of degree 1 and rank 1 over Maximal order of Rational function field in x over Finite Field of size 7 + sage: I.module() # needs sage.modules + Free module of degree 1 and rank 1 over + Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: [x^2 + 1] - sage: V, from_V, to_V = K.vector_space(); V # optional - sage.rings.finite_rings - Vector space of dimension 1 over Rational function field in x over Finite Field of size 7 - sage: I.module().is_submodule(V) # optional - sage.rings.finite_rings + sage: V, from_V, to_V = K.vector_space(); V # needs sage.modules + Vector space of dimension 1 over + Rational function field in x over Finite Field of size 7 + sage: I.module().is_submodule(V) # needs sage.modules True """ return self._module @@ -997,9 +1053,9 @@ class IdealMonoid(UniqueRepresentation, Parent): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: M = O.ideal_monoid(); M # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: O = K.maximal_order() + sage: M = O.ideal_monoid(); M Monoid of ideals of Maximal order of Rational function field in x over Finite Field of size 2 """ @@ -1009,10 +1065,10 @@ def __init__(self, R): TESTS:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: M = O.ideal_monoid() # optional - sage.rings.finite_rings - sage: TestSuite(M).run() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: O = K.maximal_order() + sage: M = O.ideal_monoid() + sage: TestSuite(M).run() """ self.Element = R._ideal_class Parent.__init__(self, category=Monoids()) @@ -1026,9 +1082,9 @@ def _repr_(self): TESTS:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: M = O.ideal_monoid(); M._repr_() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: O = K.maximal_order() + sage: M = O.ideal_monoid(); M._repr_() 'Monoid of ideals of Maximal order of Rational function field in x over Finite Field of size 2' """ return "Monoid of ideals of %s" % self.__R @@ -1039,9 +1095,9 @@ def ring(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: M = O.ideal_monoid(); M.ring() is O # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: O = K.maximal_order() + sage: M = O.ideal_monoid(); M.ring() is O True """ return self.__R @@ -1052,12 +1108,12 @@ def _element_constructor_(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: M = O.ideal_monoid() # optional - sage.rings.finite_rings - sage: M(x) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: O = K.maximal_order() + sage: M = O.ideal_monoid() + sage: M(x) Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2 - sage: M([x-4, 1/x]) # optional - sage.rings.finite_rings + sage: M([x-4, 1/x]) Ideal (1/x) of Maximal order of Rational function field in x over Finite Field of size 2 """ try: # x is an ideal @@ -1072,12 +1128,12 @@ def _coerce_map_from_(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: M = O.ideal_monoid() # optional - sage.rings.finite_rings - sage: M.has_coerce_map_from(O) # indirect doctest # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: O = K.maximal_order() + sage: M = O.ideal_monoid() + sage: M.has_coerce_map_from(O) # indirect doctest True - sage: M.has_coerce_map_from(O.ideal_monoid()) # optional - sage.rings.finite_rings + sage: M.has_coerce_map_from(O.ideal_monoid()) True """ if isinstance(x, IdealMonoid): @@ -1091,10 +1147,10 @@ def _an_element_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: M = O.ideal_monoid() # optional - sage.rings.finite_rings - sage: M.an_element() # indirect doctest; random # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: O = K.maximal_order() + sage: M = O.ideal_monoid() + sage: M.an_element() # indirect doctest; random Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2 """ diff --git a/src/sage/rings/function_field/ideal_polymod.py b/src/sage/rings/function_field/ideal_polymod.py index 936b63b48fe..6735dd6a8fa 100644 --- a/src/sage/rings/function_field/ideal_polymod.py +++ b/src/sage/rings/function_field/ideal_polymod.py @@ -44,10 +44,11 @@ class FunctionFieldIdeal_polymod(FunctionFieldIdeal): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.ideal(y) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: O.ideal(y) Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x """ def __init__(self, ring, hnf, denominator=1): @@ -56,11 +57,12 @@ def __init__(self, ring, hnf, denominator=1): TESTS:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: TestSuite(I).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: TestSuite(I).run() """ FunctionFieldIdeal.__init__(self, ring) @@ -96,29 +98,31 @@ def __bool__(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y); I Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: I.is_zero() # optional - sage.rings.finite_rings + sage: I.is_zero() False - sage: J = 0*I; J # optional - sage.rings.finite_rings + sage: J = 0*I; J Zero ideal of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: J.is_zero() # optional - sage.rings.finite_rings + sage: J.is_zero() True - sage: K. = FunctionField(GF(2)); _.=K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _.=K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y); I Ideal (y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: I.is_zero() # optional - sage.rings.finite_rings + sage: I.is_zero() False - sage: J = 0*I; J # optional - sage.rings.finite_rings + sage: J = 0*I; J Zero ideal of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: J.is_zero() # optional - sage.rings.finite_rings + sage: J.is_zero() True """ return self._hnf.nrows() != 0 @@ -129,17 +133,19 @@ def __hash__(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: { I: 2 }[I] == 2 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: { I: 2 }[I] == 2 True - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: { I: 2 }[I] == 2 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: { I: 2 }[I] == 2 True """ return hash((self._ring, self._hnf, self._denominator)) @@ -150,30 +156,32 @@ def __contains__(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(7)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal([y]); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); _. = K[] + sage: L. = K.extension(Y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal([y]); I Ideal (y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: x * y in I # optional - sage.rings.finite_rings + sage: x * y in I True - sage: y / x in I # optional - sage.rings.finite_rings + sage: y / x in I False - sage: y^2 - 2 in I # optional - sage.rings.finite_rings + sage: y^2 - 2 in I False - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal([y]); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal([y]); I Ideal (y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: x * y in I # optional - sage.rings.finite_rings + sage: x * y in I True - sage: y / x in I # optional - sage.rings.finite_rings + sage: y / x in I False - sage: y^2 - 2 in I # optional - sage.rings.finite_rings + sage: y^2 - 2 in I False sage: K. = FunctionField(QQ); _. = K[] @@ -219,30 +227,32 @@ def __invert__(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: ~I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); _. = K[] + sage: L. = K.extension(Y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: ~I Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I^(-1) # optional - sage.rings.finite_rings + sage: I^(-1) Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: ~I * I # optional - sage.rings.finite_rings + sage: ~I * I Ideal (1) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 :: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: ~I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: ~I Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: I^(-1) # optional - sage.rings.finite_rings + sage: I^(-1) Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: ~I * I # optional - sage.rings.finite_rings + sage: ~I * I Ideal (1) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x :: @@ -291,28 +301,30 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: I == I + I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: I == I + I True - sage: I == I * I # optional - sage.rings.finite_rings + sage: I == I * I False :: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: I == I + I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: I == I + I True - sage: I == I * I # optional - sage.rings.finite_rings + sage: I == I * I False - sage: I < I * I # optional - sage.rings.finite_rings + sage: I < I * I True - sage: I > I * I # optional - sage.rings.finite_rings + sage: I > I * I False """ return richcmp((self._denominator, self._hnf), (other._denominator, other._hnf), op) @@ -323,19 +335,21 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: I + J Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: I + J Ideal (1, y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ ds = self._denominator @@ -350,20 +364,22 @@ def _mul_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: I * J Ideal (x^4 + x^2 + x, x*y + x^2) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: I * J Ideal ((x + 1)*y + (x^2 + 1)/x) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -401,19 +417,21 @@ def _acted_upon_(self, other, on_left): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x) # optional - sage.rings.finite_rings - sage: x * I == I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: J = O.ideal(x) + sage: x * I == I * J True - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x) # optional - sage.rings.finite_rings - sage: x * I == I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: J = O.ideal(x) + sage: x * I == I * J True """ from sage.modules.free_module_element import vector @@ -441,12 +459,13 @@ def intersect(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x) # optional - sage.rings.finite_rings - sage: I.intersect(J) == I * J * (I + J)^-1 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: J = O.ideal(x) + sage: I.intersect(J) == I * J * (I + J)^-1 True """ @@ -486,10 +505,11 @@ def hnf(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y*(y+1)); I.hnf() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y*(y+1)); I.hnf() [x^6 + x^3 0] [ x^3 + 1 1] @@ -510,13 +530,14 @@ def denominator(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y/(y+1)) # optional - sage.rings.finite_rings - sage: d = I.denominator(); d # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y/(y+1)) + sage: d = I.denominator(); d x^3 - sage: d in O # optional - sage.rings.finite_rings + sage: d in O True :: @@ -540,11 +561,12 @@ def module(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.module() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: F. = K.extension(y^2 - x^3 - 1) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.module() Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: @@ -564,17 +586,19 @@ def gens_over_base(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_over_base() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens_over_base() (x^4 + x^2 + x, y + x) - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_over_base() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens_over_base() (x^3 + 1, y + x) """ gens, d = self._gens_over_base @@ -588,11 +612,12 @@ def _gens_over_base(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings - sage: I._gens_over_base # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: I._gens_over_base ([x, y], x) """ gens = [] @@ -609,17 +634,19 @@ def gens(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens() (x^4 + x^2 + x, y + x) - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens() (x^3 + 1, y + x) """ return self.gens_over_base() @@ -634,11 +661,12 @@ def basis_matrix(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.denominator() * I.basis_matrix() == I.hnf() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.denominator() * I.basis_matrix() == I.hnf() True """ d = self.denominator() @@ -654,24 +682,26 @@ def is_integral(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.is_integral() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.is_integral() False - sage: J = I.denominator() * I # optional - sage.rings.finite_rings - sage: J.is_integral() # optional - sage.rings.finite_rings + sage: J = I.denominator() * I + sage: J.is_integral() True - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.is_integral() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.is_integral() False - sage: J = I.denominator() * I # optional - sage.rings.finite_rings - sage: J.is_integral() # optional - sage.rings.finite_rings + sage: J = I.denominator() * I + sage: J.is_integral() True sage: K. = FunctionField(QQ); _. = PolynomialRing(K) @@ -694,29 +724,31 @@ def ideal_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.ideal_below() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.ideal_below() Traceback (most recent call last): ... TypeError: not an integral ideal - sage: J = I.denominator() * I # optional - sage.rings.finite_rings - sage: J.ideal_below() # optional - sage.rings.finite_rings + sage: J = I.denominator() * I + sage: J.ideal_below() Ideal (x^3 + x^2 + x) of Maximal order of Rational function field in x over Finite Field of size 2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings - sage: I.ideal_below() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.ideal_below() Traceback (most recent call last): ... TypeError: not an integral ideal - sage: J = I.denominator() * I # optional - sage.rings.finite_rings - sage: J.ideal_below() # optional - sage.rings.finite_rings + sage: J = I.denominator() * I + sage: J.ideal_below() Ideal (x^3 + x) of Maximal order of Rational function field in x over Finite Field of size 2 @@ -760,38 +792,40 @@ def norm(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: i1 = O.ideal(x) # optional - sage.rings.finite_rings - sage: i2 = O.ideal(y) # optional - sage.rings.finite_rings - sage: i3 = i1 * i2 # optional - sage.rings.finite_rings - sage: i3.norm() == i1.norm() * i2.norm() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: i1 = O.ideal(x) + sage: i2 = O.ideal(y) + sage: i3 = i1 * i2 + sage: i3.norm() == i1.norm() * i2.norm() True - sage: i1.norm() # optional - sage.rings.finite_rings + sage: i1.norm() x^3 - sage: i1.norm() == x ** F.degree() # optional - sage.rings.finite_rings + sage: i1.norm() == x ** F.degree() True - sage: i2.norm() # optional - sage.rings.finite_rings + sage: i2.norm() x^6 + x^4 + x^2 - sage: i2.norm() == y.norm() # optional - sage.rings.finite_rings + sage: i2.norm() == y.norm() True - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: i1 = O.ideal(x) # optional - sage.rings.finite_rings - sage: i2 = O.ideal(y) # optional - sage.rings.finite_rings - sage: i3 = i1 * i2 # optional - sage.rings.finite_rings - sage: i3.norm() == i1.norm() * i2.norm() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: i1 = O.ideal(x) + sage: i2 = O.ideal(y) + sage: i3 = i1 * i2 + sage: i3.norm() == i1.norm() * i2.norm() True - sage: i1.norm() # optional - sage.rings.finite_rings + sage: i1.norm() x^2 - sage: i1.norm() == x ** L.degree() # optional - sage.rings.finite_rings + sage: i1.norm() == x ** L.degree() True - sage: i2.norm() # optional - sage.rings.finite_rings + sage: i2.norm() (x^2 + 1)/x - sage: i2.norm() == y.norm() # optional - sage.rings.finite_rings + sage: i2.norm() == y.norm() True """ n = 1 @@ -806,18 +840,20 @@ def is_prime(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.is_prime() for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: [f.is_prime() for f,_ in I.factor()] [True, True] - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.is_prime() for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: [f.is_prime() for f,_ in I.factor()] [True, True] sage: K. = FunctionField(QQ); _. = PolynomialRing(K) @@ -854,21 +890,23 @@ def valuation(self, ideal): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x, (1/(x^3 + x^2 + x))*y^2) # optional - sage.rings.finite_rings - sage: I.is_prime() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, (1/(x^3 + x^2 + x))*y^2) + sage: I.is_prime() True - sage: J = O.ideal(y) # optional - sage.rings.finite_rings - sage: I.valuation(J) # optional - sage.rings.finite_rings + sage: J = O.ideal(y) + sage: I.valuation(J) 2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.valuation(I) for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: [f.valuation(I) for f,_ in I.factor()] [-1, 2] The method closely follows Algorithm 4.8.17 of [Coh1993]_. @@ -923,20 +961,22 @@ def prime_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.prime_below() for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: [f.prime_below() for f,_ in I.factor()] [Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2, Ideal (x^2 + x + 1) of Maximal order of Rational function field in x over Finite Field of size 2] - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: [f.prime_below() for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: [f.prime_below() for f,_ in I.factor()] [Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2, Ideal (x + 1) of Maximal order of Rational function field in x over Finite Field of size 2] @@ -956,11 +996,12 @@ def _factor(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: I == I.factor().prod() # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: I == I.factor().prod() # indirect doctest True """ O = self.ring() @@ -999,10 +1040,11 @@ class FunctionFieldIdeal_global(FunctionFieldIdeal_polymod): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.ideal(y) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: O.ideal(y) Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x """ def __init__(self, ring, hnf, denominator=1): @@ -1011,11 +1053,12 @@ def __init__(self, ring, hnf, denominator=1): TESTS:: - sage: K. = FunctionField(GF(5)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: TestSuite(I).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(5)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: TestSuite(I).run() """ FunctionFieldIdeal_polymod.__init__(self, ring, hnf, denominator) @@ -1028,18 +1071,19 @@ def __pow__(self, mod): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^7 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: S = I / J # optional - sage.rings.finite_rings - sage: a = S^100 # optional - sage.rings.finite_rings - sage: _ = S.gens_two() # optional - sage.rings.finite_rings - sage: b = S^100 # faster # optional - sage.rings.finite_rings - sage: b == I^100 / J^100 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^7 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: J = O.ideal(x + y) + sage: S = I / J + sage: a = S^100 + sage: _ = S.gens_two() + sage: b = S^100 # faster + sage: b == I^100 / J^100 True - sage: b == a # optional - sage.rings.finite_rings + sage: b == a True """ if mod > 2 and self._gens_two_vecs is not None: @@ -1074,17 +1118,19 @@ def gens(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 - x^3*Y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens() (x^4 + x^2 + x, y + x) - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(x + y) + sage: I.gens() (x^3 + 1, y + x) """ if self._gens_two.is_in_cache(): @@ -1100,25 +1146,27 @@ def gens_two(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: I # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: I # indirect doctest Ideal (y) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - sage: ~I # indirect doctest # optional - sage.rings.finite_rings + sage: ~I # indirect doctest Ideal ((1/(x^6 + x^4 + x^2))*y^2) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y) # optional - sage.rings.finite_rings - sage: I # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: I # indirect doctest Ideal (y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: ~I # indirect doctest # optional - sage.rings.finite_rings + sage: ~I # indirect doctest Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1153,17 +1201,19 @@ def _gens_two(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2, x*y, x + y) # optional - sage.rings.finite_rings - sage: I._gens_two() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] + sage: F. = K.extension(Y^3 + x^3*Y + x) + sage: O = F.maximal_order() + sage: I = O.ideal(x^2, x*y, x + y) + sage: I._gens_two() (x, y) - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y - x) # optional - sage.rings.finite_rings - sage: y.zeros()[0].prime_ideal()._gens_two() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: _. = K[] + sage: L. = K.extension(Y - x) + sage: y.zeros()[0].prime_ideal()._gens_two() (x,) """ O = self.ring() @@ -1264,10 +1314,11 @@ class FunctionFieldIdealInfinite_polymod(FunctionFieldIdealInfinite): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.ideal(1/y) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: Oinf.ideal(1/y) Ideal (1/x^4*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 """ @@ -1277,11 +1328,12 @@ def __init__(self, ring, ideal): TESTS:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings - sage: TestSuite(I).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/y) + sage: TestSuite(I).run() """ FunctionFieldIdealInfinite.__init__(self, ring) self._ideal = ideal @@ -1292,17 +1344,19 @@ def __hash__(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings - sage: d = { I: 1 } # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/y) + sage: d = { I: 1 } - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings - sage: d = { I: 1 } # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/y) + sage: d = { I: 1 } """ return hash((self.ring(), self._ideal)) @@ -1316,15 +1370,16 @@ def __contains__(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings - sage: 1/y in I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/y) + sage: 1/y in I True - sage: 1/x in I # optional - sage.rings.finite_rings + sage: 1/x in I False - sage: 1/x^2 in I # optional - sage.rings.finite_rings + sage: 1/x^2 in I True """ F = self.ring().fraction_field() @@ -1341,21 +1396,23 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I + J Ideal (1/x) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I + J Ideal (1/x) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1371,21 +1428,23 @@ def _mul_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I * J Ideal (1/x^7*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I * J Ideal (1/x^4*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1397,11 +1456,12 @@ def __pow__(self, n): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: J^3 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: J = Oinf.ideal(1/x) + sage: J^3 Ideal (1/x^3) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 """ @@ -1413,25 +1473,27 @@ def __invert__(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(y) # optional - sage.rings.finite_rings - sage: ~J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: J = Oinf.ideal(y) + sage: ~J Ideal (1/x^4*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: J * ~J # optional - sage.rings.finite_rings + sage: J * ~J Ideal (1) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(y) # optional - sage.rings.finite_rings - sage: ~J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: J = Oinf.ideal(y) + sage: ~J Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: J * ~J # optional - sage.rings.finite_rings + sage: J * ~J Ideal (1) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1443,32 +1505,34 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I * J == J * I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I * J == J * I True - sage: I + J == J # optional - sage.rings.finite_rings + sage: I + J == J True - sage: I + J == I # optional - sage.rings.finite_rings + sage: I + J == I False - sage: (I < J) == (not J < I) # optional - sage.rings.finite_rings + sage: (I < J) == (not J < I) True - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I * J == J * I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x^2*1/y) + sage: J = Oinf.ideal(1/x) + sage: I * J == J * I True - sage: I + J == J # optional - sage.rings.finite_rings + sage: I + J == J True - sage: I + J == I # optional - sage.rings.finite_rings + sage: I + J == I False - sage: (I < J) == (not J < I) # optional - sage.rings.finite_rings + sage: (I < J) == (not J < I) True """ return richcmp(self._ideal, other._ideal, op) @@ -1480,11 +1544,12 @@ def _relative_degree(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: [J._relative_degree for J,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: [J._relative_degree for J,_ in I.factor()] [1] """ if not self.is_prime(): @@ -1498,18 +1563,20 @@ def gens(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens() (x, y, 1/x^2*y^2) - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens() (x, y) """ F = self.ring().fraction_field() @@ -1522,18 +1589,20 @@ def gens_two(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_two() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens_two() (x, y) - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_two() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens_two() (x,) """ F = self.ring().fraction_field() @@ -1546,11 +1615,12 @@ def gens_over_base(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings - sage: I.gens_over_base() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = K[] + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(x + y) + sage: I.gens_over_base() (x, y, 1/x^2*y^2) """ F = self.ring().fraction_field() @@ -1563,11 +1633,12 @@ def ideal_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/y^2) # optional - sage.rings.finite_rings - sage: I.ideal_below() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = K[] + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/y^2) + sage: I.ideal_below() Ideal (x^3) of Maximal order of Rational function field in x over Finite Field in z2 of size 3^2 """ @@ -1579,30 +1650,32 @@ def is_prime(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x^3*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4)^3 - sage: I.is_prime() # optional - sage.rings.finite_rings + sage: I.is_prime() False - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: J.is_prime() # optional - sage.rings.finite_rings + sage: J = I.factor()[0][0] + sage: J.is_prime() True - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x)^2 - sage: I.is_prime() # optional - sage.rings.finite_rings + sage: I.is_prime() False - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: J.is_prime() # optional - sage.rings.finite_rings + sage: J = I.factor()[0][0] + sage: J.is_prime() True """ return self._ideal.is_prime() @@ -1614,31 +1687,33 @@ def prime_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x^3*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4)^3 - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: J.is_prime() # optional - sage.rings.finite_rings + sage: J = I.factor()[0][0] + sage: J.is_prime() True - sage: J.prime_below() # optional - sage.rings.finite_rings + sage: J.prime_below() Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field in z2 of size 3^2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: I.factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() (Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x)^2 - sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings - sage: J.is_prime() # optional - sage.rings.finite_rings + sage: J = I.factor()[0][0] + sage: J.is_prime() True - sage: J.prime_below() # optional - sage.rings.finite_rings + sage: J.prime_below() Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 """ @@ -1659,11 +1734,12 @@ def valuation(self, ideal): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings - sage: [f.valuation(I) for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(y) + sage: [f.valuation(I) for f,_ in I.factor()] [-1] """ if not self.is_prime(): @@ -1677,16 +1753,17 @@ def _factor(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: f = 1/x # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(f) # optional - sage.rings.finite_rings - sage: I._factor() # optional - sage.rings.finite_rings - [(Ideal (1/x, 1/x^4*y^2 + 1/x^2*y + 1) of Maximal infinite order of Function field in y - defined by y^3 + x^6 + x^4 + x^2, 1), - (Ideal (1/x, 1/x^2*y + 1) of Maximal infinite order of Function field in y - defined by y^3 + x^6 + x^4 + x^2, 1)] + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: Oinf = F.maximal_order_infinite() + sage: f = 1/x + sage: I = Oinf.ideal(f) + sage: I._factor() + [(Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1/x^2*y + 1) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2, + 1), + (Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2, + 1)] """ if self._ideal.is_prime.is_in_cache() and self._ideal.is_prime(): return [(self, 1)] diff --git a/src/sage/rings/function_field/ideal_rational.py b/src/sage/rings/function_field/ideal_rational.py index 06400bf31f1..e6abe1ff605 100644 --- a/src/sage/rings/function_field/ideal_rational.py +++ b/src/sage/rings/function_field/ideal_rational.py @@ -207,7 +207,7 @@ def is_prime(self): sage: K. = FunctionField(QQ) sage: O = K.maximal_order() sage: I = O.ideal(x^3 + x^2) - sage: [f.is_prime() for f,m in I.factor()] # optional - sage.rings.finite_rings + sage: [f.is_prime() for f,m in I.factor()] # needs sage.libs.pari [True, True] """ return self._gen.denominator() == 1 and self._gen.numerator().is_prime() @@ -222,13 +222,13 @@ def module(self): sage: K. = FunctionField(QQ) sage: O = K.maximal_order() sage: I = O.ideal(x^3 + x^2) - sage: I.module() # optional - sage.modules + sage: I.module() # needs sage.modules Free module of degree 1 and rank 1 over Maximal order of Rational function field in x over Rational Field Echelon basis matrix: [x^3 + x^2] sage: J = 0*I - sage: J.module() # optional - sage.modules + sage: J.module() # needs sage.modules Free module of degree 1 and rank 0 over Maximal order of Rational function field in x over Rational Field Echelon basis matrix: @@ -243,10 +243,11 @@ def gen(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2 + x) # optional - sage.rings.finite_rings - sage: I.gen() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x^2 + x) + sage: I.gen() x^2 + x """ return self._gen @@ -257,10 +258,11 @@ def gens(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2 + x) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x^2 + x) + sage: I.gens() (x^2 + x,) """ return (self._gen,) @@ -272,10 +274,11 @@ def gens_over_base(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2 + x) # optional - sage.rings.finite_rings - sage: I.gens_over_base() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x^2 + x) + sage: I.gens_over_base() (x^2 + x,) """ return (self._gen,) @@ -293,7 +296,7 @@ def valuation(self, ideal): sage: F. = FunctionField(QQ) sage: O = F.maximal_order() sage: I = O.ideal(x^2*(x^2+x+1)^3) - sage: [f.valuation(I) for f,_ in I.factor()] # optional - sage.rings.finite_rings + sage: [f.valuation(I) for f,_ in I.factor()] # needs sage.libs.pari [2, 3] """ if not self.is_prime(): @@ -316,13 +319,13 @@ def _valuation(self, ideal): sage: F. = FunctionField(QQ) sage: O = F.maximal_order() sage: p = O.ideal(x) - sage: p.valuation(O.ideal(x + 1)) # indirect doctest # optional - sage.rings.finite_rings + sage: p.valuation(O.ideal(x + 1)) # indirect doctest # needs sage.libs.pari 0 - sage: p.valuation(O.ideal(x^2)) # indirect doctest # optional - sage.rings.finite_rings + sage: p.valuation(O.ideal(x^2)) # indirect doctest # needs sage.libs.pari 2 - sage: p.valuation(O.ideal(1/x^3)) # indirect doctest # optional - sage.rings.finite_rings + sage: p.valuation(O.ideal(1/x^3)) # indirect doctest # needs sage.libs.pari -3 - sage: p.valuation(O.ideal(0)) # indirect doctest # optional - sage.rings.finite_rings + sage: p.valuation(O.ideal(0)) # indirect doctest # needs sage.libs.pari +Infinity """ return ideal.gen().valuation(self.gen()) @@ -334,10 +337,11 @@ def _factor(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^3*(x+1)^2) # optional - sage.rings.finite_rings - sage: I.factor() # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x^3*(x+1)^2) + sage: I.factor() # indirect doctest (Ideal (x) of Maximal order of Rational function field in x over Finite Field in z2 of size 2^2)^3 * (Ideal (x + 1) of Maximal order of Rational function field in x @@ -360,9 +364,9 @@ class FunctionFieldIdealInfinite_rational(FunctionFieldIdealInfinite): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.ideal(x) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: Oinf.ideal(x) Ideal (x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 """ def __init__(self, ring, gen): @@ -371,10 +375,11 @@ def __init__(self, ring, gen): TESTS:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x) # optional - sage.rings.finite_rings - sage: TestSuite(I).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x) + sage: TestSuite(I).run() """ FunctionFieldIdealInfinite.__init__(self, ring) self._gen = gen @@ -385,11 +390,12 @@ def __hash__(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings - sage: d = { I: 1, J: 2 } # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x) + sage: J = Oinf.ideal(1/x) + sage: d = { I: 1, J: 2 } """ return hash( (self.ring(), self._gen) ) @@ -423,11 +429,12 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x + 1) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(x^2 + x) # optional - sage.rings.finite_rings - sage: I + J == J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x + 1) + sage: J = Oinf.ideal(x^2 + x) + sage: I + J == J True """ return richcmp(self._gen, other._gen, op) @@ -442,11 +449,12 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x/(x^2+1)) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/(x+1)) # optional - sage.rings.finite_rings - sage: I + J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x/(x^2+1)) + sage: J = Oinf.ideal(1/(x+1)) + sage: I + J Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 """ @@ -462,11 +470,12 @@ def _mul_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x/(x^2+1)) # optional - sage.rings.finite_rings - sage: J = Oinf.ideal(1/(x+1)) # optional - sage.rings.finite_rings - sage: I * J # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x/(x^2+1)) + sage: J = Oinf.ideal(1/(x+1)) + sage: I * J Ideal (1/x^2) of Maximal infinite order of Rational function field in x over Finite Field of size 2 """ @@ -482,10 +491,11 @@ def _acted_upon_(self, other, on_left): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x/(x^2+1)) # optional - sage.rings.finite_rings - sage: x * I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x/(x^2+1)) + sage: x * I Ideal (1) of Maximal infinite order of Rational function field in x over Finite Field of size 2 """ @@ -497,10 +507,11 @@ def __invert__(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x/(x^2 + 1)) # optional - sage.rings.finite_rings - sage: ~I # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x/(x^2 + 1)) + sage: ~I # indirect doctest Ideal (x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 """ @@ -512,10 +523,11 @@ def is_prime(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x/(x^2 + 1)) # optional - sage.rings.finite_rings - sage: I.is_prime() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal(x/(x^2 + 1)) + sage: I.is_prime() True """ x = self._ring.fraction_field().gen() @@ -527,10 +539,11 @@ def gen(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) # optional - sage.rings.finite_rings - sage: I.gen() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) + sage: I.gen() 1/x^2 """ return self._gen @@ -541,10 +554,11 @@ def gens(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) # optional - sage.rings.finite_rings - sage: I.gens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) + sage: I.gens() (1/x^2,) """ return (self._gen,) @@ -556,10 +570,11 @@ def gens_over_base(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) # optional - sage.rings.finite_rings - sage: I.gens_over_base() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) + sage: I.gens_over_base() (1/x^2,) """ return (self._gen,) @@ -597,10 +612,11 @@ def _factor(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal((x+1)/(x^3+1)) # optional - sage.rings.finite_rings - sage: I._factor() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x+1)/(x^3+1)) + sage: I._factor() [(Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field of size 2, 2)] """ diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index fc1768df310..c90a532d6c5 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -9,17 +9,17 @@ sage: K.hom(1/x) Function Field endomorphism of Rational function field in x over Rational Field Defn: x |--> 1/x - sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field - sage: K.hom(y) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: K.hom(y) # needs sage.rings.function_field Function Field morphism: From: Rational function field in x over Rational Field To: Function field in y defined by y^2 - x Defn: x |--> y - sage: L.hom([y,x]) # optional - sage.rings.function_field + sage: L.hom([y,x]) # needs sage.rings.function_field Function Field endomorphism of Function field in y defined by y^2 - x Defn: y |--> y x |--> x - sage: L.hom([x,y]) # optional - sage.rings.function_field + sage: L.hom([x,y]) # needs sage.rings.function_field Traceback (most recent call last): ... ValueError: invalid morphism @@ -72,9 +72,9 @@ class FunctionFieldVectorSpaceIsomorphism(Morphism): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: isinstance(f, sage.rings.function_field.maps.FunctionFieldVectorSpaceIsomorphism) # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: isinstance(f, sage.rings.function_field.maps.FunctionFieldVectorSpaceIsomorphism) # needs sage.modules sage.rings.function_field True """ def _repr_(self) -> str: @@ -84,13 +84,13 @@ def _repr_(self) -> str: EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: f # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: f # needs sage.modules sage.rings.function_field Isomorphism: From: Vector space of dimension 2 over Rational function field in x over Rational Field To: Function field in y defined by y^2 - x*y + 4*x^3 - sage: t # optional - sage.modules sage.rings.function_field + sage: t # needs sage.modules sage.rings.function_field Isomorphism: From: Function field in y defined by y^2 - x*y + 4*x^3 To: Vector space of dimension 2 over Rational function field in x over Rational Field @@ -107,9 +107,9 @@ def is_injective(self) -> bool: EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: f.is_injective() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: f.is_injective() # needs sage.modules sage.rings.function_field True """ return True @@ -121,9 +121,9 @@ def is_surjective(self) -> bool: EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: f.is_surjective() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: f.is_surjective() # needs sage.modules sage.rings.function_field True """ return True @@ -144,11 +144,11 @@ def _richcmp_(self, other, op): sage: L = K.function_field() sage: f = K.coerce_map_from(L) - sage: K = QQbar['x'].fraction_field() # optional - sage.rings.number_field - sage: L = K.function_field() # optional - sage.rings.number_field - sage: g = K.coerce_map_from(L) # optional - sage.rings.number_field + sage: K = QQbar['x'].fraction_field() # needs sage.rings.number_field + sage: L = K.function_field() + sage: g = K.coerce_map_from(L) - sage: f == g # optional - sage.rings.number_field + sage: f == g # needs sage.rings.number_field False sage: f == f True @@ -187,8 +187,8 @@ class MapVectorSpaceToFunctionField(FunctionFieldVectorSpaceIsomorphism): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space(); f # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space(); f # needs sage.modules sage.rings.function_field Isomorphism: From: Vector space of dimension 2 over Rational function field in x over Rational Field To: Function field in y defined by y^2 - x*y + 4*x^3 @@ -198,8 +198,8 @@ def __init__(self, V, K): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space(); type(f) # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space(); type(f) # needs sage.modules sage.rings.function_field """ self._V = V @@ -219,18 +219,18 @@ def _call_(self, v): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: f(x*V.0 + (1/x^3)*V.1) # indirect doctest # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: f(x*V.0 + (1/x^3)*V.1) # indirect doctest # needs sage.modules sage.rings.function_field 1/x^3*y + x TESTS: Test that this map is a bijection for some random inputs:: - sage: R. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z^3 - y - x) # optional - sage.rings.function_field - sage: for F in [K, L, M]: # optional - sage.modules sage.rings.function_field + sage: R. = L[] # needs sage.rings.function_field + sage: M. = L.extension(z^3 - y - x) # needs sage.rings.function_field + sage: for F in [K, L, M]: # needs sage.modules sage.rings.function_field ....: for base in F._intermediate_fields(K): ....: V, f, t = F.vector_space(base) ....: for i in range(100): @@ -262,9 +262,9 @@ def domain(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: f.domain() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: f.domain() # needs sage.modules sage.rings.function_field Vector space of dimension 2 over Rational function field in x over Rational Field """ return self._V @@ -276,9 +276,9 @@ def codomain(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: f.codomain() # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: f.codomain() # needs sage.modules sage.rings.function_field Function field in y defined by y^2 - x*y + 4*x^3 """ return self._K @@ -291,8 +291,8 @@ class MapFunctionFieldToVectorSpace(FunctionFieldVectorSpaceIsomorphism): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space(); t # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space(); t # needs sage.modules sage.rings.function_field Isomorphism: From: Function field in y defined by y^2 - x*y + 4*x^3 To: Vector space of dimension 2 over Rational function field in x over Rational Field @@ -310,9 +310,9 @@ def __init__(self, K, V): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: TestSuite(t).run(skip="_test_category") # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: TestSuite(t).run(skip="_test_category") # needs sage.modules sage.rings.function_field """ self._V = V self._K = K @@ -327,18 +327,18 @@ def _call_(self, x): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field - sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field - sage: t(x + (1/x^3)*y) # indirect doctest # optional - sage.modules sage.rings.function_field + sage: L. = K.extension(y^2 - x*y + 4*x^3) # needs sage.rings.function_field + sage: V, f, t = L.vector_space() # needs sage.modules sage.rings.function_field + sage: t(x + (1/x^3)*y) # indirect doctest # needs sage.modules sage.rings.function_field (x, 1/x^3) TESTS: Test that this map is a bijection for some random inputs:: - sage: R. = L[] # optional - sage.rings.function_field - sage: M. = L.extension(z^3 - y - x) # optional - sage.rings.function_field - sage: for F in [K, L, M]: # optional - sage.modules sage.rings.function_field + sage: R. = L[] # needs sage.rings.function_field + sage: M. = L.extension(z^3 - y - x) # needs sage.rings.function_field + sage: for F in [K, L, M]: # needs sage.modules sage.rings.function_field ....: for base in F._intermediate_fields(K): ....: V, f, t = F.vector_space(base) ....: for i in range(100): @@ -391,10 +391,11 @@ def _repr_type(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = L.hom(y*2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f._repr_type() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^3 + 6*x^3 + x) # needs sage.rings.function_field + sage: f = L.hom(y*2) # needs sage.rings.function_field + sage: f._repr_type() # needs sage.rings.function_field 'Function Field' """ return "Function Field" @@ -405,10 +406,11 @@ def _repr_defn(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = L.hom(y*2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f._repr_defn() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^3 + 6*x^3 + x) # needs sage.rings.function_field + sage: f = L.hom(y*2) # needs sage.rings.function_field + sage: f._repr_defn() # needs sage.rings.function_field 'y |--> 2*y' """ a = '%s |--> %s' % (self.domain().variable_name(), self._im_gen) @@ -423,14 +425,15 @@ class FunctionFieldMorphism_polymod(FunctionFieldMorphism): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = L.hom(y*2); f # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^3 + 6*x^3 + x) # needs sage.rings.function_field + sage: f = L.hom(y*2); f # needs sage.rings.function_field Function Field endomorphism of Function field in y defined by y^3 + 6*x^3 + x Defn: y |--> 2*y - sage: factor(L.polynomial()) # optional - sage.rings.finite_rings sage.rings.function_field + sage: factor(L.polynomial()) # needs sage.rings.function_field y^3 + 6*x^3 + x - sage: f(y).charpoly('y') # optional - sage.rings.finite_rings sage.rings.function_field + sage: f(y).charpoly('y') # needs sage.rings.function_field y^3 + 6*x^3 + x """ def __init__(self, parent, im_gen, base_morphism): @@ -439,10 +442,11 @@ def __init__(self, parent, im_gen, base_morphism): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = L.hom(y*2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: TestSuite(f).run(skip="_test_category") # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^3 + 6*x^3 + x) # needs sage.rings.function_field + sage: f = L.hom(y*2) # needs sage.rings.function_field + sage: TestSuite(f).run(skip="_test_category") # needs sage.rings.function_field """ FunctionFieldMorphism.__init__(self, parent, im_gen, base_morphism) # Verify that the morphism is valid: @@ -458,11 +462,12 @@ def _call_(self, x): """ EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 + 6*x^3 + x); f = L.hom(y*2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: f(y/x + x^2/(x+1)) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^3 + 6*x^3 + x); f = L.hom(y*2) # needs sage.rings.function_field + sage: f(y/x + x^2/(x+1)) # indirect doctest # needs sage.rings.function_field 2/x*y + x^2/(x + 1) - sage: f(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f(y) # needs sage.rings.function_field 2*y """ v = x.list() @@ -482,8 +487,8 @@ def __init__(self, parent, im_gen, base_morphism): EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: f = K.hom(1/x); f # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: f = K.hom(1/x); f Function Field endomorphism of Rational function field in x over Finite Field of size 7 Defn: x |--> 1/x """ @@ -493,27 +498,29 @@ def _call_(self, x): """ EXAMPLES:: - sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings - sage: f = K.hom(1/x); f # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)) + sage: f = K.hom(1/x); f Function Field endomorphism of Rational function field in x over Finite Field of size 7 Defn: x |--> 1/x - sage: f(x+1) # indirect doctest # optional - sage.rings.finite_rings + sage: f(x+1) # indirect doctest (x + 1)/x - sage: 1/x + 1 # optional - sage.rings.finite_rings + sage: 1/x + 1 (x + 1)/x You can specify a morphism on the base ring:: - sage: Qi = GaussianIntegers().fraction_field() # optional - sage.rings.number_field - sage: i = Qi.gen() # optional - sage.rings.number_field - sage: K. = FunctionField(Qi) # optional - sage.rings.number_field - sage: phi1 = Qi.hom([CC.gen()]) # optional - sage.rings.number_field - sage: phi2 = Qi.hom([-CC.gen()]) # optional - sage.rings.number_field - sage: f = K.hom(CC.pi(), phi1) # optional - sage.rings.number_field - sage: f(1 + i + x) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: Qi = GaussianIntegers().fraction_field() + sage: i = Qi.gen() + sage: K. = FunctionField(Qi) + sage: phi1 = Qi.hom([CC.gen()]) + sage: phi2 = Qi.hom([-CC.gen()]) + sage: f = K.hom(CC.pi(), phi1) + sage: f(1 + i + x) 4.14159265358979 + 1.00000000000000*I - sage: g = K.hom(CC.pi(), phi2) # optional - sage.rings.number_field - sage: g(1 + i + x) # optional - sage.rings.number_field + sage: g = K.hom(CC.pi(), phi2) + sage: g(1 + i + x) 4.14159265358979 - 1.00000000000000*I """ a = x.element() @@ -717,35 +724,37 @@ class FunctionFieldCompletion(Map): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: m # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: m = L.completion(p) # needs sage.rings.function_field + sage: m # needs sage.rings.function_field Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(x) # needs sage.rings.function_field s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + s^9 + s^10 + s^12 + s^13 + s^15 + s^16 + s^17 + s^19 + O(s^22) - sage: m(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(y) # needs sage.rings.function_field s^-1 + 1 + s^3 + s^5 + s^7 + s^9 + s^13 + s^15 + s^17 + O(s^19) - sage: m(x*y) == m(x) * m(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(x*y) == m(x) * m(y) # needs sage.rings.function_field True - sage: m(x+y) == m(x) + m(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(x+y) == m(x) + m(y) # needs sage.rings.function_field True The variable name of the series can be supplied. If the place is not rational such that the residue field is a proper extension of the constant field, you can also specify the generator name of the extension:: - sage: p2 = L.places_finite(2)[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: p2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings sage.rings.function_field + sage: p2 = L.places_finite(2)[0] + sage: p2 Place (x^2 + x + 1, x*y + 1) - sage: m2 = L.completion(p2, 't', gen_name='b') # optional - sage.rings.finite_rings sage.rings.function_field - sage: m2(x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m2 = L.completion(p2, 't', gen_name='b') + sage: m2(x) (b + 1) + t + t^2 + t^4 + t^8 + t^16 + O(t^20) - sage: m2(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m2(y) b + b*t + b*t^3 + b*t^4 + (b + 1)*t^5 + (b + 1)*t^7 + b*t^9 + b*t^11 + b*t^12 + b*t^13 + b*t^15 + b*t^16 + (b + 1)*t^17 + (b + 1)*t^19 + O(t^20) """ @@ -755,11 +764,12 @@ def __init__(self, field, place, name=None, prec=None, gen_name=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: m # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: m = L.completion(p) # needs sage.rings.function_field + sage: m # needs sage.rings.function_field Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 @@ -793,11 +803,12 @@ def _repr_type(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: m # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: m = L.completion(p) # needs sage.rings.function_field + sage: m # indirect doctest # needs sage.rings.function_field Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 @@ -810,11 +821,12 @@ def _call_(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: m(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: m = L.completion(p) # needs sage.rings.function_field + sage: m(y) # needs sage.rings.function_field s^-1 + 1 + s^3 + s^5 + s^7 + s^9 + s^13 + s^15 + s^17 + O(s^19) """ if self._precision == infinity: @@ -828,11 +840,12 @@ def _call_with_args(self, f, args, kwds): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: m(x+y, 10) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: m = L.completion(p) # needs sage.rings.function_field + sage: m(x+y, 10) # indirect doctest # needs sage.rings.function_field s^-1 + 1 + s^2 + s^4 + s^8 + O(s^9) """ if self._precision == infinity: @@ -852,11 +865,12 @@ def _expand(self, f, prec=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: m(x, prec=20) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: m = L.completion(p) # needs sage.rings.function_field + sage: m(x, prec=20) # indirect doctest # needs sage.rings.function_field s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + s^9 + s^10 + s^12 + s^13 + s^15 + s^16 + s^17 + s^19 + O(s^22) """ @@ -886,15 +900,16 @@ def _expand_lazy(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p, prec=infinity) # optional - sage.rings.finite_rings sage.rings.function_field - sage: e = m(x); e # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: m = L.completion(p, prec=infinity) # needs sage.rings.function_field + sage: e = m(x); e # needs sage.rings.function_field s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + ... - sage: e.coefficient(99) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: e.coefficient(99) # indirect doctest # needs sage.rings.function_field 0 - sage: e.coefficient(100) # optional - sage.rings.finite_rings sage.rings.function_field + sage: e.coefficient(100) # needs sage.rings.function_field 1 """ place = self._place @@ -918,11 +933,12 @@ def default_precision(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field - sage: m.default_precision() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: m = L.completion(p) # needs sage.rings.function_field + sage: m.default_precision() # needs sage.rings.function_field 20 """ return self._precision @@ -938,14 +954,15 @@ def _repr_(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: R = p.valuation_ring() # optional - sage.rings.finite_rings sage.rings.function_field - sage: k, fr_k, to_k = R.residue_field() # optional - sage.rings.finite_rings sage.rings.function_field - sage: k # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: R = p.valuation_ring() # needs sage.rings.function_field + sage: k, fr_k, to_k = R.residue_field() # needs sage.rings.function_field + sage: k # needs sage.rings.function_field Finite Field of size 2 - sage: fr_k # optional - sage.rings.finite_rings sage.rings.function_field + sage: fr_k # needs sage.rings.function_field Ring morphism: From: Finite Field of size 2 To: Valuation ring at Place (x, x*y) @@ -966,13 +983,14 @@ def _repr_(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^2-x^3-1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(x - 2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: D = I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field - sage: V, from_V, to_V = D.function_space() # optional - sage.rings.finite_rings sage.rings.function_field - sage: from_V # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) # needs sage.rings.function_field + sage: O = F.maximal_order() # needs sage.rings.function_field + sage: I = O.ideal(x - 2) # needs sage.rings.function_field + sage: D = I.divisor() # needs sage.rings.function_field + sage: V, from_V, to_V = D.function_space() # needs sage.rings.function_field + sage: from_V # needs sage.rings.function_field Linear map: From: Vector space of dimension 2 over Finite Field of size 5 To: Function field in y defined by y^2 + 4*x^3 + 4 @@ -993,13 +1011,14 @@ def _repr_(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(x - 2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: D = I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field - sage: V, from_V, to_V = D.function_space() # optional - sage.rings.finite_rings sage.rings.function_field - sage: to_V # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2 - x^3 - 1) # needs sage.rings.function_field + sage: O = F.maximal_order() # needs sage.rings.function_field + sage: I = O.ideal(x - 2) # needs sage.rings.function_field + sage: D = I.divisor() # needs sage.rings.function_field + sage: V, from_V, to_V = D.function_space() # needs sage.rings.function_field + sage: to_V # needs sage.rings.function_field Section of linear map: From: Function field in y defined by y^2 + 4*x^3 + 4 To: Vector space of dimension 2 over Finite Field of size 5 diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py index 47d85622146..52732cad62a 100644 --- a/src/sage/rings/function_field/order.py +++ b/src/sage/rings/function_field/order.py @@ -30,49 +30,53 @@ `O` and one maximal infinite order `O_\infty`. There are other non-maximal orders such as equation orders:: - sage: K. = FunctionField(GF(3)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^3 - y - x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: 1/y in O # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(3)); R. = K[] + sage: L. = K.extension(y^3 - y - x) + sage: O = L.equation_order() + sage: 1/y in O False - sage: x/y in O # optional - sage.rings.finite_rings sage.rings.function_field + sage: x/y in O True Sage provides an extensive functionality for computations in maximal orders of function fields. For example, you can decompose a prime ideal of a rational function field in an extension:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: o = K.maximal_order() # optional - sage.rings.finite_rings - sage: p = o.ideal(x + 1) # optional - sage.rings.finite_rings - sage: p.is_prime() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: o = K.maximal_order() + sage: p = o.ideal(x + 1) + sage: p.is_prime() # needs sage.libs.pari True - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.decomposition(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: O.decomposition(p) [(Ideal (x + 1, y + 1) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), (Ideal (x + 1, (1/(x^3 + x^2 + x))*y^2 + y + 1) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2, 2, 1)] - sage: p1, relative_degree,ramification_index = O.decomposition(p)[1] # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1.parent() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: p1, relative_degree,ramification_index = O.decomposition(p)[1] + sage: p1.parent() Monoid of ideals of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - sage: relative_degree # optional - sage.rings.finite_rings sage.rings.function_field + sage: relative_degree 2 - sage: ramification_index # optional - sage.rings.finite_rings sage.rings.function_field + sage: ramification_index 1 When the base constant field is the algebraic field `\QQbar`, the only prime ideals of the maximal order of the rational function field are linear polynomials. :: - sage: K. = FunctionField(QQbar) # optional - sage.rings.number_field - sage: R. = K[] # optional - sage.rings.number_field - sage: L. = K.extension(y^2 - (x^3-x^2)) # optional - sage.rings.function_field sage.rings.number_field - sage: p = K.maximal_order().ideal(x) # optional - sage.rings.function_field sage.rings.number_field - sage: L.maximal_order().decomposition(p) # optional - sage.rings.function_field sage.rings.number_field + sage: # needs sage.rings.function_field sage.rings.number_field + sage: K. = FunctionField(QQbar) + sage: R. = K[] + sage: L. = K.extension(y^2 - (x^3-x^2)) + sage: p = K.maximal_order().ideal(x) + sage: L.maximal_order().decomposition(p) [(Ideal (1/x*y - I) of Maximal order of Function field in y defined by y^2 - x^3 + x^2, 1, 1), @@ -269,9 +273,9 @@ def _repr_(self): sage: FunctionField(QQ,'y').maximal_order_infinite() Maximal infinite order of Rational function field in y over Rational Field - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings sage.rings.function_field - sage: F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # needs sage.rings.function_field + sage: F.maximal_order_infinite() # needs sage.modules sage.rings.function_field Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 """ return "Maximal infinite order of %s"%(self.function_field(),) diff --git a/src/sage/rings/function_field/order_basis.py b/src/sage/rings/function_field/order_basis.py index 1b0a4434d2c..3452d961459 100644 --- a/src/sage/rings/function_field/order_basis.py +++ b/src/sage/rings/function_field/order_basis.py @@ -1,5 +1,4 @@ -# sage.doctest: optional - sage.modules (because __init__ constructs a vector space) -# some tests are marked # optional - sage.rings.finite_rings (because they use finite fields) +# sage.doctest: needs sage.modules (because __init__ constructs a vector space) r""" Orders of function fields: basis """ @@ -34,9 +33,9 @@ class FunctionFieldOrder_basis(FunctionFieldOrder): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order(); O # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # needs sage.rings.function_field + sage: O = L.equation_order(); O # needs sage.rings.function_field Order in Function field in y defined by y^4 + x*y + 4*x + 1 The basis only defines an order if the module it generates is closed under @@ -44,23 +43,26 @@ class FunctionFieldOrder_basis(FunctionFieldOrder): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # optional - sage.rings.function_field - sage: y.is_integral() # optional - sage.rings.function_field + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # needs sage.rings.function_field + sage: y.is_integral() # needs sage.rings.function_field False - sage: L.order(y) # optional - sage.rings.function_field + sage: L.order(y) # needs sage.rings.function_field Traceback (most recent call last): ... - ValueError: the module generated by basis (1, y, y^2, y^3, y^4) must be closed under multiplication + ValueError: the module generated by basis (1, y, y^2, y^3, y^4) + must be closed under multiplication The basis also has to be linearly independent and of the same rank as the degree of the function field of its elements (only checked when ``check`` is ``True``):: - sage: L.order(L(x)) # optional - sage.rings.function_field + sage: # needs sage.rings.function_field + sage: L.order(L(x)) Traceback (most recent call last): ... ValueError: basis (1, x, x^2, x^3, x^4) is not linearly independent - sage: sage.rings.function_field.order_basis.FunctionFieldOrder_basis((y,y,y^3,y^4,y^5)) # optional - sage.rings.function_field + sage: from sage.rings.function_field.order_basis import FunctionFieldOrder_basis + sage: FunctionFieldOrder_basis((y,y,y^3,y^4,y^5)) Traceback (most recent call last): ... ValueError: basis (y, y, y^3, y^4, 2*x*y + (x^4 + 1)/x) is not linearly independent @@ -71,10 +73,11 @@ def __init__(self, basis, check=True): TESTS:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: TestSuite(O).run() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: TestSuite(O).run() """ if len(basis) == 0: raise ValueError("basis must have positive length") @@ -156,13 +159,14 @@ def ideal_with_gens_over_base(self, gens): We construct some ideals in a nontrivial function field:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order(); O # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order(); O Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I = O.ideal_with_gens_over_base([1, y]); I # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal_with_gens_over_base([1, y]); I Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I.module() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.module() Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: @@ -171,14 +175,15 @@ def ideal_with_gens_over_base(self, gens): There is no check if the resulting object is really an ideal:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal_with_gens_over_base([y]); I # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal_with_gens_over_base([y]); I Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: y in I # optional - sage.rings.finite_rings sage.rings.function_field + sage: y in I True - sage: y^2 in I # optional - sage.rings.finite_rings sage.rings.function_field + sage: y^2 in I False """ F = self.function_field() @@ -211,16 +216,19 @@ def ideal(self, *gens): A fractional ideal of a nontrivial extension:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2 - 4) # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: S = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: S.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field - Ideal (1, (6/(x^3 + 1))*y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I2 = S.ideal(x^2 - 4); I2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: O = K.maximal_order() + sage: I = O.ideal(x^2 - 4) + + sage: # needs sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) + sage: S = L.equation_order() + sage: S.ideal(1/y) + Ideal (1, (6/(x^3 + 1))*y) of + Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: I2 = S.ideal(x^2 - 4); I2 Ideal (x^2 + 3) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I2 == S.ideal(I) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I2 == S.ideal(I) True """ if len(gens) == 1: @@ -240,10 +248,11 @@ def polynomial(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.polynomial() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: O.polynomial() y^4 + x*y + 4*x + 1 """ return self._field.polynomial() @@ -254,10 +263,11 @@ def basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.basis() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: O.basis() (1, y, y^2, y^3) """ return self._basis @@ -269,10 +279,11 @@ def free_module(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.free_module() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: O.free_module() Free module of degree 4 and rank 4 over Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: @@ -293,11 +304,12 @@ def coordinate_vector(self, e): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: f = (x + y)^3 # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.coordinate_vector(f) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: f = (x + y)^3 + sage: O.coordinate_vector(f) (x^3, 3*x^2, 3*x, 1) """ return self._module.coordinate_vector(self._to_module(e), check=False) @@ -316,25 +328,26 @@ class FunctionFieldOrderInfinite_basis(FunctionFieldOrderInfinite): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order_infinite(); O # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # needs sage.rings.function_field + sage: O = L.equation_order_infinite(); O # needs sage.rings.function_field Infinite order in Function field in y defined by y^4 + x*y + 4*x + 1 The basis only defines an order if the module it generates is closed under multiplication and contains the identity element (only checked when ``check`` is ``True``):: - sage: O = L.order_infinite_with_basis([1, y, 1/x^2*y^2, y^3]); O # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.order_infinite_with_basis([1, y, 1/x^2*y^2, y^3]); O # needs sage.rings.function_field Traceback (most recent call last): ... - ValueError: the module generated by basis (1, y, 1/x^2*y^2, y^3) must be closed under multiplication + ValueError: the module generated by basis (1, y, 1/x^2*y^2, y^3) + must be closed under multiplication The basis also has to be linearly independent and of the same rank as the degree of the function field of its elements (only checked when ``check`` is ``True``):: - sage: O = L.order_infinite_with_basis([1, y, 1/x^2*y^2, 1 + y]); O # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.order_infinite_with_basis([1, y, 1/x^2*y^2, 1 + y]); O # needs sage.rings.function_field Traceback (most recent call last): ... ValueError: The given basis vectors must be linearly independent. @@ -342,9 +355,10 @@ class FunctionFieldOrderInfinite_basis(FunctionFieldOrderInfinite): Note that 1 does not need to be an element of the basis, as long as it is in the module spanned by it:: - sage: O = L.order_infinite_with_basis([1 + 1/x*y, 1/x*y, 1/x^2*y^2, 1/x^3*y^3]); O # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: O = L.order_infinite_with_basis([1 + 1/x*y, 1/x*y, 1/x^2*y^2, 1/x^3*y^3]); O Infinite order in Function field in y defined by y^4 + x*y + 4*x + 1 - sage: O.basis() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.basis() (1/x*y + 1, 1/x*y, 1/x^2*y^2, 1/x^3*y^3) """ def __init__(self, basis, check=True): @@ -353,10 +367,11 @@ def __init__(self, basis, check=True): TESTS:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field - sage: TestSuite(O).run() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order_infinite() + sage: TestSuite(O).run() """ if len(basis) == 0: raise ValueError("basis must have positive length") @@ -446,28 +461,31 @@ def ideal_with_gens_over_base(self, gens): We construct some ideals in a nontrivial function field:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order(); O # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order(); O Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I = O.ideal_with_gens_over_base([1, y]); I # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal_with_gens_over_base([1, y]); I Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I.module() # optional - sage.rings.finite_rings sage.rings.function_field - Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 + sage: I.module() + Free module of degree 2 and rank 2 over + Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: [1 0] [0 1] There is no check if the resulting object is really an ideal:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal_with_gens_over_base([y]); I # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal_with_gens_over_base([y]); I Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: y in I # optional - sage.rings.finite_rings sage.rings.function_field + sage: y in I True - sage: y^2 in I # optional - sage.rings.finite_rings sage.rings.function_field + sage: y^2 in I False """ F = self.function_field() @@ -503,8 +521,8 @@ def ideal(self, *gens): sage: K. = FunctionField(QQ); R. = K[] sage: O = K.maximal_order_infinite() sage: I = O.ideal(x^2 - 4) - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: S = L.order_infinite_with_basis([1, 1/x^2*y]) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) # needs sage.rings.function_field + sage: S = L.order_infinite_with_basis([1, 1/x^2*y]) # needs sage.rings.function_field """ if len(gens) == 1: gens = gens[0] @@ -523,10 +541,10 @@ def polynomial(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.polynomial() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # needs sage.rings.function_field + sage: O = L.equation_order() # needs sage.rings.function_field + sage: O.polynomial() # needs sage.rings.function_field y^4 + x*y + 4*x + 1 """ return self._field.polynomial() @@ -537,10 +555,10 @@ def basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.basis() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # needs sage.rings.function_field + sage: O = L.equation_order() # needs sage.rings.function_field + sage: O.basis() # needs sage.rings.function_field (1, y, y^2, y^3) """ return self._basis @@ -552,10 +570,10 @@ def free_module(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O.free_module() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # needs sage.rings.function_field + sage: O = L.equation_order() # needs sage.rings.function_field + sage: O.free_module() # needs sage.rings.function_field Free module of degree 4 and rank 4 over Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py index a95c003188f..e4f7d6eece8 100644 --- a/src/sage/rings/function_field/order_polymod.py +++ b/src/sage/rings/function_field/order_polymod.py @@ -40,10 +40,11 @@ def __init__(self, field, ideal_class=FunctionFieldIdeal_polymod): TESTS:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: TestSuite(O).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.maximal_order() + sage: TestSuite(O).run() """ FunctionFieldMaximalOrder.__init__(self, field, ideal_class) @@ -135,26 +136,27 @@ def _element_constructor_(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 - x*Y + x^2 + 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: y in O # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^2 - x*Y + x^2 + 1) + sage: O = L.maximal_order() + sage: y in O True - sage: 1/y in O # optional - sage.rings.finite_rings + sage: 1/y in O False - sage: x in O # optional - sage.rings.finite_rings + sage: x in O True - sage: 1/x in O # optional - sage.rings.finite_rings + sage: 1/x in O False - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: 1 in O # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: 1 in O True - sage: y in O # optional - sage.rings.finite_rings + sage: y in O False - sage: x*y in O # optional - sage.rings.finite_rings + sage: x*y in O True - sage: x^2*y in O # optional - sage.rings.finite_rings + sage: x^2*y in O True """ F = self.function_field() @@ -177,13 +179,14 @@ def ideal_with_gens_over_base(self, gens): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order(); O # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order(); O Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I = O.ideal_with_gens_over_base([1, y]); I # optional - sage.rings.finite_rings + sage: I = O.ideal_with_gens_over_base([1, y]); I Ideal (1) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I.module() # optional - sage.rings.finite_rings + sage: I.module() Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: @@ -192,14 +195,15 @@ def ideal_with_gens_over_base(self, gens): There is no check if the resulting object is really an ideal:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.equation_order() # optional - sage.rings.finite_rings - sage: I = O.ideal_with_gens_over_base([y]); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.equation_order() + sage: I = O.ideal_with_gens_over_base([y]); I Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: y in I # optional - sage.rings.finite_rings + sage: y in I True - sage: y^2 in I # optional - sage.rings.finite_rings + sage: y^2 in I False """ return self._ideal_from_vectors([self.coordinate_vector(g) for g in gens]) @@ -215,16 +219,17 @@ def _ideal_from_vectors(self, vecs): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: v1 = O.coordinate_vector(x^3 + 1) # optional - sage.rings.finite_rings - sage: v2 = O.coordinate_vector(y) # optional - sage.rings.finite_rings - sage: v1 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: v1 = O.coordinate_vector(x^3 + 1) + sage: v2 = O.coordinate_vector(y) + sage: v1 (x^3 + 1, 0) - sage: v2 # optional - sage.rings.finite_rings + sage: v2 (0, 1) - sage: O._ideal_from_vectors([v1, v2]) # optional - sage.rings.finite_rings + sage: O._ideal_from_vectors([v1, v2]) Ideal (y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 """ @@ -248,18 +253,19 @@ def _ideal_from_vectors_and_denominator(self, vecs, d=1, check=True): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(y^2) # optional - sage.rings.finite_rings - sage: m = I.basis_matrix() # optional - sage.rings.finite_rings - sage: v1 = m[0] # optional - sage.rings.finite_rings - sage: v2 = m[1] # optional - sage.rings.finite_rings - sage: v1 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y^2) + sage: m = I.basis_matrix() + sage: v1 = m[0] + sage: v2 = m[1] + sage: v1 (x^3 + 1, 0) - sage: v2 # optional - sage.rings.finite_rings + sage: v2 (0, x^3 + 1) - sage: O._ideal_from_vectors([v1, v2]) # indirect doctest # optional - sage.rings.finite_rings + sage: O._ideal_from_vectors([v1, v2]) # indirect doctest Ideal (x^3 + 1) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 """ @@ -312,17 +318,18 @@ def ideal(self, *gens, **kwargs): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2 - 4) # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: S = L.maximal_order() # optional - sage.rings.finite_rings - sage: S.ideal(1/y) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: O = K.maximal_order() + sage: I = O.ideal(x^2 - 4) + sage: L. = K.extension(y^2 - x^3 - 1) + sage: S = L.maximal_order() + sage: S.ideal(1/y) Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I2 = S.ideal(x^2 - 4); I2 # optional - sage.rings.finite_rings + sage: I2 = S.ideal(x^2 - 4); I2 Ideal (x^2 + 3) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I2 == S.ideal(I) # optional - sage.rings.finite_rings + sage: I2 == S.ideal(I) True sage: K. = FunctionField(QQ); R. = K[] @@ -355,10 +362,11 @@ def polynomial(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.equation_order() # optional - sage.rings.finite_rings - sage: O.polynomial() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: O.polynomial() y^4 + x*y + 4*x + 1 sage: K. = FunctionField(QQ); R. = K[] @@ -376,10 +384,11 @@ def basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.equation_order() # optional - sage.rings.finite_rings - sage: O.basis() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: O.basis() (1, y, y^2, y^3) sage: K. = FunctionField(QQ) @@ -399,16 +408,17 @@ def gen(self, n=0): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.gen() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: O = L.maximal_order() + sage: O.gen() 1 - sage: O.gen(1) # optional - sage.rings.finite_rings + sage: O.gen(1) y - sage: O.gen(2) # optional - sage.rings.finite_rings + sage: O.gen(2) (1/(x^3 + x^2 + x))*y^2 - sage: O.gen(3) # optional - sage.rings.finite_rings + sage: O.gen(3) Traceback (most recent call last): ... IndexError: there are only 3 generators @@ -424,10 +434,11 @@ def ngens(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order() # optional - sage.rings.finite_rings - sage: Oinf.ngens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: Oinf = L.maximal_order() + sage: Oinf.ngens() 3 """ return len(self._basis) @@ -438,10 +449,11 @@ def free_module(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.free_module() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.maximal_order() + sage: O.free_module() Free module of degree 4 and rank 4 over Maximal order of Rational function field in x over Finite Field of size 7 User basis matrix: @@ -458,12 +470,13 @@ def coordinate_vector(self, e): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.coordinate_vector(y) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.maximal_order() + sage: O.coordinate_vector(y) (0, 1, 0, 0) - sage: O.coordinate_vector(x*y) # optional - sage.rings.finite_rings + sage: O.coordinate_vector(x*y) (0, x, 0, 0) sage: K. = FunctionField(QQ); R. = K[] @@ -506,10 +519,11 @@ def different(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.different() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.maximal_order() + sage: O.different() Ideal (y^3 + 2*x) of Maximal order of Function field in y defined by y^4 + x*y + 4*x + 1 """ @@ -522,10 +536,11 @@ def codifferent(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O.codifferent() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.maximal_order() + sage: O.codifferent() Ideal (1, (1/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y^3 + ((5*x^3 + 6*x^2 + x + 6)/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y^2 + ((x^3 + 2*x^2 + 2*x + 2)/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y @@ -542,10 +557,11 @@ def _codifferent_matrix(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: O._codifferent_matrix() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.maximal_order() + sage: O._codifferent_matrix() [ 4 0 0 4*x] [ 0 0 4*x 5*x + 3] [ 0 4*x 5*x + 3 0] @@ -573,12 +589,13 @@ def decomposition(self, ideal): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: o = K.maximal_order() # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: p = o.ideal(x + 1) # optional - sage.rings.finite_rings - sage: O.decomposition(p) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: o = K.maximal_order() + sage: O = F.maximal_order() + sage: p = o.ideal(x + 1) + sage: O.decomposition(p) [(Ideal (x + 1, y + 1) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), (Ideal (x + 1, (1/(x^3 + x^2 + x))*y^2 + y + 1) of Maximal order @@ -701,14 +718,14 @@ class FunctionFieldMaximalOrderInfinite_polymod(FunctionFieldMaximalOrderInfinit EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # needs sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # needs sage.rings.finite_rings + sage: F.maximal_order_infinite() # needs sage.rings.finite_rings Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.finite_rings + sage: L.maximal_order_infinite() # needs sage.rings.finite_rings Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ def __init__(self, field, category=None): @@ -717,10 +734,11 @@ def __init__(self, field, category=None): TESTS:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings - sage: O = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: TestSuite(O).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order_infinite() + sage: TestSuite(O).run() """ FunctionFieldMaximalOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_polymod) @@ -741,18 +759,19 @@ def _element_constructor_(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.basis() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.basis() (1, 1/x*y) - sage: 1 in Oinf # optional - sage.rings.finite_rings + sage: 1 in Oinf True - sage: 1/x*y in Oinf # optional - sage.rings.finite_rings + sage: 1/x*y in Oinf True - sage: x*y in Oinf # optional - sage.rings.finite_rings + sage: x*y in Oinf False - sage: 1/x in Oinf # optional - sage.rings.finite_rings + sage: 1/x in Oinf True """ F = self.function_field() @@ -776,18 +795,20 @@ def basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.basis() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.basis() (1, 1/x^2*y, (1/(x^4 + x^3 + x^2))*y^2) :: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.basis() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.basis() (1, 1/x*y) """ return self._basis @@ -800,16 +821,17 @@ def gen(self, n=0): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.gen() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.gen() 1 - sage: Oinf.gen(1) # optional - sage.rings.finite_rings + sage: Oinf.gen(1) 1/x^2*y - sage: Oinf.gen(2) # optional - sage.rings.finite_rings + sage: Oinf.gen(2) (1/(x^4 + x^3 + x^2))*y^2 - sage: Oinf.gen(3) # optional - sage.rings.finite_rings + sage: Oinf.gen(3) Traceback (most recent call last): ... IndexError: there are only 3 generators @@ -825,10 +847,11 @@ def ngens(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.ngens() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.ngens() 3 """ return len(self._basis) @@ -843,19 +866,21 @@ def ideal(self, *gens): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x, y); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(x, y); I Ideal (y) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 :: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(x, y); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(x, y); I Ideal (x) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -944,11 +969,12 @@ def _to_iF(self, I): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings - sage: Oinf._to_iF(I) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(y) + sage: Oinf._to_iF(I) Ideal (1, 1/x*s) of Maximal order of Function field in s defined by s^2 + x*s + x^3 + x """ @@ -965,10 +991,11 @@ def decomposition(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.decomposition() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: Oinf = F.maximal_order_infinite() + sage: Oinf.decomposition() [(Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), (Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1/x^2*y + 1) of Maximal infinite order @@ -976,10 +1003,11 @@ def decomposition(self): :: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.decomposition() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.decomposition() [(Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x, 1, 2)] @@ -1023,10 +1051,11 @@ def different(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf.different() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.different() Ideal (1/x) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -1044,10 +1073,11 @@ def _codifferent_matrix(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: Oinf._codifferent_matrix() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf._codifferent_matrix() [ 0 1/x] [ 1/x 1/x^2] """ @@ -1075,13 +1105,14 @@ def coordinate_vector(self, e): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: f = 1/y^2 # optional - sage.rings.finite_rings - sage: f in Oinf # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: f = 1/y^2 + sage: f in Oinf True - sage: Oinf.coordinate_vector(f) # optional - sage.rings.finite_rings + sage: Oinf.coordinate_vector(f) ((x^3 + x^2 + x)/(x^4 + 1), x^3/(x^4 + 1)) """ return self._module.coordinate_vector(self._to_module(e)) @@ -1097,9 +1128,9 @@ class FunctionFieldMaximalOrder_global(FunctionFieldMaximalOrder_polymod): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: L.maximal_order() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # needs sage.rings.finite_rings + sage: L.maximal_order() # needs sage.rings.finite_rings Maximal order of Function field in y defined by y^4 + x*y + 4*x + 1 """ @@ -1109,10 +1140,11 @@ def __init__(self, field): TESTS:: - sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: TestSuite(O).run() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.maximal_order() + sage: TestSuite(O).run() """ FunctionFieldMaximalOrder_polymod.__init__(self, field, ideal_class=FunctionFieldIdeal_global) @@ -1130,12 +1162,13 @@ def p_radical(self, prime): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2 * (x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: o = K.maximal_order() # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: p = o.ideal(x + 1) # optional - sage.rings.finite_rings - sage: O.p_radical(p) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: F. = K.extension(t^3 - x^2 * (x^2 + x + 1)^2) + sage: o = K.maximal_order() + sage: O = F.maximal_order() + sage: p = o.ideal(x + 1) + sage: O.p_radical(p) Ideal (x + 1) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2 """ @@ -1192,12 +1225,13 @@ def decomposition(self, ideal): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings - sage: o = K.maximal_order() # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: p = o.ideal(x + 1) # optional - sage.rings.finite_rings - sage: O.decomposition(p) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: o = K.maximal_order() + sage: O = F.maximal_order() + sage: p = o.ideal(x + 1) + sage: O.decomposition(p) [(Ideal (x + 1, y + 1) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), (Ideal (x + 1, (1/(x^3 + x^2 + x))*y^2 + y + 1) of Maximal order diff --git a/src/sage/rings/function_field/order_rational.py b/src/sage/rings/function_field/order_rational.py index 9a57abfafe5..ff701315f0f 100644 --- a/src/sage/rings/function_field/order_rational.py +++ b/src/sage/rings/function_field/order_rational.py @@ -38,9 +38,9 @@ class FunctionFieldMaximalOrder_rational(FunctionFieldMaximalOrder): EXAMPLES:: - sage: K. = FunctionField(GF(19)); K # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(19)); K Rational function field in t over Finite Field of size 19 - sage: R = K.maximal_order(); R # optional - sage.rings.finite_rings + sage: R = K.maximal_order(); R Maximal order of Rational function field in t over Finite Field of size 19 """ def __init__(self, field): @@ -99,9 +99,9 @@ def ideal_with_gens_over_base(self, gens): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field - sage: O = L.equation_order() # optional - sage.rings.function_field - sage: O.ideal_with_gens_over_base([x^3 + 1, -y]) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^3 - 1) # needs sage.rings.function_field + sage: O = L.equation_order() # needs sage.rings.function_field + sage: O.ideal_with_gens_over_base([x^3 + 1, -y]) # needs sage.rings.function_field Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ideal(gens) @@ -129,60 +129,62 @@ def _residue_field(self, ideal, name=None): EXAMPLES:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings - sage: R, fr_R, to_R = O._residue_field(I) # optional - sage.rings.finite_rings - sage: R # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: O = F.maximal_order() + sage: I = O.ideal(x^2 + x + 1) + sage: R, fr_R, to_R = O._residue_field(I) + sage: R Finite Field in z2 of size 2^2 - sage: [to_R(fr_R(e)) == e for e in R] # optional - sage.rings.finite_rings + sage: [to_R(fr_R(e)) == e for e in R] [True, True, True, True] - sage: [to_R(fr_R(e)).parent() is R for e in R] # optional - sage.rings.finite_rings + sage: [to_R(fr_R(e)).parent() is R for e in R] [True, True, True, True] - sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) # optional - sage.rings.finite_rings - sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) # optional - sage.rings.finite_rings + sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) + sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) True - sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) # optional - sage.rings.finite_rings + sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) True - sage: to_R(e1).parent() is R # optional - sage.rings.finite_rings + sage: to_R(e1).parent() is R True - sage: to_R(e2).parent() is R # optional - sage.rings.finite_rings + sage: to_R(e2).parent() is R True - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: I = O.ideal(x + 1) # optional - sage.rings.finite_rings - sage: R, fr_R, to_R = O._residue_field(I) # optional - sage.rings.finite_rings - sage: R # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: O = F.maximal_order() + sage: I = O.ideal(x + 1) + sage: R, fr_R, to_R = O._residue_field(I) + sage: R Finite Field of size 2 - sage: [to_R(fr_R(e)) == e for e in R] # optional - sage.rings.finite_rings + sage: [to_R(fr_R(e)) == e for e in R] [True, True] - sage: [to_R(fr_R(e)).parent() is R for e in R] # optional - sage.rings.finite_rings + sage: [to_R(fr_R(e)).parent() is R for e in R] [True, True] - sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) # optional - sage.rings.finite_rings - sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) # optional - sage.rings.finite_rings + sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) + sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) True - sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) # optional - sage.rings.finite_rings + sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) True - sage: to_R(e1).parent() is R # optional - sage.rings.finite_rings + sage: to_R(e1).parent() is R True - sage: to_R(e2).parent() is R # optional - sage.rings.finite_rings + sage: to_R(e2).parent() is R True sage: F. = FunctionField(QQ) sage: O = F.maximal_order() sage: I = O.ideal(x^2 + x + 1) - sage: R, fr_R, to_R = O._residue_field(I) # optional - sage.rings.number_field - sage: R # optional - sage.rings.number_field + sage: R, fr_R, to_R = O._residue_field(I) # needs sage.rings.number_field + sage: R # needs sage.rings.number_field Number Field in a with defining polynomial x^2 + x + 1 - sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) # optional - sage.rings.number_field - sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) # optional - sage.rings.number_field + sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) # needs sage.rings.number_field + sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) # needs sage.rings.number_field True - sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) # optional - sage.rings.number_field + sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) # needs sage.rings.number_field True - sage: to_R(e1).parent() is R # optional - sage.rings.number_field + sage: to_R(e1).parent() is R # needs sage.rings.number_field True - sage: to_R(e2).parent() is R # optional - sage.rings.number_field + sage: to_R(e2).parent() is R # needs sage.rings.number_field True sage: F. = FunctionField(QQ) @@ -255,32 +257,34 @@ def _residue_field_global(self, q, name=None): EXAMPLES:: - sage: k. = GF(4) # optional - sage.rings.finite_rings - sage: F. = FunctionField(k) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: O._ring # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(4) + sage: F. = FunctionField(k) + sage: O = F.maximal_order() + sage: O._ring Univariate Polynomial Ring in x over Finite Field in a of size 2^2 - sage: f = x^3 + x + 1 # optional - sage.rings.finite_rings - sage: _f = f.numerator() # optional - sage.rings.finite_rings - sage: _f.is_irreducible() # optional - sage.rings.finite_rings + sage: f = x^3 + x + 1 + sage: _f = f.numerator() + sage: _f.is_irreducible() True - sage: K, fr_K, to_K = O._residue_field_global(_f) # optional - sage.rings.finite_rings sage.modules - sage: K # optional - sage.rings.finite_rings sage.modules + sage: K, fr_K, to_K = O._residue_field_global(_f) # needs sage.modules + sage: K # needs sage.modules Finite Field in z6 of size 2^6 - sage: all(to_K(fr_K(e)) == e for e in K) # optional - sage.rings.finite_rings sage.modules + sage: all(to_K(fr_K(e)) == e for e in K) # needs sage.modules True - sage: k. = GF(2) # optional - sage.rings.finite_rings - sage: F. = FunctionField(k) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: O._ring # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(2) + sage: F. = FunctionField(k) + sage: O = F.maximal_order() + sage: O._ring # needs sage.libs.ntl Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: f = x^3 + x + 1 # optional - sage.rings.finite_rings - sage: _f = f.numerator() # optional - sage.rings.finite_rings - sage: _f.is_irreducible() # optional - sage.rings.finite_rings + sage: f = x^3 + x + 1 + sage: _f = f.numerator() + sage: _f.is_irreducible() True - sage: K, fr_K, to_K = O._residue_field_global(_f) # optional - sage.rings.finite_rings sage.modules - sage: all(to_K(fr_K(e)) == e for e in K) # optional - sage.rings.finite_rings sage.modules + sage: K, fr_K, to_K = O._residue_field_global(_f) # needs sage.modules + sage: all(to_K(fr_K(e)) == e for e in K) # needs sage.modules True """ @@ -341,9 +345,9 @@ def basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(19)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: O.basis() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(19)) + sage: O = K.maximal_order() + sage: O.basis() (1,) """ return self._basis @@ -429,9 +433,9 @@ class FunctionFieldMaximalOrderInfinite_rational(FunctionFieldMaximalOrderInfini EXAMPLES:: - sage: K. = FunctionField(GF(19)); K # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(19)); K Rational function field in t over Finite Field of size 19 - sage: R = K.maximal_order_infinite(); R # optional - sage.rings.finite_rings + sage: R = K.maximal_order_infinite(); R Maximal infinite order of Rational function field in t over Finite Field of size 19 """ def __init__(self, field, category=None): @@ -440,9 +444,9 @@ def __init__(self, field, category=None): TESTS:: - sage: K. = FunctionField(GF(19)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: TestSuite(O).run(skip='_test_gcd_vs_xgcd') # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(19)) + sage: O = K.maximal_order_infinite() + sage: TestSuite(O).run(skip='_test_gcd_vs_xgcd') """ FunctionFieldMaximalOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_rational, category=PrincipalIdealDomains().or_subcategory(category)) @@ -480,9 +484,9 @@ def basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(19)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order() # optional - sage.rings.finite_rings - sage: O.basis() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(19)) + sage: O = K.maximal_order() + sage: O.basis() (1,) """ return 1/self.function_field().gen() @@ -522,9 +526,9 @@ def prime_ideal(self): EXAMPLES:: - sage: K. = FunctionField(GF(19)) # optional - sage.rings.finite_rings - sage: O = K.maximal_order_infinite() # optional - sage.rings.finite_rings - sage: O.prime_ideal() # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(19)) + sage: O = K.maximal_order_infinite() + sage: O.prime_ideal() Ideal (1/t) of Maximal infinite order of Rational function field in t over Finite Field of size 19 """ diff --git a/src/sage/rings/function_field/place.py b/src/sage/rings/function_field/place.py index a1f5f101475..48ea2a2c32f 100644 --- a/src/sage/rings/function_field/place.py +++ b/src/sage/rings/function_field/place.py @@ -11,9 +11,9 @@ All rational places of a function field can be computed:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.places() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: L.places() # needs sage.rings.function_field [Place (1/x, 1/x^3*y^2 + 1/x), Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1), Place (x, y)] @@ -21,20 +21,20 @@ The residue field associated with a place is given as an extension of the constant field:: - sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings - sage: p = O.ideal(x^2 + x + 1).place() # optional - sage.rings.finite_rings - sage: k, fr_k, to_k = p.residue_field() # optional - sage.rings.finite_rings - sage: k # optional - sage.rings.finite_rings + sage: F. = FunctionField(GF(2)) + sage: O = F.maximal_order() + sage: p = O.ideal(x^2 + x + 1).place() # needs sage.libs.pari + sage: k, fr_k, to_k = p.residue_field() # needs sage.rings.function_field + sage: k Finite Field in z2 of size 2^2 The homomorphisms are between the valuation ring and the residue field:: - sage: fr_k # optional - sage.rings.finite_rings + sage: fr_k Ring morphism: From: Finite Field in z2 of size 2^2 To: Valuation ring at Place (x^2 + x + 1) - sage: to_k # optional - sage.rings.finite_rings + sage: to_k Ring morphism: From: Valuation ring at Place (x^2 + x + 1) To: Finite Field in z2 of size 2^2 @@ -77,9 +77,9 @@ class FunctionFieldPlace(Element): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: L.places_finite()[0] # needs sage.rings.function_field Place (x, y) """ def __init__(self, parent, prime): @@ -88,10 +88,10 @@ def __init__(self, parent, prime): TESTS:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: TestSuite(p).run() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: TestSuite(p).run() # needs sage.rings.function_field """ Element.__init__(self, parent) @@ -103,10 +103,10 @@ def __hash__(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: {p: 1} # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: {p: 1} # needs sage.rings.function_field {Place (x, y): 1} """ return hash((self.function_field(), self._prime)) @@ -117,10 +117,10 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: p # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: p # needs sage.rings.function_field Place (x, y) """ try: @@ -136,10 +136,10 @@ def _latex_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: latex(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) # needs sage.rings.function_field + sage: p = L.places_finite()[0] # needs sage.rings.function_field + sage: latex(p) # needs sage.rings.function_field \left(y\right) """ return self._prime._latex_() @@ -150,14 +150,15 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1, p2, p3 = L.places()[:3] # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1 < p2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: p1, p2, p3 = L.places()[:3] + sage: p1 < p2 True - sage: p2 < p1 # optional - sage.rings.finite_rings sage.rings.function_field + sage: p2 < p1 False - sage: p1 == p3 # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1 == p3 False """ from sage.rings.function_field.order import FunctionFieldOrderInfinite @@ -176,12 +177,13 @@ def _acted_upon_(self, other, self_on_left): EXAMPLES:: - sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings - sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(x + 1, y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: P = I.place() # optional - sage.rings.finite_rings sage.rings.function_field - sage: -3*P + 5*P # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(Y^2 - x^3 - 1) + sage: O = F.maximal_order() + sage: I = O.ideal(x + 1, y) + sage: P = I.place() + sage: -3*P + 5*P 2*Place (x + 1, y) """ if self_on_left: @@ -194,10 +196,11 @@ def _neg_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1, p2, p3 = L.places()[:3] # optional - sage.rings.finite_rings sage.rings.function_field - sage: -p1 + p2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: p1, p2, p3 = L.places()[:3] + sage: -p1 + p2 - Place (1/x, 1/x^3*y^2 + 1/x) + Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) """ @@ -210,10 +213,11 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1, p2, p3 = L.places()[:3] # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1 + p2 + p3 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: p1, p2, p3 = L.places()[:3] + sage: p1 + p2 + p3 Place (1/x, 1/x^3*y^2 + 1/x) + Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) + Place (x, y) @@ -227,10 +231,11 @@ def _sub_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1, p2 = L.places()[:2] # optional - sage.rings.finite_rings sage.rings.function_field - sage: p1 - p2 # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: p1, p2 = L.places()[:2] + sage: p1 - p2 Place (1/x, 1/x^3*y^2 + 1/x) - Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) """ @@ -246,14 +251,14 @@ def __radd__(self, other): EXAMPLES:: - sage: k. = GF(2) # optional - sage.rings.finite_rings - sage: K. = FunctionField(k) # optional - sage.rings.finite_rings - sage: sum(K.places_finite()) # optional - sage.rings.finite_rings + sage: k. = GF(2) + sage: K. = FunctionField(k) + sage: sum(K.places_finite()) # needs sage.libs.pari Place (x) + Place (x + 1) Note that this does not work, as wanted:: - sage: 0 + K.place_infinite() # optional - sage.rings.finite_rings + sage: 0 + K.place_infinite() Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for +: ... @@ -272,10 +277,10 @@ def function_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: p.function_field() == L # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.function_field + sage: p = L.places()[0] # needs sage.rings.function_field + sage: p.function_field() == L # needs sage.rings.function_field True """ return self.parent()._field @@ -286,10 +291,10 @@ def prime_ideal(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: p = L.places()[0] # optional - sage.rings.finite_rings sage.rings.function_field - sage: p.prime_ideal() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.function_field + sage: p = L.places()[0] # needs sage.rings.function_field + sage: p.prime_ideal() # needs sage.rings.function_field Ideal (1/x^3*y^2 + 1/x) of Maximal infinite order of Function field in y defined by y^3 + x^3*y + x """ @@ -301,12 +306,13 @@ def divisor(self, multiplicity=1): EXAMPLES:: - sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings - sage: F. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: I = O.ideal(x + 1, y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: P = I.place() # optional - sage.rings.finite_rings sage.rings.function_field - sage: P.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(Y^2 - x^3 - 1) + sage: O = F.maximal_order() + sage: I = O.ideal(x + 1, y) + sage: P = I.place() + sage: P.divisor() Place (x + 1, y) """ from .divisor import prime_divisor @@ -323,9 +329,9 @@ class PlaceSet(UniqueRepresentation, Parent): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.function_field + sage: L.place_set() # needs sage.rings.function_field Set of places of Function field in y defined by y^3 + x^3*y + x """ Element = FunctionFieldPlace @@ -336,10 +342,10 @@ def __init__(self, field): TESTS:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: places = L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field - sage: TestSuite(places).run() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.function_field + sage: places = L.place_set() # needs sage.rings.function_field + sage: TestSuite(places).run() # needs sage.rings.function_field """ self.Element = field._place_class Parent.__init__(self, category=Sets().Infinite()) @@ -352,9 +358,9 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.function_field + sage: L.place_set() # needs sage.rings.function_field Set of places of Function field in y defined by y^3 + x^3*y + x """ return "Set of places of {}".format(self._field) @@ -365,11 +371,12 @@ def _element_constructor_(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: places = L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field - sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field - sage: places(O.ideal(x, y)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: places = L.place_set() + sage: O = L.maximal_order() + sage: places(O.ideal(x, y)) Place (x, y) """ from .ideal import FunctionFieldIdeal @@ -385,10 +392,11 @@ def _an_element_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: places = L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field - sage: places.an_element() # random # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: places = L.place_set() + sage: places.an_element() # random Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2 """ @@ -408,10 +416,11 @@ def function_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: PS = L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field - sage: PS.function_field() == L # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: PS = L.place_set() + sage: PS.function_field() == L True """ return self._field diff --git a/src/sage/rings/function_field/place_polymod.py b/src/sage/rings/function_field/place_polymod.py index f3f7778b6aa..b1d525152f0 100644 --- a/src/sage/rings/function_field/place_polymod.py +++ b/src/sage/rings/function_field/place_polymod.py @@ -33,14 +33,15 @@ def place_below(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: OK = K.maximal_order() # optional - sage.rings.finite_rings - sage: OL = L.maximal_order() # optional - sage.rings.finite_rings - sage: p = OK.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings - sage: dec = OL.decomposition(p) # optional - sage.rings.finite_rings - sage: q = dec[0][0].place() # optional - sage.rings.finite_rings - sage: q.place_below() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: OK = K.maximal_order() + sage: OL = L.maximal_order() + sage: p = OK.ideal(x^2 + x + 1) + sage: dec = OL.decomposition(p) + sage: q = dec[0][0].place() + sage: q.place_below() Place (x^2 + x + 1) """ return self.prime_ideal().prime_below().place() @@ -51,14 +52,15 @@ def relative_degree(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: OK = K.maximal_order() # optional - sage.rings.finite_rings - sage: OL = L.maximal_order() # optional - sage.rings.finite_rings - sage: p = OK.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings - sage: dec = OL.decomposition(p) # optional - sage.rings.finite_rings - sage: q = dec[0][0].place() # optional - sage.rings.finite_rings - sage: q.relative_degree() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: OK = K.maximal_order() + sage: OL = L.maximal_order() + sage: p = OK.ideal(x^2 + x + 1) + sage: dec = OL.decomposition(p) + sage: q = dec[0][0].place() + sage: q.relative_degree() 1 """ return self._prime._relative_degree @@ -69,14 +71,15 @@ def degree(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: OK = K.maximal_order() # optional - sage.rings.finite_rings - sage: OL = L.maximal_order() # optional - sage.rings.finite_rings - sage: p = OK.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings - sage: dec = OL.decomposition(p) # optional - sage.rings.finite_rings - sage: q = dec[0][0].place() # optional - sage.rings.finite_rings - sage: q.degree() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: OK = K.maximal_order() + sage: OL = L.maximal_order() + sage: p = OK.ideal(x^2 + x + 1) + sage: dec = OL.decomposition(p) + sage: q = dec[0][0].place() + sage: q.degree() 2 """ return self.relative_degree() * self.place_below().degree() @@ -88,12 +91,13 @@ def is_infinite_place(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: pls = L.places() # optional - sage.rings.finite_rings - sage: [p.is_infinite_place() for p in pls] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: pls = L.places() + sage: [p.is_infinite_place() for p in pls] [True, True, False] - sage: [p.place_below() for p in pls] # optional - sage.rings.finite_rings + sage: [p.place_below() for p in pls] [Place (1/x), Place (1/x), Place (x)] """ return self.place_below().is_infinite_place() @@ -105,10 +109,11 @@ def local_uniformizer(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: pls = L.places() # optional - sage.rings.finite_rings - sage: [p.local_uniformizer().valuation(p) for p in pls] # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: pls = L.places() + sage: [p.local_uniformizer().valuation(p) for p in pls] [1, 1, 1, 1, 1] """ gens = self._prime.gens() @@ -123,16 +128,17 @@ def gaps(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: p = O.ideal(x,y).place() # optional - sage.rings.finite_rings - sage: p.gaps() # a Weierstrass place # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: O = L.maximal_order() + sage: p = O.ideal(x,y).place() + sage: p.gaps() # a Weierstrass place [1, 2, 4] - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings - sage: [p.gaps() for p in L.places()] # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # needs sage.rings.finite_rings + sage: [p.gaps() for p in L.places()] # needs sage.rings.finite_rings [[1, 2, 4], [1, 2, 4], [1, 2, 4]] """ if self.degree() == 1: @@ -150,16 +156,17 @@ def _gaps_rational(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: p = O.ideal(x, y).place() # optional - sage.rings.finite_rings - sage: p.gaps() # indirect doctest # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: O = L.maximal_order() + sage: p = O.ideal(x, y).place() + sage: p.gaps() # indirect doctest [1, 2, 4] - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: [p.gaps() for p in L.places()] # indirect doctest # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # needs sage.rings.finite_rings + sage: [p.gaps() for p in L.places()] # indirect doctest # needs sage.rings.finite_rings [[1, 2, 4], [1, 2, 4], [1, 2, 4]] """ from sage.matrix.constructor import matrix @@ -281,16 +288,17 @@ def _gaps_wronskian(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings - sage: O = L.maximal_order() # optional - sage.rings.finite_rings - sage: p = O.ideal(x, y).place() # optional - sage.rings.finite_rings - sage: p._gaps_wronskian() # a Weierstrass place # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: O = L.maximal_order() + sage: p = O.ideal(x, y).place() + sage: p._gaps_wronskian() # a Weierstrass place [1, 2, 4] - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings - sage: [p._gaps_wronskian() for p in L.places()] # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] # needs sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # needs sage.rings.finite_rings + sage: [p._gaps_wronskian() for p in L.places()] # needs sage.rings.finite_rings [[1, 2, 4], [1, 2, 4], [1, 2, 4]] """ from sage.matrix.constructor import matrix @@ -344,28 +352,29 @@ def residue_field(self, name=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings - sage: k, fr_k, to_k = p.residue_field() # optional - sage.rings.finite_rings - sage: k # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: k, fr_k, to_k = p.residue_field() + sage: k Finite Field of size 2 - sage: fr_k # optional - sage.rings.finite_rings + sage: fr_k Ring morphism: From: Finite Field of size 2 To: Valuation ring at Place (x, x*y) - sage: to_k # optional - sage.rings.finite_rings + sage: to_k Ring morphism: From: Valuation ring at Place (x, x*y) To: Finite Field of size 2 - sage: to_k(y) # optional - sage.rings.finite_rings + sage: to_k(y) Traceback (most recent call last): ... TypeError: y fails to convert into the map's domain Valuation ring at Place (x, x*y)... - sage: to_k(1/y) # optional - sage.rings.finite_rings + sage: to_k(1/y) 0 - sage: to_k(y/(1+y)) # optional - sage.rings.finite_rings + sage: to_k(y/(1+y)) 1 """ return self.valuation_ring().residue_field(name=name) @@ -383,21 +392,23 @@ def _residue_field(self, name=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings - sage: k,fr_k,to_k = p._residue_field() # optional - sage.rings.finite_rings - sage: k # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: k,fr_k,to_k = p._residue_field() + sage: k Finite Field of size 2 - sage: [fr_k(e) for e in k] # optional - sage.rings.finite_rings + sage: [fr_k(e) for e in k] [0, 1] :: - sage: K. = FunctionField(GF(9)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^3 + Y - x^4) # optional - sage.rings.finite_rings - sage: p = L.places()[-1] # optional - sage.rings.finite_rings - sage: p.residue_field() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(9)); _. = K[] + sage: L. = K.extension(Y^3 + Y - x^4) + sage: p = L.places()[-1] + sage: p.residue_field() (Finite Field in z2 of size 3^2, Ring morphism: From: Finite Field in z2 of size 3^2 To: Valuation ring at Place (x + 1, y + 2*z2), Ring morphism: @@ -428,11 +439,12 @@ def _residue_field(self, name=None): :: - sage: K. = FunctionField(QQbar); _. = K[] # optional - sage.rings.number_field - sage: L. = K.extension(Y^3 + Y - x^4) # optional - sage.rings.number_field - sage: O = K.maximal_order() # optional - sage.rings.number_field - sage: I = O.ideal(x) # optional - sage.rings.number_field - sage: [p.residue_field() for p in L.places_above(I.place())] # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: K. = FunctionField(QQbar); _. = K[] + sage: L. = K.extension(Y^3 + Y - x^4) + sage: O = K.maximal_order() + sage: I = O.ideal(x) + sage: [p.residue_field() for p in L.places_above(I.place())] [(Algebraic Field, Ring morphism: From: Algebraic Field To: Valuation ring at Place (x, y - I, y^2 + 1), Ring morphism: @@ -656,10 +668,11 @@ def valuation_ring(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings - sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings - sage: p.valuation_ring() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: p.valuation_ring() Valuation ring at Place (x, x*y) """ from .valuation_ring import FunctionFieldValuationRing diff --git a/src/sage/rings/function_field/place_rational.py b/src/sage/rings/function_field/place_rational.py index c73d704f387..b6fd87f4fc2 100644 --- a/src/sage/rings/function_field/place_rational.py +++ b/src/sage/rings/function_field/place_rational.py @@ -77,7 +77,7 @@ def residue_field(self, name=None): sage: F. = FunctionField(GF(2)) sage: O = F.maximal_order() sage: p = O.ideal(x^2 + x + 1).place() - sage: k, fr_k, to_k = p.residue_field() + sage: k, fr_k, to_k = p.residue_field() # needs sage.rings.function_field sage: k Finite Field in z2 of size 2^2 sage: fr_k @@ -169,7 +169,7 @@ def valuation_ring(self): EXAMPLES:: sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: L. = K.extension(Y^2 + Y + x + 1/x) # needs sage.rings.function_field sage: p = L.places_finite()[0] sage: p.valuation_ring() Valuation ring at Place (x, x*y) diff --git a/src/sage/rings/function_field/valuation.py b/src/sage/rings/function_field/valuation.py index 90d5a2dae3e..5eecbb38095 100644 --- a/src/sage/rings/function_field/valuation.py +++ b/src/sage/rings/function_field/valuation.py @@ -32,8 +32,8 @@ sage: v = K.valuation(x - 1); v (x - 1)-adic valuation sage: R. = K[] - sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field - sage: w = v.extensions(L); w # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: w = v.extensions(L); w # needs sage.rings.function_field [[ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation] @@ -56,9 +56,9 @@ sage: K. = FunctionField(QQ) sage: v = K.valuation(x - 1) sage: R. = K[] - sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field - sage: ws = v.extensions(L) # optional - sage.rings.function_field - sage: for w in ws: TestSuite(w).run(max_runs=100) # long time # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: ws = v.extensions(L) # needs sage.rings.function_field + sage: for w in ws: TestSuite(w).run(max_runs=100) # long time # needs sage.rings.function_field Run test suite for valuations that do not correspond to a classical place:: @@ -77,19 +77,19 @@ Run test suite for some other classical places over large ground fields:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: M. = FunctionField(K) # optional - sage.rings.finite_rings - sage: v = M.valuation(x^3 - t) # optional - sage.rings.finite_rings - sage: TestSuite(v).run(max_runs=10) # long time # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: M. = FunctionField(K) + sage: v = M.valuation(x^3 - t) + sage: TestSuite(v).run(max_runs=10) # long time Run test suite for extensions over the infinite place:: sage: K. = FunctionField(QQ) sage: v = K.valuation(1/x) sage: R. = K[] - sage: L. = K.extension(y^2 - 1/(x^2 + 1)) # optional - sage.rings.function_field - sage: w = v.extensions(L) # optional - sage.rings.function_field - sage: TestSuite(w).run() # long time # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - 1/(x^2 + 1)) # needs sage.rings.function_field + sage: w = v.extensions(L) # needs sage.rings.function_field + sage: TestSuite(w).run() # long time # needs sage.rings.function_field Run test suite for a valuation with `v(1/x) > 0` which does not come from a classical valuation of the infinite place:: @@ -106,14 +106,14 @@ sage: K. = FunctionField(QQ) sage: v = K.valuation(x^2 + 1) sage: L. = FunctionField(GaussianIntegers().fraction_field()) - sage: ws = v.extensions(L) # optional - sage.rings.function_field - sage: for w in ws: TestSuite(w).run(max_runs=100) # long time + sage: ws = v.extensions(L) # needs sage.rings.function_field + sage: for w in ws: TestSuite(w).run(max_runs=100) # long time # needs sage.rings.function_field Run test suite for a finite place with residual degree and ramification:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: L. = FunctionField(K) # optional - sage.rings.finite_rings - sage: v = L.valuation(x^6 - t) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: L. = FunctionField(K) + sage: v = L.valuation(x^6 - t) sage: TestSuite(v).run(max_runs=10) # long time Run test suite for a valuation which is backed by limit valuation:: @@ -122,8 +122,8 @@ sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) sage: v = K.valuation(x - 1) - sage: w = v.extension(L) # optional - sage.rings.function_field - sage: TestSuite(w).run() # long time # optional - sage.rings.function_field + sage: w = v.extension(L) # needs sage.rings.function_field + sage: TestSuite(w).run() # long time # needs sage.rings.function_field Run test suite for a valuation which sends an element to `-\infty`:: @@ -307,11 +307,13 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^3 + 1/x^3*y + 2/x^4) # optional - sage.rings.function_field - sage: v = K.valuation(x) # optional - sage.rings.function_field - sage: v.extensions(L) # optional - sage.rings.function_field - [[ (x)-adic valuation, v(y) = 1 ]-adic valuation (in Function field in y defined by y^3 + x*y + 2*x^2 after y |--> 1/x^2*y), - [ (x)-adic valuation, v(y) = 1/2 ]-adic valuation (in Function field in y defined by y^3 + x*y + 2*x^2 after y |--> 1/x^2*y)] + sage: L. = K.extension(y^3 + 1/x^3*y + 2/x^4) # needs sage.rings.function_field + sage: v = K.valuation(x) # needs sage.rings.function_field + sage: v.extensions(L) # needs sage.rings.function_field + [[ (x)-adic valuation, v(y) = 1 ]-adic valuation + (in Function field in y defined by y^3 + x*y + 2*x^2 after y |--> 1/x^2*y), + [ (x)-adic valuation, v(y) = 1/2 ]-adic valuation + (in Function field in y defined by y^3 + x*y + 2*x^2 after y |--> 1/x^2*y)] """ # this should have been handled by create_key already @@ -352,11 +354,11 @@ def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, v TESTS:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w = v.extension(L) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) # needs sage.rings.function_field + sage: v = K.valuation(1/x) # needs sage.rings.function_field + sage: w = v.extension(L) # indirect doctest # needs sage.rings.function_field """ from sage.categories.function_fields import FunctionFields @@ -493,8 +495,8 @@ def extensions(self, L): sage: K. = FunctionField(QQ) sage: v = K.valuation(x) sage: R. = K[] - sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field - sage: v.extensions(L) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x) # needs sage.rings.function_field + sage: v.extensions(L) # needs sage.rings.function_field [(x)-adic valuation] TESTS: @@ -503,21 +505,22 @@ def extensions(self, L): sage: v = K.valuation(1/x) sage: R. = K[] - sage: L. = K.extension(y^2 - 1/(x^2 + 1)) # optional - sage.rings.function_field - sage: sorted(v.extensions(L), key=str) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - 1/(x^2 + 1)) # needs sage.rings.function_field + sage: sorted(v.extensions(L), key=str) # needs sage.rings.function_field [[ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation, [ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation] Iterated extensions over the infinite place:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings - sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field - sage: R. = L[] # optional - sage.rings.finite_rings sage.rings.function_field - sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w.extension(M) # squarefreeness is not implemented here # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = K.valuation(1/x) + sage: w = v.extension(L) + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: w.extension(M) # not implemented Traceback (most recent call last): ... NotImplementedError @@ -531,19 +534,20 @@ def extensions(self, L): sage: v = K.valuation(v) sage: R. = K[] - sage: L. = K.extension(y^3 - x^4 - 1) # optional - sage.rings.function_field - sage: v.extensions(L) # optional - sage.rings.function_field + sage: L. = K.extension(y^3 - x^4 - 1) # needs sage.rings.function_field + sage: v.extensions(L) # needs sage.rings.function_field [2-adic valuation] Test that this works in towers:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y - x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: R. = L[] # optional - sage.rings.finite_rings sage.rings.function_field - sage: L. = L.extension(z - y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v.extensions(L) # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y - x) + sage: R. = L[] + sage: L. = L.extension(z - y) + sage: v = K.valuation(x) + sage: v.extensions(L) [(x)-adic valuation] """ K = self.domain() @@ -588,10 +592,10 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: v = K.valuation(x) # indirect doctest # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: v = K.valuation(x) # indirect doctest sage: from sage.rings.function_field.valuation import RationalFunctionFieldValuation_base - sage: isinstance(v, RationalFunctionFieldValuation_base) # optional - sage.rings.finite_rings + sage: isinstance(v, RationalFunctionFieldValuation_base) True """ @@ -602,16 +606,17 @@ def element_with_valuation(self, s): EXAMPLES:: + sage: # needs sage.rings.number_field sage: x = polygen(ZZ, 'x') - sage: K. = NumberField(x^3 + 6) # optional - sage.rings.number_field - sage: v = K.valuation(2) # optional - sage.rings.number_field - sage: R. = K[] # optional - sage.rings.number_field - sage: w = GaussValuation(R, v).augmentation(x, 1/123) # optional - sage.rings.number_field - sage: K. = FunctionField(K) # optional - sage.rings.number_field - sage: w = w.extension(K) # optional - sage.rings.number_field - sage: w.element_with_valuation(122/123) # optional - sage.rings.number_field + sage: K. = NumberField(x^3 + 6) + sage: v = K.valuation(2) + sage: R. = K[] + sage: w = GaussValuation(R, v).augmentation(x, 1/123) + sage: K. = FunctionField(K) + sage: w = w.extension(K) + sage: w.element_with_valuation(122/123) 2/x - sage: w.element_with_valuation(1) # optional - sage.rings.number_field + sage: w.element_with_valuation(1) 2 """ @@ -632,10 +637,10 @@ class ClassicalFunctionFieldValuation_base(DiscreteFunctionFieldValuation_base): TESTS:: - sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings - sage: v = K.valuation(x) # indirect doctest # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(5)) + sage: v = K.valuation(x) # indirect doctest sage: from sage.rings.function_field.valuation import ClassicalFunctionFieldValuation_base - sage: isinstance(v, ClassicalFunctionFieldValuation_base) # optional - sage.rings.finite_rings + sage: isinstance(v, ClassicalFunctionFieldValuation_base) True """ @@ -990,7 +995,7 @@ class FiniteRationalFunctionFieldValuation(InducedRationalFunctionFieldValuation EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(x + 1); v # indirect doctest + sage: v = K.valuation(x + 1); v # indirect doctest (x + 1)-adic valuation A finite place with residual degree:: @@ -1000,14 +1005,14 @@ class FiniteRationalFunctionFieldValuation(InducedRationalFunctionFieldValuation A finite place with ramification:: - sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings - sage: L. = FunctionField(K) # optional - sage.rings.finite_rings - sage: u = L.valuation(x^3 - t); u # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(3)) + sage: L. = FunctionField(K) + sage: u = L.valuation(x^3 - t); u (x^3 + 2*t)-adic valuation A finite place with residual degree and ramification:: - sage: q = L.valuation(x^6 - t); q # optional - sage.rings.finite_rings + sage: q = L.valuation(x^6 - t); q (x^6 + 2*t)-adic valuation """ @@ -1036,7 +1041,7 @@ class NonClassicalRationalFunctionFieldValuation(InducedRationalFunctionFieldVal sage: K. = FunctionField(QQ) sage: v = GaussValuation(QQ['x'], QQ.valuation(2)) - sage: w = K.valuation(v); w # indirect doctest + sage: w = K.valuation(v); w # indirect doctest 2-adic valuation """ @@ -1079,8 +1084,8 @@ def residue_ring(self): Rational function field in x over Finite Field of size 2 sage: R. = K[] - sage: L. = K.extension(y^2 + 2*x) # optional - sage.rings.function_field - sage: w.extension(L).residue_ring() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 + 2*x) # needs sage.rings.function_field + sage: w.extension(L).residue_ring() # needs sage.rings.function_field Function field in u2 defined by u2^2 + x TESTS: @@ -1111,9 +1116,9 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation, Discret sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x + 1)) # optional - sage.rings.function_field - sage: v = K.valuation(x - 1) # indirect doctest # optional - sage.rings.function_field - sage: w = v.extension(L); w # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - (x^2 + x + 1)) # needs sage.rings.function_field + sage: v = K.valuation(x - 1) # indirect doctest # needs sage.rings.function_field + sage: w = v.extension(L); w # needs sage.rings.function_field (x - 1)-adic valuation """ @@ -1121,13 +1126,14 @@ def __init__(self, parent, approximant, G, approximants): r""" TESTS:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x + 1)) # optional - sage.rings.function_field - sage: v = K.valuation(x - 1) # indirect doctest # optional - sage.rings.function_field - sage: w = v.extension(L) # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - (x^2 + x + 1)) + sage: v = K.valuation(x - 1) # indirect doctest + sage: w = v.extension(L) sage: from sage.rings.function_field.valuation import FunctionFieldFromLimitValuation - sage: isinstance(w, FunctionFieldFromLimitValuation) # optional - sage.rings.function_field + sage: isinstance(w, FunctionFieldFromLimitValuation) True """ @@ -1140,12 +1146,13 @@ def _to_base_domain(self, f): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x + 1)) # optional - sage.rings.function_field - sage: v = K.valuation(x - 1) # indirect doctest # optional - sage.rings.function_field - sage: w = v.extension(L) # optional - sage.rings.function_field - sage: w._to_base_domain(y).parent() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - (x^2 + x + 1)) + sage: v = K.valuation(x - 1) # indirect doctest + sage: w = v.extension(L) + sage: w._to_base_domain(y).parent() Univariate Polynomial Ring in y over Rational function field in x over Rational Field """ @@ -1157,12 +1164,13 @@ def scale(self, scalar): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x + 1)) # optional - sage.rings.function_field - sage: v = K.valuation(x - 1) # indirect doctest # optional - sage.rings.function_field - sage: w = v.extension(L) # optional - sage.rings.function_field - sage: 3*w # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - (x^2 + x + 1)) + sage: v = K.valuation(x - 1) # indirect doctest + sage: w = v.extension(L) + sage: 3*w 3 * (x - 1)-adic valuation """ @@ -1178,8 +1186,8 @@ class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValua EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: v = K.valuation(1/x); v # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: v = K.valuation(1/x); v Valuation at the infinite place """ @@ -1187,10 +1195,10 @@ def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_v r""" TESTS:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: v = K.valuation(1/x) sage: from sage.rings.function_field.valuation import FunctionFieldMappedValuation_base - sage: isinstance(v, FunctionFieldMappedValuation_base) # optional - sage.rings.finite_rings + sage: isinstance(v, FunctionFieldMappedValuation_base) True """ @@ -1206,12 +1214,12 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings - sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w._to_base_domain(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) # needs sage.rings.function_field + sage: v = K.valuation(1/x) + sage: w = v.extension(L) # needs sage.rings.function_field + sage: w._to_base_domain(y) # needs sage.rings.function_field x^2*y """ @@ -1223,12 +1231,12 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings - sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w._from_base_domain(w._to_base_domain(y)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) # needs sage.rings.function_field + sage: v = K.valuation(1/x) + sage: w = v.extension(L) # needs sage.rings.function_field + sage: w._from_base_domain(w._to_base_domain(y)) # needs sage.rings.function_field y r""" @@ -1240,12 +1248,12 @@ def scale(self, scalar): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings - sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field - sage: 3*w # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) # needs sage.rings.function_field + sage: v = K.valuation(1/x) + sage: w = v.extension(L) # needs sage.rings.function_field + sage: 3*w # needs sage.rings.function_field 3 * (x)-adic valuation (in Rational function field in x over Finite Field of size 2 after x |--> 1/x) """ @@ -1260,11 +1268,11 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings - sage: v.extension(L) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) # needs sage.rings.function_field + sage: v = K.valuation(1/x) + sage: v.extension(L) # indirect doctest # needs sage.rings.function_field Valuation at the infinite place """ @@ -1279,12 +1287,13 @@ def is_discrete_valuation(self): EXAMPLES:: + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x^4 - 1) # optional - sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.function_field - sage: w0,w1 = v.extensions(L) # optional - sage.rings.function_field - sage: w0.is_discrete_valuation() # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - x^4 - 1) + sage: v = K.valuation(1/x) + sage: w0,w1 = v.extensions(L) + sage: w0.is_discrete_valuation() True """ @@ -1299,8 +1308,8 @@ class FunctionFieldMappedValuationRelative_base(FunctionFieldMappedValuation_bas EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: v = K.valuation(1/x); v # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: v = K.valuation(1/x); v Valuation at the infinite place """ @@ -1308,10 +1317,10 @@ def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_v r""" TESTS:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: v = K.valuation(1/x) sage: from sage.rings.function_field.valuation import FunctionFieldMappedValuationRelative_base - sage: isinstance(v, FunctionFieldMappedValuationRelative_base) # optional - sage.rings.finite_rings + sage: isinstance(v, FunctionFieldMappedValuationRelative_base) True """ @@ -1325,8 +1334,8 @@ def restriction(self, ring): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: K.valuation(1/x).restriction(GF(2)) # optional - sage.rings.finite_rings + sage: K. = FunctionField(GF(2)) + sage: K.valuation(1/x).restriction(GF(2)) Trivial valuation on Finite Field of size 2 """ @@ -1377,7 +1386,7 @@ class InfiniteRationalFunctionFieldValuation(FunctionFieldMappedValuationRelativ EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(1/x) # indirect doctest + sage: v = K.valuation(1/x) # indirect doctest """ def __init__(self, parent): @@ -1385,7 +1394,7 @@ def __init__(self, parent): TESTS:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(1/x) # indirect doctest + sage: v = K.valuation(1/x) # indirect doctest sage: from sage.rings.function_field.valuation import InfiniteRationalFunctionFieldValuation sage: isinstance(v, InfiniteRationalFunctionFieldValuation) True @@ -1403,7 +1412,7 @@ def _repr_(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.valuation(1/x) # indirect doctest + sage: K.valuation(1/x) # indirect doctest Valuation at the infinite place """ @@ -1420,23 +1429,23 @@ class FunctionFieldExtensionMappedValuation(FunctionFieldMappedValuationRelative EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings - sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) # needs sage.rings.function_field + sage: v = K.valuation(1/x) + sage: w = v.extension(L) # needs sage.rings.function_field - sage: w(x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w(x) # needs sage.rings.function_field -1 - sage: w(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w(y) # needs sage.rings.function_field -3/2 - sage: w.uniformizer() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w.uniformizer() # needs sage.rings.function_field 1/x^2*y TESTS:: sage: from sage.rings.function_field.valuation import FunctionFieldExtensionMappedValuation - sage: isinstance(w, FunctionFieldExtensionMappedValuation) # optional - sage.rings.finite_rings sage.rings.function_field + sage: isinstance(w, FunctionFieldExtensionMappedValuation) # needs sage.rings.function_field True """ @@ -1446,18 +1455,20 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w = v.extension(L); w # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = K.valuation(1/x) + sage: w = v.extension(L); w Valuation at the infinite place + sage: # needs sage.rings.function_field sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - 1/x^2 - 1) # optional - sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.function_field - sage: w = v.extensions(L); w # optional - sage.rings.function_field + sage: L. = K.extension(y^2 - 1/x^2 - 1) + sage: v = K.valuation(1/x) + sage: w = v.extensions(L); w [[ Valuation at the infinite place, v(y + 1) = 2 ]-adic valuation, [ Valuation at the infinite place, v(y - 1) = 2 ]-adic valuation] @@ -1473,12 +1484,13 @@ def restriction(self, ring): EXAMPLES:: - sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings - sage: R. = K[] # optional - sage.rings.finite_rings - sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field - sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings - sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w.restriction(K) is v # optional - sage.rings.finite_rings sage.rings.function_field + sage: # needs sage.rings.function_field + sage: K. = FunctionField(GF(2)) + sage: R. = K[] + sage: L. = K.extension(y^2 + y + x^3) + sage: v = K.valuation(1/x) + sage: w = v.extension(L) + sage: w.restriction(K) is v True """ if ring.is_subring(self.domain().base()): diff --git a/src/sage/rings/homset.py b/src/sage/rings/homset.py index b89ba5a1ed7..6013a6ca28b 100644 --- a/src/sage/rings/homset.py +++ b/src/sage/rings/homset.py @@ -28,9 +28,9 @@ def is_RingHomset(H): True sage: is_RH(ZZ) False - sage: is_RH(Hom(RR, CC)) + sage: is_RH(Hom(RR, CC)) # needs sage.rings.real_mpfr True - sage: is_RH(Hom(FreeModule(ZZ,1), FreeModule(QQ,1))) + sage: is_RH(Hom(FreeModule(ZZ,1), FreeModule(QQ,1))) # needs sage.modules False """ return isinstance(H, RingHomset_generic) @@ -133,26 +133,27 @@ def _element_constructor_(self, x, check=True, base_map=None): You can provide a morphism on the base:: - sage: k = GF(9) # optional - sage.rings.finite_rings - sage: z2 = k.gen() # optional - sage.rings.finite_rings - sage: cc = k.frobenius_endomorphism() # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: H = Hom(R, R) # optional - sage.rings.finite_rings - sage: phi = H([x^2], base_map=cc); phi # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k = GF(9) + sage: z2 = k.gen() + sage: cc = k.frobenius_endomorphism() + sage: R. = k[] + sage: H = Hom(R, R) + sage: phi = H([x^2], base_map=cc); phi Ring endomorphism of Univariate Polynomial Ring in x over Finite Field in z2 of size 3^2 Defn: x |--> x^2 with map of base ring - sage: phi(z2 * x) == z2^3 * x^2 # optional - sage.rings.finite_rings + sage: phi(z2 * x) == z2^3 * x^2 True sage: R. = ZZ[] - sage: K. = GF(7^2) # optional - sage.rings.finite_rings - sage: L. = K.extension(x^3 - 3) # optional - sage.rings.finite_rings - sage: phi = L.hom([u^7], base_map=K.frobenius_endomorphism()) # optional - sage.rings.finite_rings - sage: phi(u) == u^7 # optional - sage.rings.finite_rings + sage: K. = GF(7^2) # needs sage.rings.finite_rings + sage: L. = K.extension(x^3 - 3) # needs sage.rings.finite_rings + sage: phi = L.hom([u^7], base_map=K.frobenius_endomorphism()) # needs sage.rings.finite_rings + sage: phi(u) == u^7 # needs sage.rings.finite_rings True - sage: phi(a) == a^7 # optional - sage.rings.finite_rings + sage: phi(a) == a^7 # needs sage.rings.finite_rings True TESTS:: @@ -248,15 +249,15 @@ class RingHomset_quo_ring(RingHomset_generic): EXAMPLES:: sage: R. = PolynomialRing(QQ, 2) - sage: S. = R.quotient(x^2 + y^2) # optional - sage.libs.singular - sage: phi = S.hom([b,a]); phi # optional - sage.libs.singular + sage: S. = R.quotient(x^2 + y^2) # needs sage.libs.singular + sage: phi = S.hom([b,a]); phi # needs sage.libs.singular Ring endomorphism of Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2 + y^2) Defn: a |--> b b |--> a - sage: phi(a) # optional - sage.libs.singular + sage: phi(a) # needs sage.libs.singular b - sage: phi(b) # optional - sage.libs.singular + sage: phi(b) # needs sage.libs.singular a TESTS: @@ -266,15 +267,15 @@ class RingHomset_quo_ring(RingHomset_generic): :: sage: R. = PolynomialRing(QQ, 2) - sage: S. = R.quotient(x^2 + y^2) # optional - sage.libs.singular - sage: H = S.Hom(R) # optional - sage.libs.singular - sage: H == loads(dumps(H)) # optional - sage.libs.singular + sage: S. = R.quotient(x^2 + y^2) # needs sage.libs.singular + sage: H = S.Hom(R) # needs sage.libs.singular + sage: H == loads(dumps(H)) # needs sage.libs.singular True We test pickling of actual homomorphisms in a quotient:: - sage: phi = S.hom([b,a]) # optional - sage.libs.singular - sage: phi == loads(dumps(phi)) # optional - sage.libs.singular + sage: phi = S.hom([b,a]) # needs sage.libs.singular + sage: phi == loads(dumps(phi)) # needs sage.libs.singular True """ @@ -287,17 +288,17 @@ def _element_constructor_(self, x, base_map=None, check=True): EXAMPLES:: sage: R. = PolynomialRing(QQ, 2) - sage: S. = R.quotient(x^2 + y^2) # optional - sage.libs.singular - sage: H = S.Hom(R) # optional - sage.libs.singular - sage: phi = H([b, a]); phi # optional - sage.libs.singular + sage: S. = R.quotient(x^2 + y^2) # needs sage.libs.singular + sage: H = S.Hom(R) # needs sage.libs.singular + sage: phi = H([b, a]); phi # needs sage.libs.singular Ring morphism: From: Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2 + y^2) To: Multivariate Polynomial Ring in x, y over Rational Field Defn: a |--> b b |--> a sage: R2. = PolynomialRing(ZZ, 2) - sage: H2 = Hom(R2, S) # optional - sage.libs.singular - sage: H2(phi) # optional - sage.libs.singular + sage: H2 = Hom(R2, S) # needs sage.libs.singular + sage: H2(phi) # needs sage.libs.singular Composite map: From: Multivariate Polynomial Ring in x, y over Integer Ring To: Quotient of Multivariate Polynomial Ring in x, y over Rational Field by the ideal (x^2 + y^2) diff --git a/src/sage/rings/polynomial/hilbert.pyx b/src/sage/rings/polynomial/hilbert.pyx index 864e2766407..ccf5159dfac 100644 --- a/src/sage/rings/polynomial/hilbert.pyx +++ b/src/sage/rings/polynomial/hilbert.pyx @@ -576,14 +576,6 @@ def hilbert_poincare_series(I, grading=None): 120*t^3 + 135*t^2 + 30*t + 1 sage: hilbert_poincare_series(J).denominator().factor() (t - 1)^14 - - This example exceeded the capabilities of Singular before version 4.2.1p2. - In Singular 4.3.1, it works correctly on 64-bit, but on 32-bit, it prints overflow warnings - and omits some terms:: - - sage: J.hilbert_numerator(algorithm='singular') - 120*t^33 - 3465*t^32 + 48180*t^31 - 429374*t^30 + 2753520*t^29 - 13522410*t^28 + 52832780*t^27 - 168384150*t^26 + 445188744*t^25 - 987193350*t^24 + 1847488500*t^23 + 1372406746*t^22 - 403422496*t^21 - 8403314*t^20 - 471656596*t^19 + 1806623746*t^18 + 752776200*t^17 + 752776200*t^16 - 1580830020*t^15 + 1673936550*t^14 - 1294246800*t^13 + 786893250*t^12 - 382391100*t^11 + 146679390*t^10 - 42299400*t^9 + 7837830*t^8 - 172260*t^7 - 468930*t^6 + 183744*t^5 - 39270*t^4 + 5060*t^3 - 330*t^2 + 1 # 64-bit - ...120*t^33 - 3465*t^32 + 48180*t^31 - ... # 32-bit """ cdef Polynomial_integer_dense_flint HP HP, grading = first_hilbert_series(I, grading=grading, return_grading=True) diff --git a/src/sage/rings/polynomial/multi_polynomial_ideal.py b/src/sage/rings/polynomial/multi_polynomial_ideal.py index 7bf29415157..c9753055fc7 100644 --- a/src/sage/rings/polynomial/multi_polynomial_ideal.py +++ b/src/sage/rings/polynomial/multi_polynomial_ideal.py @@ -166,11 +166,11 @@ sage: factor(164878) 2 * 7 * 11777 - sage: I.change_ring(P.change_ring(GF(2))).groebner_basis() # optional - sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(2))).groebner_basis() # needs sage.rings.finite_rings [x + y + z, y^2 + y, y*z + y, z^2 + 1] - sage: I.change_ring(P.change_ring(GF(7))).groebner_basis() # optional - sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(7))).groebner_basis() # needs sage.rings.finite_rings [x - 1, y + 3, z - 2] - sage: I.change_ring(P.change_ring(GF(11777))).groebner_basis() # optional - sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(11777))).groebner_basis() # needs sage.rings.finite_rings [x + 5633, y - 3007, z - 2626] The Groebner basis modulo any product of the prime factors is also non-trivial:: @@ -181,7 +181,7 @@ Modulo any other prime the Groebner basis is trivial so there are no other solutions. For example:: - sage: I.change_ring(P.change_ring(GF(3))).groebner_basis() # optional - sage.rings.finite_rings + sage: I.change_ring(P.change_ring(GF(3))).groebner_basis() # needs sage.rings.finite_rings [1] TESTS:: @@ -343,9 +343,9 @@ def _magma_init_(self, magma): EXAMPLES:: - sage: R. = PolynomialRing(GF(127),10) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R,4) # indirect doctest # optional - sage.rings.finite_rings - sage: magma(I) # optional - magma # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(127),10) # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Cyclic(R,4) # indirect doctest # needs sage.rings.finite_rings + sage: magma(I) # optional - magma # needs sage.rings.finite_rings Ideal of Polynomial ring of rank 10 over GF(127) Order: Graded Reverse Lexicographical Variables: a, b, c, d, e, f, g, h, i, j @@ -380,18 +380,20 @@ def _groebner_basis_magma(self, deg_bound=None, prot=False, magma=magma_default) EXAMPLES:: - sage: R. = PolynomialRing(GF(127), 10) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R, 6) # optional - sage.rings.finite_rings - sage: gb = I.groebner_basis('magma:GroebnerBasis') # indirect doctest; optional - magma sage.rings.finite_rings - sage: len(gb) # optional - magma # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(127), 10) + sage: I = sage.rings.ideal.Cyclic(R, 6) + sage: gb = I.groebner_basis('magma:GroebnerBasis') # optional - magma + sage: len(gb) # optional - magma 45 We may also pass a degree bound to Magma:: - sage: R. = PolynomialRing(GF(127), 10) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R, 6) # optional - sage.rings.finite_rings - sage: gb = I.groebner_basis('magma:GroebnerBasis', deg_bound=4) # indirect doctest; optional - magma # optional - sage.rings.finite_rings - sage: len(gb) # optional - magma # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(127), 10) + sage: I = sage.rings.ideal.Cyclic(R, 6) + sage: gb = I.groebner_basis('magma:GroebnerBasis', deg_bound=4) # optional - magma + sage: len(gb) # optional - magma 5 """ R = self.ring() @@ -1046,38 +1048,38 @@ def triangular_decomposition(self, algorithm=None, singular=singular_default): EXAMPLES:: - sage: P. = PolynomialRing(QQ, 5, order='lex'); P.rename("P") + sage: P. = PolynomialRing(QQ, 5, order='lex') sage: I = sage.rings.ideal.Cyclic(P) sage: GB = Ideal(I.groebner_basis('libsingular:stdfglm')) sage: GB.triangular_decomposition('singular:triangLfak') - [Ideal (a - 1, b - 1, c - 1, d^2 + 3*d + 1, e + d + 3) of P, - Ideal (a - 1, b - 1, c^2 + 3*c + 1, d + c + 3, e - 1) of P, - Ideal (a - 1, b^2 + 3*b + 1, c + b + 3, d - 1, e - 1) of P, + [Ideal (a - 1, b - 1, c - 1, d^2 + 3*d + 1, e + d + 3) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, + Ideal (a - 1, b - 1, c^2 + 3*c + 1, d + c + 3, e - 1) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, + Ideal (a - 1, b^2 + 3*b + 1, c + b + 3, d - 1, e - 1) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a - 1, b^4 + b^3 + b^2 + b + 1, -c + b^2, -d + b^3, - e + b^3 + b^2 + b + 1) of P, - Ideal (a^2 + 3*a + 1, b - 1, c - 1, d - 1, e + a + 3) of P, - Ideal (a^2 + 3*a + 1, b + a + 3, c - 1, d - 1, e - 1) of P, + e + b^3 + b^2 + b + 1) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, + Ideal (a^2 + 3*a + 1, b - 1, c - 1, d - 1, e + a + 3) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, + Ideal (a^2 + 3*a + 1, b + a + 3, c - 1, d - 1, e - 1) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 - 4*a^3 + 6*a^2 + a + 1, -11*b^2 + 6*b*a^3 - 26*b*a^2 + 41*b*a - 4*b - 8*a^3 + 31*a^2 - 40*a - 24, 11*c + 3*a^3 - 13*a^2 + 26*a - 2, 11*d + 3*a^3 - 13*a^2 + 26*a - 2, - -11*e - 11*b + 6*a^3 - 26*a^2 + 41*a - 4) of P, + -11*e - 11*b + 6*a^3 - 26*a^2 + 41*a - 4) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, - b - 1, c + a^3 + a^2 + a + 1, -d + a^3, -e + a^2) of P, + b - 1, c + a^3 + a^2 + a + 1, -d + a^3, -e + a^2) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, - b - a, c - a, d^2 + 3*d*a + a^2, e + d + 3*a) of P, + b - a, c - a, d^2 + 3*d*a + a^2, e + d + 3*a) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, - b - a, c^2 + 3*c*a + a^2, d + c + 3*a, e - a) of P, + b - a, c^2 + 3*c*a + a^2, d + c + 3*a, e - a) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, - b^2 + 3*b*a + a^2, c + b + 3*a, d - a, e - a) of P, + b^2 + 3*b*a + a^2, c + b + 3*a, d - a, e - a) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + a^2 + a + 1, b^3 + b^2*a + b^2 + b*a^2 + b*a + b + a^3 + a^2 + a + 1, c + b^2*a^3 + b^2*a^2 + b^2*a + b^2, -d + b^2*a^2 + b^2*a + b^2 + b*a^2 + b*a + a^2, - -e + b^2*a^3 - b*a^2 - b*a - b - a^2 - a) of P, + -e + b^2*a^3 - b*a^2 - b*a - b - a^2 - a) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field, Ideal (a^4 + a^3 + 6*a^2 - 4*a + 1, -11*b^2 + 6*b*a^3 + 10*b*a^2 + 39*b*a + 2*b + 16*a^3 + 23*a^2 + 104*a - 24, 11*c + 3*a^3 + 5*a^2 + 25*a + 1, 11*d + 3*a^3 + 5*a^2 + 25*a + 1, - -11*e - 11*b + 6*a^3 + 10*a^2 + 39*a + 2) of P] + -11*e - 11*b + 6*a^3 + 10*a^2 + 39*a + 2) of Multivariate Polynomial Ring in e, d, c, b, a over Rational Field] sage: R. = PolynomialRing(QQ, 2, order='lex') sage: f1 = 1/2*((x1^2 + 2*x1 - 4)*x2^2 + 2*(x1^2 + x1)*x2 + x1^2) @@ -1099,9 +1101,9 @@ def triangular_decomposition(self, algorithm=None, singular=singular_default): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: J = Ideal(x^2 + y^2 - 2, y^2 - 1) # optional - sage.rings.number_field - sage: J.triangular_decomposition() # optional - sage.rings.number_field + sage: R. = QQbar[] # needs sage.rings.number_field + sage: J = Ideal(x^2 + y^2 - 2, y^2 - 1) # needs sage.rings.number_field + sage: J.triangular_decomposition() # needs sage.rings.number_field [Ideal (y^2 - 1, x^2 - 1) of Multivariate Polynomial Ring in x, y over Algebraic Field] """ P = self.ring() @@ -1157,9 +1159,9 @@ def dimension(self, singular=singular_default): EXAMPLES:: - sage: P. = PolynomialRing(GF(32003), order='degrevlex') # optional - sage.rings.finite_rings - sage: I = ideal(x^2 - y, x^3) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: P. = PolynomialRing(GF(32003), order='degrevlex') # needs sage.rings.finite_rings + sage: I = ideal(x^2 - y, x^3) # needs sage.rings.finite_rings + sage: I.dimension() # needs sage.rings.finite_rings 1 If the ideal is the total ring, the dimension is `-1` by convention. @@ -1171,22 +1173,23 @@ def dimension(self, singular=singular_default): EXAMPLES:: - sage: R. = PolynomialRing(GF(2147483659^2), order='lex') # optional - sage.rings.finite_rings - sage: I = R.ideal([x*y, x*y + 1]) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(2147483659^2), order='lex') + sage: I = R.ideal([x*y, x*y + 1]) + sage: I.dimension() verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. -1 - sage: I=ideal([x*(x*y+1), y*(x*y+1)]) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: I=ideal([x*(x*y+1), y*(x*y+1)]) + sage: I.dimension() verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. 1 - sage: I = R.ideal([x^3*y, x*y^2]) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: I = R.ideal([x^3*y, x*y^2]) + sage: I.dimension() verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. 1 - sage: R. = PolynomialRing(GF(2147483659^2), order='lex') # optional - sage.rings.finite_rings - sage: I = R.ideal(0) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(2147483659^2), order='lex') + sage: I = R.ideal(0) + sage: I.dimension() verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. 2 @@ -1198,9 +1201,9 @@ def dimension(self, singular=singular_default): Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = ideal(x^2-y, x^3-QQbar(-1)) # optional - sage.rings.number_field - sage: I.dimension() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = ideal(x^2-y, x^3-QQbar(-1)) # needs sage.rings.number_field + sage: I.dimension() # needs sage.rings.number_field 1 .. NOTE:: @@ -1299,19 +1302,20 @@ def vector_space_dimension(self): Due to integer overflow, the result is correct only modulo ``2^32``, see :trac:`8586`:: - sage: P. = PolynomialRing(GF(32003), 3) # optional - sage.rings.finite_rings - sage: sage.rings.ideal.FieldIdeal(P).vector_space_dimension() # known bug # optional - sage.rings.finite_rings + sage: P. = PolynomialRing(GF(32003), 3) # needs sage.rings.finite_rings + sage: sage.rings.ideal.FieldIdeal(P).vector_space_dimension() # known bug, needs sage.rings.finite_rings 32777216864027 TESTS: Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = ideal(x^2-y,x^3-QQbar(-1),z-y) # optional - sage.rings.number_field - sage: I.dimension() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: P. = QQbar[] + sage: I = ideal(x^2-y,x^3-QQbar(-1),z-y) + sage: I.dimension() 0 - sage: I.vector_space_dimension() # optional - sage.rings.number_field + sage: I.vector_space_dimension() 3 """ @@ -1351,9 +1355,9 @@ def _groebner_basis_ginv(self, algorithm="TQ", criteria='CritPartially', divisio sage: I.groebner_basis(algorithm='ginv') # optional - ginv [z^3 - 79/210*z^2 + 1/30*y + 1/70*z, y^2 - 3/5*z^2 - 1/5*y + 1/5*z, y*z + 6/5*z^2 - 1/10*y - 2/5*z, x + 2*y + 2*z - 1] - sage: P. = PolynomialRing(GF(127), order='degrevlex') # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(P) # optional - sage.rings.finite_rings - sage: I.groebner_basis(algorithm='ginv') # optional - ginv # optional - sage.rings.finite_rings + sage: P. = PolynomialRing(GF(127), order='degrevlex') # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(P) # needs sage.rings.finite_rings + sage: I.groebner_basis(algorithm='ginv') # optional - ginv # needs sage.rings.finite_rings ... [z^3 + 22*z^2 - 55*y + 49*z, y^2 - 26*z^2 - 51*y + 51*z, y*z + 52*z^2 + 38*y + 25*z, x + 2*y + 2*z - 1] @@ -1596,9 +1600,9 @@ def genus(self): Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = ideal(y^3*z + x^3*y + x*z^3) # optional - sage.rings.number_field - sage: I.genus() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = ideal(y^3*z + x^3*y + x*z^3) # needs sage.rings.number_field + sage: I.genus() # needs sage.rings.number_field 3 """ from sage.libs.singular.function_factory import ff @@ -1657,10 +1661,11 @@ def intersection(self, *others): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = x*R # optional - sage.rings.number_field - sage: J = y*R # optional - sage.rings.number_field - sage: I.intersection(J) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = x*R + sage: J = y*R + sage: I.intersection(J) Ideal (x*y) of Multivariate Polynomial Ring in x, y over Algebraic Field """ R = self.ring() @@ -1741,10 +1746,11 @@ def radical(self): :: - sage: R. = PolynomialRing(GF(37), 3) # optional - sage.rings.finite_rings - sage: p = z^2 + 1; q = z^3 + 2 # optional - sage.rings.finite_rings - sage: I = (p*q^2, y - z^2) * R # optional - sage.rings.finite_rings - sage: I.radical() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(37), 3) + sage: p = z^2 + 1; q = z^3 + 2 + sage: I = (p*q^2, y - z^2) * R + sage: I.radical() Ideal (z^2 - y, y^2*z + y*z + 2*y + 2) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 37 """ @@ -1829,16 +1835,17 @@ def syzygy_module(self): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: f = 2*x^2 + y # optional - sage.rings.number_field - sage: g = y # optional - sage.rings.number_field - sage: h = 2*f + g # optional - sage.rings.number_field - sage: I = Ideal([f,g,h]) # optional - sage.rings.number_field - sage: M = I.syzygy_module(); M # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: f = 2*x^2 + y + sage: g = y + sage: h = 2*f + g + sage: I = Ideal([f,g,h]) + sage: M = I.syzygy_module(); M [ -2 -1 1] [ -y 2*x^2 + y 0] - sage: G = vector(I.gens()) # optional - sage.rings.number_field - sage: M*G # optional - sage.rings.number_field + sage: G = vector(I.gens()) + sage: M*G (0, 0) """ from sage.libs.singular.function_factory import ff @@ -1967,8 +1974,8 @@ def interreduced_basis(self): The interreduced basis of 0 is 0:: - sage: P. = GF(2)[] # optional - sage.rings.finite_rings - sage: Ideal(P(0)).interreduced_basis() # optional - sage.rings.finite_rings + sage: P. = GF(2)[] # needs sage.rings.finite_rings + sage: Ideal(P(0)).interreduced_basis() # needs sage.rings.finite_rings [0] ALGORITHM: @@ -1981,9 +1988,9 @@ def interreduced_basis(self): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([z*x + y^3, z + y^3, z + x*y]) # optional - sage.rings.number_field - sage: I.interreduced_basis() # optional - sage.rings.number_field + sage: R. = QQbar[] # needs sage.rings.number_field + sage: I = Ideal([z*x + y^3, z + y^3, z + x*y]) # needs sage.rings.number_field + sage: I.interreduced_basis() # needs sage.rings.number_field [y^3 + z, x*y + z, x*z - z] """ return self.basis.reduced() @@ -2011,18 +2018,19 @@ def basis_is_groebner(self, singular=singular_default): EXAMPLES:: - sage: R. = PolynomialRing(GF(127), 10) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R, 4) # optional - sage.rings.finite_rings - sage: I.basis_is_groebner() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(127), 10) + sage: I = sage.rings.ideal.Cyclic(R, 4) + sage: I.basis_is_groebner() False - sage: I2 = Ideal(I.groebner_basis()) # optional - sage.rings.finite_rings - sage: I2.basis_is_groebner() # optional - sage.rings.finite_rings + sage: I2 = Ideal(I.groebner_basis()) + sage: I2.basis_is_groebner() True A more complicated example:: - sage: R. = PolynomialRing(GF(7583)) # optional - sage.rings.finite_rings - sage: l = [u6 + u5 + u4 + u3 + u2 - 3791*h, # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(7583)) # needs sage.rings.finite_rings + sage: l = [u6 + u5 + u4 + u3 + u2 - 3791*h, # needs sage.rings.finite_rings ....: U6 + U5 + U4 + U3 + U2 - 3791*h, ....: U2*u2 - h^2, U3*u3 - h^2, U4*u4 - h^2, ....: U5*u4 + U5*u3 + U4*u3 + U5*u2 + U4*u2 + U3*u2 - 3791*U5*h @@ -2064,10 +2072,10 @@ def basis_is_groebner(self, singular=singular_default): ....: - 2*U5*U4*U2^2*h^2 - 2*U5*U3*U2^2*h^2 - 2*U4*U3*U2^2*h^2 ....: - U5*U4*U3*h^3 - U5*U4*U2*h^3 - U5*U3*U2*h^3 - U4*U3*U2*h^3] - sage: Ideal(l).basis_is_groebner() # optional - sage.rings.finite_rings + sage: Ideal(l).basis_is_groebner() # needs sage.rings.finite_rings False - sage: gb = Ideal(l).groebner_basis() # optional - sage.rings.finite_rings - sage: Ideal(gb).basis_is_groebner() # optional - sage.rings.finite_rings + sage: gb = Ideal(l).groebner_basis() # needs sage.rings.finite_rings + sage: Ideal(gb).basis_is_groebner() # needs sage.rings.finite_rings True .. NOTE:: @@ -2085,12 +2093,13 @@ def basis_is_groebner(self, singular=singular_default): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = sage.rings.ideal.Cyclic(R,4) # optional - sage.rings.number_field - sage: I.basis_is_groebner() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = sage.rings.ideal.Cyclic(R,4) + sage: I.basis_is_groebner() False - sage: I2 = Ideal(I.groebner_basis()) # optional - sage.rings.number_field - sage: I2.basis_is_groebner() # optional - sage.rings.number_field + sage: I2 = Ideal(I.groebner_basis()) + sage: I2.basis_is_groebner() True """ from sage.matrix.constructor import matrix @@ -2173,9 +2182,9 @@ def transformed_basis(self, algorithm="gwalk", other_ring=None, singular=singula :: - sage: R. = PolynomialRing(GF(32003), 3, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([y^3 + x*y*z + y^2*z + x*z^3, 3 + x*y + x^2*y + y^2*z]) # optional - sage.rings.finite_rings - sage: I.transformed_basis('gwalk') # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(32003), 3, order='lex') # needs sage.rings.finite_rings + sage: I = Ideal([y^3 + x*y*z + y^2*z + x*z^3, 3 + x*y + x^2*y + y^2*z]) # needs sage.rings.finite_rings + sage: I.transformed_basis('gwalk') # needs sage.rings.finite_rings [z*y^2 + y*x^2 + y*x + 3, z*x + 8297*y^8*x^2 + 8297*y^8*x + 3556*y^7 - 8297*y^6*x^4 + 15409*y^6*x^3 - 8297*y^6*x^2 - 8297*y^5*x^5 + 15409*y^5*x^4 - 8297*y^5*x^3 + 3556*y^5*x^2 @@ -2195,12 +2204,13 @@ def transformed_basis(self, algorithm="gwalk", other_ring=None, singular=singula Check that this method works over QQbar (:trac:`25351`). We are not currently able to specify other_ring, due to the limitations of @handle_AA_and_QQbar:: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([y^3 + x^2, x^2*y + x^2, x^3 - x^2, z^4 - x^2 - y]) # optional - sage.rings.number_field - sage: I = Ideal(I.groebner_basis()) # optional - sage.rings.number_field - sage: S. = PolynomialRing(QQbar, 3, order='lex') # optional - sage.rings.number_field - sage: J = Ideal(I.transformed_basis('fglm', other_ring=S)) # known bug # optional - sage.rings.number_field - sage: J # known bug # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = Ideal([y^3 + x^2, x^2*y + x^2, x^3 - x^2, z^4 - x^2 - y]) + sage: I = Ideal(I.groebner_basis()) + sage: S. = PolynomialRing(QQbar, 3, order='lex') + sage: J = Ideal(I.transformed_basis('fglm', other_ring=S)) # known bug + sage: J # known bug """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence R = self.ring() @@ -2287,12 +2297,13 @@ def elimination_ideal(self, variables, algorithm=None, *args, **kwds): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = R * [x - t, y - t^2, z - t^3, s - x + y^3] # optional - sage.rings.number_field - sage: J = I.elimination_ideal([t, s]); J # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = R * [x - t, y - t^2, z - t^3, s - x + y^3] + sage: J = I.elimination_ideal([t, s]); J Ideal (y^2 - x*z, x*y - z, x^2 - y) of Multivariate Polynomial Ring in x, y, t, s, z over Algebraic Field - sage: print("possible output from giac", flush=True); I.elimination_ideal([t, s], algorithm="giac") == J # optional - sage.rings.number_field + sage: print("possible output from giac", flush=True); I.elimination_ideal([t, s], algorithm="giac") == J possible output... True @@ -2362,15 +2373,16 @@ def quotient(self, J): EXAMPLES:: - sage: R. = PolynomialRing(GF(181), 3) # optional - sage.rings.finite_rings - sage: I = Ideal([x^2 + x*y*z, y^2 - z^3*y, z^3 + y^5*x*z]) # optional - sage.rings.finite_rings - sage: J = Ideal([x]) # optional - sage.rings.finite_rings - sage: Q = I.quotient(J) # optional - sage.rings.finite_rings - sage: y*z + x in I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(181), 3) + sage: I = Ideal([x^2 + x*y*z, y^2 - z^3*y, z^3 + y^5*x*z]) + sage: J = Ideal([x]) + sage: Q = I.quotient(J) + sage: y*z + x in I False - sage: x in J # optional - sage.rings.finite_rings + sage: x in J True - sage: x * (y*z + x) in I # optional - sage.rings.finite_rings + sage: x * (y*z + x) in I True TESTS: @@ -2385,10 +2397,11 @@ def quotient(self, J): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = ideal(x, z) # optional - sage.rings.number_field - sage: J = ideal(R(1)) # optional - sage.rings.number_field - sage: I.quotient(J) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = ideal(x, z) + sage: J = ideal(R(1)) + sage: I.quotient(J) Ideal (z, x) of Multivariate Polynomial Ring in x, y, z over Algebraic Field Check that :trac:`12803` is fixed:: @@ -2439,10 +2452,11 @@ def saturation(self, other): Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = R.ideal(x^5*z^3, x*y*z, y*z^4) # optional - sage.rings.number_field - sage: J = R.ideal(z) # optional - sage.rings.number_field - sage: I.saturation(other = J) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = R.ideal(x^5*z^3, x*y*z, y*z^4) + sage: J = R.ideal(z) + sage: I.saturation(other = J) (Ideal (y, x^5) of Multivariate Polynomial Ring in x, y, z over Algebraic Field, 4) """ from sage.libs.singular.function_factory import ff @@ -2492,10 +2506,11 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True EXAMPLES:: - sage: K. = GF(27) # this example is from the MAGMA handbook # optional - sage.rings.finite_rings - sage: P. = PolynomialRing(K, 2, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([x^8 + y + 2, y^6 + x*y^5 + x^2]) # optional - sage.rings.finite_rings - sage: I = Ideal(I.groebner_basis()); I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = GF(27) # this example is from the MAGMA handbook + sage: P. = PolynomialRing(K, 2, order='lex') + sage: I = Ideal([x^8 + y + 2, y^6 + x*y^5 + x^2]) + sage: I = Ideal(I.groebner_basis()); I Ideal (x - y^47 - y^45 + y^44 - y^43 + y^41 - y^39 - y^38 - y^37 - y^36 + y^35 - y^34 - y^33 + y^32 - y^31 + y^30 + y^28 + y^27 + y^26 + y^25 - y^23 + y^22 + y^21 - y^19 - y^18 - y^16 + y^15 + y^13 @@ -2504,19 +2519,20 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True - y^25 + y^24 + y^2 + y + 1) of Multivariate Polynomial Ring in x, y over Finite Field in w of size 3^3 - sage: V = I.variety(); # optional - sage.rings.finite_rings - sage: sorted(V, key=str) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: V = I.variety(); + sage: sorted(V, key=str) [{y: w^2 + 2*w, x: 2*w + 2}, {y: w^2 + 2, x: 2*w}, {y: w^2 + w, x: 2*w + 1}] - sage: [f.subs(v) # check that all polynomials vanish # optional - sage.rings.finite_rings + sage: [f.subs(v) # check that all polynomials vanish ....: for f in I.gens() for v in V] [0, 0, 0, 0, 0, 0] - sage: [I.subs(v).is_zero() for v in V] # same test, but nicer syntax # optional - sage.rings.finite_rings + sage: [I.subs(v).is_zero() for v in V] # same test, but nicer syntax [True, True, True] However, we only account for solutions in the ground field and not in the algebraic closure:: - sage: I.vector_space_dimension() # optional - sage.rings.finite_rings + sage: I.vector_space_dimension() # needs sage.rings.finite_rings 48 Here we compute the points of intersection of a hyperbola and a @@ -2538,7 +2554,7 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True sage: sorted(I.variety(ring=RR), key=str) [{y: 0.361103080528647, x: 2.76929235423863}, {y: 1.00000000000000, x: 1.00000000000000}] - sage: I.variety(ring=AA) # optional - sage.rings.number_field + sage: I.variety(ring=AA) # needs sage.rings.number_field [{y: 1, x: 1}, {y: 0.3611030805286474?, x: 2.769292354238632?}] @@ -2551,7 +2567,7 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True x: 0.11535382288068... + 0.58974280502220...*I}, {y: 0.36110308052864..., x: 2.7692923542386...}, {y: 1.00000000000000, x: 1.00000000000000}] - sage: sorted(I.variety(ring=QQbar), key=str) # optional - sage.rings.number_field + sage: sorted(I.variety(ring=QQbar), key=str) # needs sage.rings.number_field [{y: 0.3194484597356763? + 1.633170240915238?*I, x: 0.11535382288068429? - 0.5897428050222055?*I}, {y: 0.3194484597356763? - 1.633170240915238?*I, @@ -2586,9 +2602,9 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True If the ground field's characteristic is too large for Singular, we resort to a toy implementation:: - sage: R. = PolynomialRing(GF(2147483659^3), order='lex') # optional - sage.rings.finite_rings - sage: I = ideal([x^3 - 2*y^2, 3*x + y^4]) # optional - sage.rings.finite_rings - sage: I.variety() # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(2147483659^3), order='lex') # needs sage.rings.finite_rings + sage: I = ideal([x^3 - 2*y^2, 3*x + y^4]) # needs sage.rings.finite_rings + sage: I.variety() # needs sage.rings.finite_rings verbose 0 (...: multi_polynomial_ideal.py, groebner_basis) Warning: falling back to very slow toy implementation. verbose 0 (...: multi_polynomial_ideal.py, dimension) Warning: falling back to very slow toy implementation. verbose 0 (...: multi_polynomial_ideal.py, variety) Warning: falling back to very slow toy implementation. @@ -2601,20 +2617,20 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True sage: K. = QQ[] sage: I = ideal([x^2 + 2*y - 5, x + y + 3]) - sage: v = I.variety(AA)[0]; v[x], v[y] # optional - sage.rings.number_field + sage: v = I.variety(AA)[0]; v[x], v[y] # needs sage.rings.number_field (4.464101615137755?, -7.464101615137755?) - sage: list(v)[0].parent() # optional - sage.rings.number_field + sage: list(v)[0].parent() # needs sage.rings.number_field Multivariate Polynomial Ring in x, y over Algebraic Real Field - sage: v[x] # optional - sage.rings.number_field + sage: v[x] # needs sage.rings.number_field 4.464101615137755? - sage: v["y"] # optional - sage.rings.number_field + sage: v["y"] # needs sage.rings.number_field -7.464101615137755? ``msolve`` also works over finite fields:: - sage: R. = PolynomialRing(GF(536870909), 2, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([x^2 - 1, y^2 - 1]) # optional - sage.rings.finite_rings - sage: sorted(I.variety(algorithm='msolve', # optional - msolve # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(536870909), 2, order='lex') # needs sage.rings.finite_rings + sage: I = Ideal([x^2 - 1, y^2 - 1]) # needs sage.rings.finite_rings + sage: sorted(I.variety(algorithm='msolve', # optional - msolve # needs sage.rings.finite_rings ....: proof=False), ....: key=str) [{x: 1, y: 1}, @@ -2625,9 +2641,9 @@ def variety(self, ring=None, *, algorithm="triangular_decomposition", proof=True but may fail in small characteristic, especially with ideals of high degree with respect to the characteristic:: - sage: R. = PolynomialRing(GF(3), 2, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([x^2 - 1, y^2 - 1]) # optional - sage.rings.finite_rings - sage: I.variety(algorithm='msolve', proof=False) # optional - msolve # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(3), 2, order='lex') # needs sage.rings.finite_rings + sage: I = Ideal([x^2 - 1, y^2 - 1]) # needs sage.rings.finite_rings + sage: I.variety(algorithm='msolve', proof=False) # optional - msolve, needs sage.rings.finite_rings Traceback (most recent call last): ... NotImplementedError: characteristic 3 too small @@ -2662,20 +2678,20 @@ def _variety_triangular_decomposition(self, ring): TESTS:: - sage: K. = GF(27) # optional - sage.rings.finite_rings - sage: P. = PolynomialRing(K, 2, order='lex') # optional - sage.rings.finite_rings - sage: I = Ideal([ x^8 + y + 2, y^6 + x*y^5 + x^2 ]) # optional - sage.rings.finite_rings + sage: K. = GF(27) # needs sage.rings.finite_rings + sage: P. = PolynomialRing(K, 2, order='lex') # needs sage.rings.finite_rings + sage: I = Ideal([ x^8 + y + 2, y^6 + x*y^5 + x^2 ]) # needs sage.rings.finite_rings Testing the robustness of the Singular interface:: - sage: T = I.triangular_decomposition('singular:triangLfak') # optional - sage.rings.finite_rings - sage: sorted(I.variety(), key=str) # optional - sage.rings.finite_rings + sage: T = I.triangular_decomposition('singular:triangLfak') # needs sage.rings.finite_rings + sage: sorted(I.variety(), key=str) # needs sage.rings.finite_rings [{y: w^2 + 2*w, x: 2*w + 2}, {y: w^2 + 2, x: 2*w}, {y: w^2 + w, x: 2*w + 1}] Testing that a bug is indeed fixed :: - sage: R = PolynomialRing(GF(2), 30, ['x%d'%(i+1) for i in range(30)], order='lex') # optional - sage.rings.finite_rings - sage: R.inject_variables() # optional - sage.rings.finite_rings + sage: R = PolynomialRing(GF(2), 30, ['x%d'%(i+1) for i in range(30)], order='lex') # needs sage.rings.finite_rings + sage: R.inject_variables() # needs sage.rings.finite_rings Defining... sage: I = Ideal([x1 + 1, x2, x3 + 1, x5*x10 + x10 + x18, x5*x11 + x11, \ x5*x18, x6, x7 + 1, x9, x10*x11 + x10 + x18, x10*x18 + x18, \ @@ -2687,9 +2703,9 @@ def _variety_triangular_decomposition(self, ring): x16^2 + x16, x17^2 + x17, x18^2 + x18, x19^2 + x19, x20^2 + x20, \ x21^2 + x21, x22^2 + x22, x23^2 + x23, x24^2 + x24, x25^2 + x25, \ x26^2 + x26, x27^2 + x27, x28^2 + x28, x29^2 + x29, x30^2 + x30]) # optional - sage.rings.finite_rings - sage: I.basis_is_groebner() + sage: I.basis_is_groebner() # needs sage.rings.finite_rings True - sage: sorted("".join(str(V[g]) for g in R.gens()) for V in I.variety()) # long time (6s on sage.math, 2011) # optional - sage.rings.finite_rings + sage: sorted("".join(str(V[g]) for g in R.gens()) for V in I.variety()) # long time (6s on sage.math, 2011), needs sage.rings.finite_rings ['101000100000000110001000100110', '101000100000000110001000101110', '101000100100000101001000100110', @@ -2742,7 +2758,7 @@ def _variety_triangular_decomposition(self, ring): sage: R. = PolynomialRing(QQ, order='lex') sage: I = R.ideal(c^2-2, b-c, a) - sage: I.variety(QQbar) # optional - sage.rings.number_field + sage: I.variety(QQbar) # needs sage.rings.number_field [...a: 0...] An early version of :trac:`25351` broke this method by adding the @@ -2750,13 +2766,14 @@ def _variety_triangular_decomposition(self, ring): that this circle and this hyperbola have two real intersections and two more complex ones:: - sage: K. = PolynomialRing(AA) # optional - sage.rings.number_field - sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) # optional - sage.rings.number_field - sage: len(I.variety()) # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: K. = PolynomialRing(AA) + sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) + sage: len(I.variety()) 2 - sage: K. = PolynomialRing(QQbar) # optional - sage.rings.number_field - sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) # optional - sage.rings.number_field - sage: len(I.variety()) # optional - sage.rings.number_field + sage: K. = PolynomialRing(QQbar) + sage: I = Ideal([ x*y - 1, (x-2)^2 + (y-1)^2 - 1]) + sage: len(I.variety()) 4 """ @@ -2842,7 +2859,7 @@ def hilbert_polynomial(self, algorithm='sage'): sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) - sage: I.hilbert_polynomial() # optional - sage.libs.flint + sage: I.hilbert_polynomial() # needs sage.libs.flint 5*t - 5 Of course, the Hilbert polynomial of a zero-dimensional ideal @@ -2853,14 +2870,14 @@ def hilbert_polynomial(self, algorithm='sage'): sage: J = P*[m.lm() for m in J0.groebner_basis()] sage: J.dimension() 0 - sage: J.hilbert_polynomial() # optional - sage.libs.flint + sage: J.hilbert_polynomial() # needs sage.libs.flint 0 It is possible to request a computation using the Singular library:: - sage: I.hilbert_polynomial(algorithm='singular') == I.hilbert_polynomial() # optional - sage.libs.flint sage.libs.singular + sage: I.hilbert_polynomial(algorithm='singular') == I.hilbert_polynomial() # needs sage.libs.flint True - sage: J.hilbert_polynomial(algorithm='singular') == J.hilbert_polynomial() # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_polynomial(algorithm='singular') == J.hilbert_polynomial() # needs sage.libs.flint True Here is a bigger examples:: @@ -2868,7 +2885,7 @@ def hilbert_polynomial(self, algorithm='sage'): sage: n = 4; m = 11; P = PolynomialRing(QQ, n * m, "x"); x = P.gens() sage: M = Matrix(n, x) sage: Minors = P.ideal(M.minors(2)) - sage: hp = Minors.hilbert_polynomial(); hp # optional - sage.libs.flint + sage: hp = Minors.hilbert_polynomial(); hp # needs sage.libs.flint 1/21772800*t^13 + 61/21772800*t^12 + 1661/21772800*t^11 + 26681/21772800*t^10 + 93841/7257600*t^9 + 685421/7257600*t^8 + 1524809/3110400*t^7 + 39780323/21772800*t^6 + 6638071/1360800*t^5 @@ -2879,7 +2896,7 @@ def hilbert_polynomial(self, algorithm='sage'): with Singular. We don't test it here, as it has a side-effect on other tests that is not understood yet (see :trac:`26300`):: - sage: Minors.hilbert_polynomial(algorithm='singular') # not tested # optional - sage.libs.singular + sage: Minors.hilbert_polynomial(algorithm='singular') # not tested Traceback (most recent call last): ... RuntimeError: error in Singular function call 'hilbPoly': @@ -2891,8 +2908,8 @@ def hilbert_polynomial(self, algorithm='sage'): coefficients of the Hilbert-Poincaré series in all degrees:: sage: P = PowerSeriesRing(QQ, 't', default_prec=50) - sage: hs = Minors.hilbert_series() # optional - sage.libs.flint - sage: list(P(hs.numerator()) / P(hs.denominator())) == [hp(t=k) # optional - sage.libs.flint + sage: hs = Minors.hilbert_series() # needs sage.libs.flint + sage: list(P(hs.numerator()) / P(hs.denominator())) == [hp(t=k) # needs sage.libs.flint ....: for k in range(50)] True @@ -2902,23 +2919,23 @@ def hilbert_polynomial(self, algorithm='sage'): sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3, x*y^2, y^4, x^2*y*z, y^3*z, x^2*z^2, x*y*z^2, x*z^3]) - sage: I.hilbert_polynomial(algorithm='singular') # optional - sage.libs.singular + sage: I.hilbert_polynomial(algorithm='singular') 3 - sage: I.hilbert_polynomial() # optional - sage.libs.flint + sage: I.hilbert_polynomial() # needs sage.libs.flint 3 Check that this method works over ``QQbar`` (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # optional - sage.rings.number_field - sage: I.hilbert_polynomial() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # needs sage.rings.number_field + sage: I.hilbert_polynomial() # needs sage.rings.number_field 5*t - 5 Check for :trac:`33597`:: sage: R. = QQ[] sage: I = R.ideal([X^2*Y^3, X*Z]) - sage: I.hilbert_polynomial() # optional - sage.libs.flint + sage: I.hilbert_polynomial() # needs sage.libs.flint t + 5 """ if not self.is_homogeneous(): @@ -2981,48 +2998,49 @@ def hilbert_series(self, grading=None, algorithm='sage'): sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) - sage: I.hilbert_series() # optional - sage.libs.flint + sage: I.hilbert_series() # needs sage.libs.flint (t^4 + t^3 + t^2 + t + 1)/(t^2 - 2*t + 1) sage: R. = PolynomialRing(QQ) sage: J = R.ideal([a^2*b, a*b^2]) - sage: J.hilbert_series() # optional - sage.libs.flint + sage: J.hilbert_series() # needs sage.libs.flint (t^3 - t^2 - t - 1)/(t - 1) - sage: J.hilbert_series(grading=(10,3)) # optional - sage.libs.flint + sage: J.hilbert_series(grading=(10,3)) # needs sage.libs.flint (t^25 + t^24 + t^23 - t^15 - t^14 - t^13 - t^12 - t^11 - t^10 - t^9 - t^8 - t^7 - t^6 - t^5 - t^4 - t^3 - t^2 - t - 1)/(t^12 + t^11 + t^10 - t^2 - t - 1) sage: K = R.ideal([a^2*b^3, a*b^4 + a^3*b^2]) - sage: K.hilbert_series(grading=[1,2]) # optional - sage.libs.flint + sage: K.hilbert_series(grading=[1,2]) # needs sage.libs.flint (t^11 + t^8 - t^6 - t^5 - t^4 - t^3 - t^2 - t - 1)/(t^2 - 1) - sage: K.hilbert_series(grading=[2,1]) # optional - sage.libs.flint + sage: K.hilbert_series(grading=[2,1]) # needs sage.libs.flint (2*t^7 - t^6 - t^4 - t^2 - 1)/(t - 1) TESTS:: - sage: I.hilbert_series() == I.hilbert_series(algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: # needs sage.libs.flint + sage: I.hilbert_series() == I.hilbert_series(algorithm='singular') True - sage: J.hilbert_series() == J.hilbert_series(algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_series() == J.hilbert_series(algorithm='singular') True - sage: J.hilbert_series(grading=(10,3)) == J.hilbert_series(grading=(10,3), algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_series(grading=(10,3)) == J.hilbert_series(grading=(10,3), algorithm='singular') True - sage: K.hilbert_series(grading=(1,2)) == K.hilbert_series(grading=(1,2), algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: K.hilbert_series(grading=(1,2)) == K.hilbert_series(grading=(1,2), algorithm='singular') True - sage: K.hilbert_series(grading=(2,1)) == K.hilbert_series(grading=(2,1), algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: K.hilbert_series(grading=(2,1)) == K.hilbert_series(grading=(2,1), algorithm='singular') True sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) - sage: I.hilbert_series(grading=5) # optional - sage.libs.flint + sage: I.hilbert_series(grading=5) # needs sage.libs.flint Traceback (most recent call last): ... TypeError: grading must be a list or a tuple of integers Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # optional - sage.rings.number_field - sage: I.hilbert_series() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # needs sage.rings.number_field + sage: I.hilbert_series() # needs sage.rings.number_field (t^4 + t^3 + t^2 + t + 1)/(t^2 - 2*t + 1) """ if not self.is_homogeneous(): @@ -3080,42 +3098,51 @@ def hilbert_numerator(self, grading=None, algorithm='sage'): sage: P. = PolynomialRing(QQ) sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) - sage: I.hilbert_numerator() # optional - sage.libs.flint + sage: I.hilbert_numerator() # needs sage.libs.flint -t^5 + 1 sage: R. = PolynomialRing(QQ) sage: J = R.ideal([a^2*b, a*b^2]) - sage: J.hilbert_numerator() # optional - sage.libs.flint + sage: J.hilbert_numerator() # needs sage.libs.flint t^4 - 2*t^3 + 1 - sage: J.hilbert_numerator(grading=(10,3)) # optional - sage.libs.flint + sage: J.hilbert_numerator(grading=(10,3)) # needs sage.libs.flint t^26 - t^23 - t^16 + 1 TESTS:: - sage: I.hilbert_numerator() == I.hilbert_numerator(algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: I.hilbert_numerator() == I.hilbert_numerator(algorithm='singular') # needs sage.libs.flint True - sage: J.hilbert_numerator() == J.hilbert_numerator(algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_numerator() == J.hilbert_numerator(algorithm='singular') # needs sage.libs.flint True - sage: J.hilbert_numerator(grading=(10,3)) == J.hilbert_numerator(grading=(10,3), algorithm='singular') # optional - sage.libs.flint sage.libs.singular + sage: J.hilbert_numerator(grading=(10,3)) == J.hilbert_numerator(grading=(10,3), algorithm='singular') # needs sage.libs.flint True Check that this method works over QQbar (:trac:`25351`):: - sage: P. = QQbar[] # optional - sage.rings.number_field - sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # optional - sage.rings.number_field - sage: I.hilbert_numerator() # optional - sage.rings.number_field + sage: P. = QQbar[] # needs sage.rings.number_field + sage: I = Ideal([x^3*y^2 + 3*x^2*y^2*z + y^3*z^2 + z^5]) # needs sage.rings.number_field + sage: I.hilbert_numerator() # needs sage.rings.number_field -t^5 + 1 + This example returns a wrong answer due to an integer overflow in Singular:: + + sage: n=4; m=11; P = PolynomialRing(QQ, n*m, "x"); x = P.gens(); M = Matrix(n, x) + sage: I = P.ideal(M.minors(2)) + sage: J = P * [m.lm() for m in I.groebner_basis()] + sage: J.hilbert_numerator(algorithm='singular') + ...120*t^33 - 3465*t^32 + 48180*t^31 - ... + Our two algorithms should always agree; not tested until :trac:`33178` is fixed:: - sage: m = ZZ.random_element(2,8) # not tested - sage: nvars = m^2 # not tested - sage: R = PolynomialRing(QQ, "x", nvars) # not tested - sage: M = matrix(R, m, R.gens()) # not tested - sage: I = R.ideal(M.minors(2)) # not tested - sage: n1 = I.hilbert_numerator() # not tested - sage: n2 = I.hilbert_numerator(algorithm='singular') # not tested - sage: n1 == n2 # not tested + sage: # not tested + sage: m = ZZ.random_element(2,8) + sage: nvars = m^2 + sage: R = PolynomialRing(QQ, "x", nvars) + sage: M = matrix(R, m, R.gens()) + sage: I = R.ideal(M.minors(2)) + sage: n1 = I.hilbert_numerator() + sage: n2 = I.hilbert_numerator(algorithm='singular') + sage: n1 == n2 True """ @@ -3272,19 +3299,20 @@ def normal_basis(self, degree=None, algorithm='libsingular', Check that this method works over QQbar (:trac:`25351`):: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: I = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5, x*z-1) # optional - sage.rings.number_field - sage: I.normal_basis() # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: R. = QQbar[] + sage: I = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5, x*z-1) + sage: I.normal_basis() [y*z^2, z^2, y*z, z, x*y, y, x, 1] - sage: J = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5) # optional - sage.rings.number_field - sage: [J.normal_basis(d) for d in (0..3)] # optional - sage.rings.number_field + sage: J = R.ideal(x^2+y^2+z^2-4, x^2+2*y^2-5) + sage: [J.normal_basis(d) for d in (0..3)] [[1], [z, y, x], [z^2, y*z, x*z, x*y], [z^3, y*z^2, x*z^2, x*y*z]] Check the option ``algorithm="singular"`` with a weighted term order:: sage: T = TermOrder('wdegrevlex', (1, 2, 3)) - sage: S. = PolynomialRing(GF(2), order=T) # optional - sage.rings.finite_rings - sage: S.ideal(x^6 + y^3 + z^2).normal_basis(6, algorithm='singular') # optional - sage.rings.finite_rings + sage: S. = PolynomialRing(GF(2), order=T) # needs sage.rings.finite_rings + sage: S.ideal(x^6 + y^3 + z^2).normal_basis(6, algorithm='singular') # needs sage.rings.finite_rings [x^4*y, x^2*y^2, y^3, x^3*z, x*y*z, z^2] """ from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence @@ -3364,14 +3392,15 @@ def _groebner_basis_macaulay2(self, strategy=None): Over finite fields, Macaulay2 supports different algorithms to compute Gröbner bases:: - sage: R = PolynomialRing(GF(101), 'x', 4) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R) # optional - sage.rings.finite_rings - sage: gb1 = I.groebner_basis('macaulay2:gb') # optional - macaulay2 # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R) # optional - sage.rings.finite_rings - sage: gb2 = I.groebner_basis('macaulay2:mgb') # optional - macaulay2 # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(R) # optional - sage.rings.finite_rings - sage: gb3 = I.groebner_basis('macaulay2:f4') # optional - macaulay2 # optional - sage.rings.finite_rings - sage: gb1 == gb2 == gb3 # optional - macaulay2 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R = PolynomialRing(GF(101), 'x', 4) + sage: I = sage.rings.ideal.Cyclic(R) + sage: gb1 = I.groebner_basis('macaulay2:gb') # optional - macaulay2 + sage: I = sage.rings.ideal.Cyclic(R) + sage: gb2 = I.groebner_basis('macaulay2:mgb') # optional - macaulay2 + sage: I = sage.rings.ideal.Cyclic(R) + sage: gb3 = I.groebner_basis('macaulay2:f4') # optional - macaulay2 + sage: gb1 == gb2 == gb3 # optional - macaulay2 True TESTS:: @@ -3435,26 +3464,27 @@ def __init__(self, ring, gens, coerce=True, side="left"): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], # indirect doctest # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], # indirect doctest ....: coerce=False) - sage: I # random # optional - sage.combinat sage.modules + sage: I # random Left Ideal (y^2, x^2, z^2 - 1) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(I.gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(I.gens(), key=str) [x^2, y^2, z^2 - 1] - sage: H.ideal([y^2, x^2, z^2 - H.one()], side="twosided") # random # optional - sage.combinat sage.modules + sage: H.ideal([y^2, x^2, z^2 - H.one()], side="twosided") # random Twosided Ideal (y^2, x^2, z^2 - 1) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(H.ideal([y^2, x^2, z^2 - H.one()], side="twosided").gens(), # optional - sage.combinat sage.modules + sage: sorted(H.ideal([y^2, x^2, z^2 - H.one()], side="twosided").gens(), ....: key=str) [x^2, y^2, z^2 - 1] - sage: H.ideal([y^2, x^2, z^2 - H.one()], side="right") # optional - sage.combinat sage.modules + sage: H.ideal([y^2, x^2, z^2 - H.one()], side="right") Traceback (most recent call last): ... ValueError: Only left and two-sided ideals are allowed. @@ -3479,15 +3509,16 @@ def __call_singular(self, cmd, arg=None): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: id = H.ideal(x + y, y + z) # optional - sage.combinat sage.modules - sage: id.std() # indirect doctest # random # optional - sage.combinat sage.modules + sage: id = H.ideal(x + y, y + z) + sage: id.std() # indirect doctest # random Left Ideal (z, y, x) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(id.std().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(id.std().gens(), key=str) [x, y, z] """ from sage.libs.singular.function import singular_function @@ -3504,16 +3535,17 @@ def std(self): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I.std() #random # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) + sage: I.std() #random Left Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(I.std().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(I.std().gens(), key=str) [2*x*y - z - 1, x*z + x, x^2, y*z - y, y^2, z^2 - 1] @@ -3521,37 +3553,38 @@ def std(self): Groebner basis. But if it is a two-sided ideal, then the output of :meth:`std` and :meth:`twostd` coincide:: - sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) # optional - sage.combinat sage.modules - sage: JL #random # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) + sage: JL #random Left Ideal (x^3, y^3, z^3 - 4*z) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(JL.gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(JL.gens(), key=str) [x^3, y^3, z^3 - 4*z] - sage: JL.std() # random # optional - sage.combinat sage.modules + sage: JL.std() # random Left Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, 2*x*y*z - z^2 - 2*z, y^3, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(JL.std().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(JL.std().gens(), key=str) [2*x*y*z - z^2 - 2*z, x*z^2 + 2*x*z, x^3, y*z^2 - 2*y*z, y^3, z^3 - 4*z] - sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') # optional - sage.combinat sage.modules - sage: JT #random # optional - sage.combinat sage.modules + sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') + sage: JT #random Twosided Ideal (x^3, y^3, z^3 - 4*z) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(JT.gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(JT.gens(), key=str) [x^3, y^3, z^3 - 4*z] - sage: JT.std() #random # optional - sage.combinat sage.modules + sage: JT.std() #random Twosided Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, y^2*z - 2*y^2, 2*x*y*z - z^2 - 2*z, x^2*z + 2*x^2, y^3, x*y^2 - y*z, x^2*y - x*z - 2*x, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: sorted(JT.std().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(JT.std().gens(), key=str) [2*x*y*z - z^2 - 2*z, x*y^2 - y*z, x*z^2 + 2*x*z, x^2*y - x*z - 2*x, x^2*z + 2*x^2, x^3, y*z^2 - 2*y*z, y^2*z - 2*y^2, y^3, z^3 - 4*z] - sage: JT.std() == JL.twostd() # optional - sage.combinat sage.modules + sage: JT.std() == JL.twostd() True ALGORITHM: Uses Singular's ``std`` command @@ -3568,20 +3601,21 @@ def elimination_ideal(self, variables): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I.elimination_ideal([x, z]) # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) + sage: I.elimination_ideal([x, z]) Left Ideal (y^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {...} - sage: J = I.twostd(); J # optional - sage.combinat sage.modules + sage: J = I.twostd(); J Twosided Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {...} - sage: J.elimination_ideal([x, z]) # optional - sage.combinat sage.modules + sage: J.elimination_ideal([x, z]) Twosided Ideal (y^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {...} @@ -3604,15 +3638,16 @@ def twostd(self): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I.twostd() #random # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], coerce=False) + sage: I.twostd() #random Twosided Ideal (z^2 - 1, y*z - y, x*z + x, y^2, 2*x*y - z - 1, x^2) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field... - sage: sorted(I.twostd().gens(), key=str) # optional - sage.combinat sage.modules + sage: sorted(I.twostd().gens(), key=str) [2*x*y - z - 1, x*z + x, x^2, y*z - y, y^2, z^2 - 1] ALGORITHM: Uses Singular's ``twostd`` command @@ -3631,10 +3666,11 @@ def _groebner_strategy(self): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I._groebner_strategy() # random # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) + sage: I._groebner_strategy() # random Groebner Strategy for ideal generated by 6 elements over Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} @@ -3655,27 +3691,28 @@ def reduce(self,p): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: I = H.ideal([y^2, x^2, z^2 - H.one()], # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: I = H.ideal([y^2, x^2, z^2 - H.one()], ....: coerce=False, side='twosided') - sage: Q = H.quotient(I); Q #random # optional - sage.combinat sage.modules + sage: Q = H.quotient(I); Q #random Quotient of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} by the ideal (y^2, x^2, z^2 - 1) - sage: Q.2^2 == Q.one() # indirect doctest # optional - sage.combinat sage.modules + sage: Q.2^2 == Q.one() # indirect doctest True Here, we see that the relation that we just found in the quotient is actually a consequence of the given relations:: - sage: H.2^2 - H.one() in I.std().gens() # optional - sage.combinat sage.modules + sage: H.2^2 - H.one() in I.std().gens() # needs sage.combinat sage.modules True Here is the corresponding direct test:: - sage: I.reduce(z^2) # optional - sage.combinat sage.modules + sage: I.reduce(z^2) # needs sage.combinat sage.modules 1 """ @@ -3687,15 +3724,16 @@ def _contains_(self,p): We define a left and a two-sided ideal:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) # optional - sage.combinat sage.modules - sage: JL.std() #random # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H. = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: JL = H.ideal([x^3, y^3, z^3 - 4*z]) + sage: JL.std() #random Left Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, 2*x*y*z - z^2 - 2*z, y^3, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, nc-relations: {z*x: x*z + 2*x, z*y: y*z - 2*y, y*x: x*y - z} - sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') # optional - sage.combinat sage.modules - sage: JT.std() #random # optional - sage.combinat sage.modules + sage: JT = H.ideal([x^3, y^3, z^3 - 4*z], side='twosided') + sage: JT.std() #random Twosided Ideal (z^3 - 4*z, y*z^2 - 2*y*z, x*z^2 + 2*x*z, y^2*z - 2*y^2, 2*x*y*z - z^2 - 2*z, x^2*z + 2*x^2, y^3, x*y^2 - y*z, x^2*y - x*z - 2*x, x^3) of Noncommutative Multivariate Polynomial Ring in x, y, z over Rational Field, @@ -3704,9 +3742,9 @@ def _contains_(self,p): Apparently, ``x*y^2-y*z`` should be in the two-sided, but not in the left ideal:: - sage: x*y^2-y*z in JL #indirect doctest # optional - sage.combinat sage.modules + sage: x*y^2-y*z in JL #indirect doctest # needs sage.combinat sage.modules False - sage: x*y^2-y*z in JT # optional - sage.combinat sage.modules + sage: x*y^2-y*z in JT # needs sage.combinat sage.modules True """ @@ -3726,12 +3764,13 @@ def syzygy_module(self): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: G = vector(I.gens()); G # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) + sage: G = vector(I.gens()); G d...: UserWarning: You are constructing a free module over a noncommutative ring. Sage does not have a concept of left/right and both sided modules, so be careful. @@ -3743,7 +3782,7 @@ def syzygy_module(self): It's also not guaranteed that all multiplications are done from the right side. (y^2, x^2, z^2 - 1) - sage: M = I.syzygy_module(); M # optional - sage.combinat sage.modules + sage: M = I.syzygy_module(); M [ -z^2 - 8*z - 15 0 y^2] [ 0 -z^2 + 8*z - 15 x^2] [ x^2*z^2 + 8*x^2*z + 15*x^2 -y^2*z^2 + 8*y^2*z - 15*y^2 -4*x*y*z + 2*z^2 + 2*z] @@ -3754,7 +3793,7 @@ def syzygy_module(self): [ x^4*z + 4*x^4 -x^2*y^2*z + 4*x^2*y^2 - 4*x*y*z^2 + 32*x*y*z - 6*z^3 - 64*x*y + 66*z^2 - 240*z + 288 0] [x^3*y^2*z + 4*x^3*y^2 + 18*x^2*y*z - 36*x*z^3 + 66*x^2*y - 432*x*z^2 - 1656*x*z - 2052*x -x*y^4*z + 4*x*y^4 - 8*y^3*z^2 + 62*y^3*z - 114*y^3 48*y*z^2 - 36*y*z] - sage: M*G # optional - sage.combinat sage.modules + sage: M*G # needs sage.combinat sage.modules (0, 0, 0, 0, 0, 0, 0, 0, 0) ALGORITHM: Uses Singular's ``syz`` command @@ -3781,12 +3820,13 @@ def res(self, length): EXAMPLES:: - sage: A. = FreeAlgebra(QQ, 3) # optional - sage.combinat sage.modules - sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) # optional - sage.combinat sage.modules - sage: H.inject_variables() # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = FreeAlgebra(QQ, 3) + sage: H = A.g_algebra({y*x: x*y-z, z*x: x*z+2*x, z*y: y*z-2*y}) + sage: H.inject_variables() Defining x, y, z - sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) # optional - sage.combinat sage.modules - sage: I.res(3) # optional - sage.combinat sage.modules + sage: I = H.ideal([y^2, x^2, z^2-H.one()], coerce=False) + sage: I.res(3) """ if self.side() == 'twosided': @@ -3816,8 +3856,8 @@ def __init__(self, ring, gens, coerce=True): sage: R. = PolynomialRing(IntegerRing(), 2, order='lex') sage: R.ideal([x, y]) Ideal (x, y) of Multivariate Polynomial Ring in x, y over Integer Ring - sage: R. = GF(3)[] # optional - sage.rings.finite_rings - sage: R.ideal([x0^2, x1^3]) # optional - sage.rings.finite_rings + sage: R. = GF(3)[] # needs sage.rings.finite_rings + sage: R.ideal([x0^2, x1^3]) # needs sage.rings.finite_rings Ideal (x0^2, x1^3) of Multivariate Polynomial Ring in x0, x1 over Finite Field of size 3 """ Ideal_generic.__init__(self, ring, gens, coerce=coerce) @@ -3903,22 +3943,24 @@ def __richcmp__(self, other, op): :: - sage: R. = GF(32003)[] # optional - sage.rings.finite_rings - sage: I = R*[x^2 + x, y] # optional - sage.rings.finite_rings - sage: J = R*[x + 1, y] # optional - sage.rings.finite_rings - sage: J < I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(32003)[] + sage: I = R*[x^2 + x, y] + sage: J = R*[x + 1, y] + sage: J < I False - sage: I < J # optional - sage.rings.finite_rings + sage: I < J True :: - sage: R. = GF(32003)[] # optional - sage.rings.finite_rings - sage: I = R*[x^2 + x, y] # optional - sage.rings.finite_rings - sage: J = R*[x + 1, y] # optional - sage.rings.finite_rings - sage: J > I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(32003)[] + sage: I = R*[x^2 + x, y] + sage: J = R*[x + 1, y] + sage: J > I True - sage: I > J # optional - sage.rings.finite_rings + sage: I > J False :: @@ -3956,15 +3998,16 @@ def __richcmp__(self, other, op): We test to make sure that pickling works with the cached Groebner basis:: - sage: R. = GF(32003)[] # optional - sage.rings.finite_rings - sage: I = R*[x^2 + x, y] # optional - sage.rings.finite_rings - sage: J = R*[x + 1, y] # optional - sage.rings.finite_rings - sage: J >= I # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(32003)[] + sage: I = R*[x^2 + x, y] + sage: J = R*[x + 1, y] + sage: J >= I True - sage: I >= J # optional - sage.rings.finite_rings + sage: I >= J False - sage: loads(dumps(I)).__getstate__() # optional - sage.rings.finite_rings + sage: loads(dumps(I)).__getstate__() # needs sage.rings.finite_rings (Monoid of ideals of Multivariate Polynomial Ring in x, y over Finite Field of size 32003, {'_Ideal_generic__gens': (x^2 + x, y), '_Ideal_generic__ring': Multivariate Polynomial Ring in x, y over Finite Field of size 32003, @@ -4330,31 +4373,31 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal Here we use Macaulay2 with three different strategies over a finite field. :: - sage: R. = PolynomialRing(GF(101), 3) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('macaulay2:gb') # optional - macaulay2 # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(101), 3) # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('macaulay2:gb') # optional - macaulay2 # needs sage.rings.finite_rings [c^3 + 28*c^2 - 37*b + 13*c, b^2 - 41*c^2 + 20*b - 20*c, b*c - 19*c^2 + 10*b + 40*c, a + 2*b + 2*c - 1] - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('macaulay2:f4') # optional - macaulay2 # optional - sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('macaulay2:f4') # optional - macaulay2 # needs sage.rings.finite_rings [c^3 + 28*c^2 - 37*b + 13*c, b^2 - 41*c^2 + 20*b - 20*c, b*c - 19*c^2 + 10*b + 40*c, a + 2*b + 2*c - 1] - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('macaulay2:mgb') # optional - macaulay2 # optional - sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('macaulay2:mgb') # optional - macaulay2 # needs sage.rings.finite_rings [c^3 + 28*c^2 - 37*b + 13*c, b^2 - 41*c^2 + 20*b - 20*c, b*c - 19*c^2 + 10*b + 40*c, a + 2*b + 2*c - 1] Over prime fields of small characteristic, we can also use the `optional package msolve <../spkg/msolve.html>`_:: - sage: R. = PolynomialRing(GF(101), 3) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('msolve') # optional - msolve # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(101), 3) # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('msolve') # optional - msolve # needs sage.rings.finite_rings [a + 2*b + 2*c - 1, b*c - 19*c^2 + 10*b + 40*c, b^2 - 41*c^2 + 20*b - 20*c, c^3 + 28*c^2 - 37*b + 13*c] :: - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma # optional - sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma, needs sage.rings.finite_rings [a - 60*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 - 79/7*c^2 + 3/7*c, c^4 - 10/21*c^3 + 1/84*c^2 + 1/84*c] Singular and libSingular can compute Groebner basis with degree @@ -4527,54 +4570,55 @@ def groebner_basis(self, algorithm='', deg_bound=None, mult_bound=None, prot=Fal Check that this method works over QQbar (:trac:`25351`):: - sage: P. = PolynomialRing(QQbar, 3, order='lex') # optional - sage.rings.number_field - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis() # optional - sage.rings.number_field + sage: P. = PolynomialRing(QQbar, 3, order='lex') # needs sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis() # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:groebner') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:groebner') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:std') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:std') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:stdhilb') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:stdhilb') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:stdfglm') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:stdfglm') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('libsingular:slimgb') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('libsingular:slimgb') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: J = I.change_ring(P.change_ring(order='degrevlex')) # optional - sage.rings.number_field - sage: gb = J.groebner_basis('giac') # random # optional - sage.rings.number_field - sage: gb # optional - sage.rings.number_field + sage: # needs sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching + sage: J = I.change_ring(P.change_ring(order='degrevlex')) + sage: gb = J.groebner_basis('giac') # random + sage: gb [c^3 + (-79/210)*c^2 + 1/30*b + 1/70*c, b^2 + (-3/5)*c^2 + (-1/5)*b + 1/5*c, b*c + 6/5*c^2 + (-1/10)*b + (-2/5)*c, a + 2*b + 2*c - 1] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('toy:buchberger2') # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('toy:buchberger2') # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('macaulay2:gb') # optional - macaulay2 # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('macaulay2:gb') # optional - macaulay2 # needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] - sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # optional - sage.rings.number_field - sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma # optional - sage.rings.number_field + sage: I = sage.rings.ideal.Katsura(P,3) # regenerate to prevent caching # needs sage.rings.number_field + sage: I.groebner_basis('magma:GroebnerBasis') # optional - magma, needs sage.rings.number_field [a + (-60)*c^3 + 158/7*c^2 + 8/7*c - 1, b + 30*c^3 + (-79/7)*c^2 + 3/7*c, c^4 + (-10/21)*c^3 + 1/84*c^2 + 1/84*c] msolve currently supports the degrevlex order only:: - sage: R. = PolynomialRing(GF(101), 3, order='lex') # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # optional - sage.rings.finite_rings - sage: I.groebner_basis('msolve') # optional - msolve # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(GF(101), 3, order='lex') # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Katsura(R,3) # regenerate to prevent caching # needs sage.rings.finite_rings + sage: I.groebner_basis('msolve') # optional - msolve # needs sage.rings.finite_rings Traceback (most recent call last): ... NotImplementedError: msolve only supports the degrevlex order (use transformed_basis()) @@ -4927,28 +4971,28 @@ def homogenize(self, var='h'): EXAMPLES:: - sage: P. = PolynomialRing(GF(2)) # optional - sage.rings.finite_rings - sage: I = Ideal([x^2*y + z + 1, x + y^2 + 1]); I # optional - sage.rings.finite_rings + sage: P. = PolynomialRing(GF(2)) # needs sage.rings.finite_rings + sage: I = Ideal([x^2*y + z + 1, x + y^2 + 1]); I # needs sage.rings.finite_rings Ideal (x^2*y + z + 1, y^2 + x + 1) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 2 :: - sage: I.homogenize() # optional - sage.rings.finite_rings + sage: I.homogenize() # needs sage.rings.finite_rings Ideal (x^2*y + z*h^2 + h^3, y^2 + x*h + h^2) of Multivariate Polynomial Ring in x, y, z, h over Finite Field of size 2 :: - sage: I.homogenize(y) # optional - sage.rings.finite_rings + sage: I.homogenize(y) # needs sage.rings.finite_rings Ideal (x^2*y + y^3 + y^2*z, x*y) of Multivariate Polynomial Ring in x, y, z over Finite Field of size 2 :: - sage: I = Ideal([x^2*y + z^3 + y^2*x, x + y^2 + 1]) # optional - sage.rings.finite_rings - sage: I.homogenize() # optional - sage.rings.finite_rings + sage: I = Ideal([x^2*y + z^3 + y^2*x, x + y^2 + 1]) # needs sage.rings.finite_rings + sage: I.homogenize() # needs sage.rings.finite_rings Ideal (x^2*y + x*y^2 + z^3, y^2 + x*h + h^2) of Multivariate Polynomial Ring in x, y, z, h over Finite Field of size 2 @@ -5024,51 +5068,51 @@ def degree_of_semi_regularity(self): We consider a homogeneous example:: sage: n = 8 - sage: K = GF(127) # optional - sage.rings.finite_rings - sage: P = PolynomialRing(K, n, 'x') # optional - sage.rings.finite_rings - sage: s = [K.random_element() for _ in range(n)] # optional - sage.rings.finite_rings - sage: L = [] # optional - sage.rings.finite_rings - sage: for i in range(2 * n): # optional - sage.rings.finite_rings + sage: K = GF(127) # needs sage.rings.finite_rings + sage: P = PolynomialRing(K, n, 'x') # needs sage.rings.finite_rings + sage: s = [K.random_element() for _ in range(n)] # needs sage.rings.finite_rings + sage: L = [] # needs sage.rings.finite_rings + sage: for i in range(2 * n): # needs sage.rings.finite_rings ....: f = P.random_element(degree=2, terms=binomial(n, 2)) ....: f -= f(*s) ....: L.append(f.homogenize()) - sage: I = Ideal(L) # optional - sage.rings.finite_rings - sage: I.degree_of_semi_regularity() # optional - sage.rings.finite_rings + sage: I = Ideal(L) # needs sage.rings.finite_rings + sage: I.degree_of_semi_regularity() # needs sage.rings.finite_rings 4 From this, we expect a Groebner basis computation to reach at most degree 4. For homogeneous systems this is equivalent to the largest degree in the Groebner basis:: - sage: max(f.degree() for f in I.groebner_basis()) # optional - sage.rings.finite_rings + sage: max(f.degree() for f in I.groebner_basis()) # needs sage.rings.finite_rings 4 We increase the number of polynomials and observe a decrease the degree of regularity:: - sage: for i in range(2 * n): # optional - sage.rings.finite_rings + sage: for i in range(2 * n): # needs sage.rings.finite_rings ....: f = P.random_element(degree=2, terms=binomial(n, 2)) ....: f -= f(*s) ....: L.append(f.homogenize()) - sage: I = Ideal(L) # optional - sage.rings.finite_rings - sage: I.degree_of_semi_regularity() # optional - sage.rings.finite_rings + sage: I = Ideal(L) # needs sage.rings.finite_rings + sage: I.degree_of_semi_regularity() # needs sage.rings.finite_rings 3 - sage: max(f.degree() for f in I.groebner_basis()) # optional - sage.rings.finite_rings + sage: max(f.degree() for f in I.groebner_basis()) # needs sage.rings.finite_rings 3 The degree of regularity approaches 2 for quadratic systems as the number of polynomials approaches `n^2`:: - sage: for i in range((n-4) * n): # optional - sage.rings.finite_rings + sage: for i in range((n-4) * n): # needs sage.rings.finite_rings ....: f = P.random_element(degree=2, terms=binomial(n, 2)) ....: f -= f(*s) ....: L.append(f.homogenize()) - sage: I = Ideal(L) # optional - sage.rings.finite_rings - sage: I.degree_of_semi_regularity() # optional - sage.rings.finite_rings + sage: I = Ideal(L) # needs sage.rings.finite_rings + sage: I.degree_of_semi_regularity() # needs sage.rings.finite_rings 2 - sage: max(f.degree() for f in I.groebner_basis()) # optional - sage.rings.finite_rings + sage: max(f.degree() for f in I.groebner_basis()) # needs sage.rings.finite_rings 2 .. NOTE:: @@ -5117,45 +5161,45 @@ def plot(self, *args, **kwds): sage: R. = PolynomialRing(QQ, 2) sage: I = R.ideal([y^3 - x^2]) - sage: I.plot() # cusp # optional - sage.plot + sage: I.plot() # cusp # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: I = R.ideal([y^2 - x^2 - 1]) - sage: I.plot((x,-3, 3), (y, -2, 2)) # hyperbola # optional - sage.plot + sage: I.plot((x,-3, 3), (y, -2, 2)) # hyperbola # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: I = R.ideal([y^2 + x^2*(1/4) - 1]) - sage: I.plot() # ellipse # optional - sage.plot + sage: I.plot() # ellipse # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: I = R.ideal([y^2-(x^2-1)*(x-2)]) - sage: I.plot() # elliptic curve # optional - sage.plot + sage: I.plot() # elliptic curve # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: f = ((x+3)^3 + 2*(x+3)^2 - y^2)*(x^3 - y^2)*((x-3)^3-2*(x-3)^2-y^2) sage: I = R.ideal(f) - sage: I.plot() # the Singular logo # optional - sage.plot + sage: I.plot() # the Singular logo # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: R. = PolynomialRing(QQ, 2) sage: I = R.ideal([x - 1]) - sage: I.plot((y, -2, 2)) # vertical line # optional - sage.plot + sage: I.plot((y, -2, 2)) # vertical line # needs sage.plot Graphics object consisting of 1 graphics primitive :: sage: I = R.ideal([-x^2*y + 1]) - sage: I.plot() # blow up # optional - sage.plot + sage: I.plot() # blow up # needs sage.plot Graphics object consisting of 1 graphics primitive """ @@ -5234,12 +5278,13 @@ def random_element(self, degree, compute_gb=False, *args, **kwds): We compute a uniformly random element up to the provided degree. :: - sage: P. = GF(127)[] # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(P) # optional - sage.rings.finite_rings - sage: f = I.random_element(degree=4, compute_gb=True, terms=infinity) # optional - sage.rings.finite_rings - sage: f.degree() <= 4 # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = GF(127)[] + sage: I = sage.rings.ideal.Katsura(P) + sage: f = I.random_element(degree=4, compute_gb=True, terms=infinity) + sage: f.degree() <= 4 True - sage: len(list(f)) <= 35 # optional - sage.rings.finite_rings + sage: len(list(f)) <= 35 True Note that sampling uniformly at random from the ideal at some large enough degree is @@ -5247,45 +5292,47 @@ def random_element(self, degree, compute_gb=False, *args, **kwds): basis if we can sample uniformly at random from an ideal:: sage: n = 3; d = 4 - sage: P = PolynomialRing(GF(127), n, 'x') # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Cyclic(P) # optional - sage.rings.finite_rings + sage: P = PolynomialRing(GF(127), n, 'x') # needs sage.rings.finite_rings + sage: I = sage.rings.ideal.Cyclic(P) # needs sage.rings.finite_rings 1. We sample `n^d` uniformly random elements in the ideal:: - sage: F = Sequence(I.random_element(degree=d, compute_gb=True, # optional - sage.rings.finite_rings + sage: F = Sequence(I.random_element(degree=d, compute_gb=True, # needs sage.rings.finite_rings ....: terms=infinity) ....: for _ in range(n^d)) 2. We linearize and compute the echelon form:: - sage: A, v = F.coefficient_matrix() # optional - sage.rings.finite_rings - sage: A.echelonize() # optional - sage.rings.finite_rings + sage: A, v = F.coefficient_matrix() # needs sage.rings.finite_rings + sage: A.echelonize() # needs sage.rings.finite_rings 3. The result is the desired Gröbner basis:: - sage: G = Sequence((A * v).list()) # optional - sage.rings.finite_rings - sage: G.is_groebner() # optional - sage.rings.finite_rings + sage: G = Sequence((A * v).list()) # needs sage.rings.finite_rings + sage: G.is_groebner() # needs sage.rings.finite_rings True - sage: Ideal(G) == I # optional - sage.rings.finite_rings + sage: Ideal(G) == I # needs sage.rings.finite_rings True We return some element in the ideal with no guarantee on the distribution:: - sage: P = PolynomialRing(GF(127), 10, 'x') # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(P) # optional - sage.rings.finite_rings - sage: f = I.random_element(degree=3) # optional - sage.rings.finite_rings - sage: f # random # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P = PolynomialRing(GF(127), 10, 'x') + sage: I = sage.rings.ideal.Katsura(P) + sage: f = I.random_element(degree=3) + sage: f # random -25*x0^2*x1 + 14*x1^3 + 57*x0*x1*x2 + ... + 19*x7*x9 + 40*x8*x9 + 49*x1 - sage: f.degree() # optional - sage.rings.finite_rings + sage: f.degree() 3 We show that the default method does not sample uniformly at random from the ideal:: - sage: P. = GF(127)[] # optional - sage.rings.finite_rings - sage: G = Sequence([x + 7, y - 2, z + 110]) # optional - sage.rings.finite_rings - sage: I = Ideal([sum(P.random_element() * g for g in G) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = GF(127)[] + sage: G = Sequence([x + 7, y - 2, z + 110]) + sage: I = Ideal([sum(P.random_element() * g for g in G) ....: for _ in range(4)]) - sage: all(I.random_element(degree=1) == 0 for _ in range(100)) # optional - sage.rings.finite_rings + sage: all(I.random_element(degree=1) == 0 for _ in range(100)) True If ``degree`` equals the degree of the generators, a random linear @@ -5350,33 +5397,35 @@ def weil_restriction(self): EXAMPLES:: - sage: k. = GF(2^2) # optional - sage.rings.finite_rings - sage: P. = PolynomialRing(k, 2) # optional - sage.rings.finite_rings - sage: I = Ideal([x*y + 1, a*x + 1]) # optional - sage.rings.finite_rings - sage: I.variety() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(2^2) + sage: P. = PolynomialRing(k, 2) + sage: I = Ideal([x*y + 1, a*x + 1]) + sage: I.variety() [{y: a, x: a + 1}] - sage: J = I.weil_restriction() # optional - sage.rings.finite_rings - sage: J # optional - sage.rings.finite_rings + sage: J = I.weil_restriction() + sage: J Ideal (x0*y0 + x1*y1 + 1, x1*y0 + x0*y1 + x1*y1, x1 + 1, x0 + x1) of Multivariate Polynomial Ring in x0, x1, y0, y1 over Finite Field of size 2 - sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal # optional - sage.rings.finite_rings - sage: J.variety() # optional - sage.rings.finite_rings + sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal + sage: J.variety() [{y1: 1, y0: 0, x1: 1, x0: 1}] - sage: J.weil_restriction() # returns J # optional - sage.rings.finite_rings + sage: J.weil_restriction() # returns J # needs sage.rings.finite_rings Ideal (x0*y0 + x1*y1 + 1, x1*y0 + x0*y1 + x1*y1, x1 + 1, x0 + x1, x0^2 + x0, x1^2 + x1, y0^2 + y0, y1^2 + y1) of Multivariate Polynomial Ring in x0, x1, y0, y1 over Finite Field of size 2 - sage: k. = GF(3^5) # optional - sage.rings.finite_rings - sage: P. = PolynomialRing(k) # optional - sage.rings.finite_rings - sage: I = sage.rings.ideal.Katsura(P) # optional - sage.rings.finite_rings - sage: I.dimension() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k. = GF(3^5) + sage: P. = PolynomialRing(k) + sage: I = sage.rings.ideal.Katsura(P) + sage: I.dimension() 0 - sage: I.variety() # optional - sage.rings.finite_rings + sage: I.variety() [{z: 0, y: 0, x: 1}] - sage: J = I.weil_restriction(); J # optional - sage.rings.finite_rings + sage: J = I.weil_restriction(); J # needs sage.rings.finite_rings Ideal (x0 - y0 - z0 - 1, x1 - y1 - z1, x2 - y2 - z2, x3 - y3 - z3, x4 - y4 - z4, x0^2 + x2*x3 + x1*x4 - y0^2 - y2*y3 - y1*y4 - z0^2 - z2*z3 - z1*z4 - x0, @@ -5401,9 +5450,9 @@ def weil_restriction(self): - y4*z0 - y3*z1 - y2*z2 - y1*z3 - y0*z4 - y4*z4 - y4) of Multivariate Polynomial Ring in x0, x1, x2, x3, x4, y0, y1, y2, y3, y4, z0, z1, z2, z3, z4 over Finite Field of size 3 - sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal # optional - sage.rings.finite_rings + sage: J += sage.rings.ideal.FieldIdeal(J.ring()) # ensure radical ideal # needs sage.rings.finite_rings sage: from sage.doctest.fixtures import reproducible_repr - sage: print(reproducible_repr(J.variety())) # optional - sage.rings.finite_rings + sage: print(reproducible_repr(J.variety())) # needs sage.rings.finite_rings [{x0: 1, x1: 0, x2: 0, x3: 0, x4: 0, y0: 0, y1: 0, y2: 0, y3: 0, y4: 0, z0: 0, z1: 0, z2: 0, z3: 0, z4: 0}] @@ -5412,15 +5461,15 @@ def weil_restriction(self): Weil restrictions are often used to study elliptic curves over extension fields so we give a simple example involving those:: - sage: K. = QuadraticField(1/3) # optional - sage.rings.number_field - sage: E = EllipticCurve(K, [1,2,3,4,5]) # optional - sage.rings.number_field + sage: K. = QuadraticField(1/3) # needs sage.rings.number_field + sage: E = EllipticCurve(K, [1,2,3,4,5]) # needs sage.rings.number_field We pick a point on ``E``:: - sage: p = E.lift_x(1); p # optional - sage.rings.number_field + sage: p = E.lift_x(1); p # needs sage.rings.number_field (1 : -6 : 1) - sage: I = E.defining_ideal(); I # optional - sage.rings.number_field + sage: I = E.defining_ideal(); I # needs sage.rings.number_field Ideal (-x^3 - 2*x^2*z + x*y*z + y^2*z - 4*x*z^2 + 3*y*z^2 - 5*z^3) of Multivariate Polynomial Ring in x, y, z over Number Field in a with defining polynomial x^2 - 1/3 @@ -5428,20 +5477,20 @@ def weil_restriction(self): Of course, the point ``p`` is a root of all generators of ``I``:: - sage: I.subs(x=1, y=2, z=1) # optional - sage.rings.number_field + sage: I.subs(x=1, y=2, z=1) # needs sage.rings.number_field Ideal (0) of Multivariate Polynomial Ring in x, y, z over Number Field in a with defining polynomial x^2 - 1/3 with a = 0.5773502691896258? ``I`` is also radical:: - sage: I.radical() == I # optional - sage.rings.number_field + sage: I.radical() == I # needs sage.rings.number_field True So we compute its Weil restriction:: - sage: J = I.weil_restriction() # optional - sage.rings.number_field - sage: J # optional - sage.rings.number_field + sage: J = I.weil_restriction() # needs sage.rings.number_field + sage: J # needs sage.rings.number_field Ideal (-x0^3 - x0*x1^2 - 2*x0^2*z0 - 2/3*x1^2*z0 + x0*y0*z0 + y0^2*z0 + 1/3*x1*y1*z0 + 1/3*y1^2*z0 - 4*x0*z0^2 + 3*y0*z0^2 - 5*z0^3 - 4/3*x0*x1*z1 + 1/3*x1*y0*z1 + 1/3*x0*y1*z1 + 2/3*y0*y1*z1 @@ -5454,19 +5503,19 @@ def weil_restriction(self): We can check that the point ``p`` is still a root of all generators of ``J``:: - sage: J.subs(x0=1, y0=2, z0=1, x1=0, y1=0, z1=0) # optional - sage.rings.number_field + sage: J.subs(x0=1, y0=2, z0=1, x1=0, y1=0, z1=0) # needs sage.rings.number_field Ideal (0, 0) of Multivariate Polynomial Ring in x0, x1, y0, y1, z0, z1 over Rational Field Example for relative number fields:: sage: R. = QQ[] - sage: K. = NumberField(x^5 - 2) # optional - sage.rings.number_field - sage: R. = K[] # optional - sage.rings.number_field - sage: L. = K.extension(x^2 + 1) # optional - sage.rings.number_field - sage: S. = L[] # optional - sage.rings.number_field - sage: I = S.ideal([y^2 - x^3 - 1]) # optional - sage.rings.number_field - sage: I.weil_restriction() # optional - sage.rings.number_field + sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field + sage: R. = K[] # needs sage.rings.number_field + sage: L. = K.extension(x^2 + 1) # needs sage.rings.number_field + sage: S. = L[] # needs sage.rings.number_field + sage: I = S.ideal([y^2 - x^3 - 1]) # needs sage.rings.number_field + sage: I.weil_restriction() # needs sage.rings.number_field Ideal (-x0^3 + 3*x0*x1^2 + y0^2 - y1^2 - 1, -3*x0^2*x1 + x1^3 + 2*y0*y1) of Multivariate Polynomial Ring in x0, x1, y0, y1 over Number Field in w with defining polynomial x^5 - 2 diff --git a/src/sage/rings/polynomial/polynomial_ring.py b/src/sage/rings/polynomial/polynomial_ring.py index 5e6203a05c3..5e4a52943fc 100644 --- a/src/sage/rings/polynomial/polynomial_ring.py +++ b/src/sage/rings/polynomial/polynomial_ring.py @@ -47,17 +47,18 @@ We create a polynomial ring over a quaternion algebra:: - sage: A. = QuaternionAlgebra(QQ, -1,-1) # optional - sage.combinat sage.modules - sage: R. = PolynomialRing(A, sparse=True) # optional - sage.combinat sage.modules - sage: f = w^3 + (i+j)*w + 1 # optional - sage.combinat sage.modules - sage: f # optional - sage.combinat sage.modules + sage: # needs sage.combinat sage.modules + sage: A. = QuaternionAlgebra(QQ, -1,-1) + sage: R. = PolynomialRing(A, sparse=True) + sage: f = w^3 + (i+j)*w + 1 + sage: f w^3 + (i + j)*w + 1 - sage: f^2 # optional - sage.combinat sage.modules + sage: f^2 w^6 + (2*i + 2*j)*w^4 + 2*w^3 - 2*w^2 + (2*i + 2*j)*w + 1 - sage: f = w + i ; g = w + j # optional - sage.combinat sage.modules - sage: f * g # optional - sage.combinat sage.modules + sage: f = w + i ; g = w + j + sage: f * g w^2 + (i + j)*w + k - sage: g * f # optional - sage.combinat sage.modules + sage: g * f w^2 + (i + j)*w - k :trac:`9944` introduced some changes related with @@ -88,28 +89,29 @@ different base rings. In that situation, coercion works by means of the :func:`~sage.categories.pushout.pushout` formalism:: - sage: R. = PolynomialRing(GF(5), sparse=True) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = PolynomialRing(GF(5), sparse=True) sage: S. = PolynomialRing(ZZ) - sage: R.has_coerce_map_from(S) # optional - sage.rings.finite_rings + sage: R.has_coerce_map_from(S) False - sage: S.has_coerce_map_from(R) # optional - sage.rings.finite_rings + sage: S.has_coerce_map_from(R) False - sage: S.0 + R.0 # optional - sage.rings.finite_rings + sage: S.0 + R.0 2*x - sage: (S.0 + R.0).parent() # optional - sage.rings.finite_rings + sage: (S.0 + R.0).parent() Univariate Polynomial Ring in x over Finite Field of size 5 - sage: (S.0 + R.0).parent().is_sparse() # optional - sage.rings.finite_rings + sage: (S.0 + R.0).parent().is_sparse() False Similarly, there is a coercion from the (non-default) NTL implementation for univariate polynomials over the integers to the default FLINT implementation, but not vice versa:: - sage: R. = PolynomialRing(ZZ, implementation='NTL') # optional - sage.libs.ntl - sage: S. = PolynomialRing(ZZ, implementation='FLINT') # optional - sage.libs.flint - sage: (S.0 + R.0).parent() is S # optional - sage.libs.flint sage.libs.ntl + sage: R. = PolynomialRing(ZZ, implementation='NTL') # needs sage.libs.ntl + sage: S. = PolynomialRing(ZZ, implementation='FLINT') # needs sage.libs.flint + sage: (S.0 + R.0).parent() is S # needs sage.libs.flint sage.libs.ntl True - sage: (R.0 + S.0).parent() is S # optional - sage.libs.flint sage.libs.ntl + sage: (R.0 + S.0).parent() is S # needs sage.libs.flint sage.libs.ntl True TESTS:: @@ -122,9 +124,9 @@ Check that :trac:`5562` has been fixed:: sage: R. = PolynomialRing(RDF, 1) - sage: v1 = vector([u]) # optional - sage.modules - sage: v2 = vector([CDF(2)]) # optional - sage.modules - sage: v1 * v2 # optional - sage.modules + sage: v1 = vector([u]) # needs sage.modules + sage: v2 = vector([CDF(2)]) # needs sage.modules + sage: v1 * v2 # needs sage.modules 2.0*u """ @@ -210,11 +212,11 @@ def is_PolynomialRing(x): :: - sage: R. = PolynomialRing(ZZ, implementation="singular"); R # optional - sage.libs.singular + sage: R. = PolynomialRing(ZZ, implementation="singular"); R # needs sage.libs.singular Multivariate Polynomial Ring in w over Integer Ring - sage: is_PolynomialRing(R) # optional - sage.libs.singular + sage: is_PolynomialRing(R) # needs sage.libs.singular False - sage: type(R) # optional - sage.libs.singular + sage: type(R) # needs sage.libs.singular """ return isinstance(x, PolynomialRing_general) @@ -243,7 +245,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, and Category of commutative algebras over (euclidean domains and infinite enumerated sets and metric spaces) and Category of infinite sets - sage: category(GF(7)['x']) # optional - sage.rings.finite_rings + sage: category(GF(7)['x']) # needs sage.rings.finite_rings Join of Category of euclidean domains and Category of commutative algebras over (finite enumerated fields and subquotients of monoids and quotients of semigroups) and Category of infinite sets @@ -268,13 +270,13 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: Zmod(1)['x'].is_finite() True - sage: GF(7)['x'].is_finite() # optional - sage.rings.finite_rings + sage: GF(7)['x'].is_finite() # needs sage.rings.finite_rings False sage: Zmod(1)['x']['y'].is_finite() True - sage: GF(7)['x']['y'].is_finite() # optional - sage.rings.finite_rings + sage: GF(7)['x']['y'].is_finite() # needs sage.rings.finite_rings False """ @@ -357,9 +359,9 @@ def _element_constructor_(self, x=None, check=True, is_gen=False, Coercing in pari elements:: - sage: QQ['x'](pari('[1,2,3/5]')) # optional - sage.libs.pari + sage: QQ['x'](pari('[1,2,3/5]')) # needs sage.libs.pari 3/5*x^2 + 2*x + 1 - sage: QQ['x'](pari('(-1/3)*x^10 + (2/3)*x - 1/5')) # optional - sage.libs.pari + sage: QQ['x'](pari('(-1/3)*x^10 + (2/3)*x - 1/5')) # needs sage.libs.pari -1/3*x^10 + 2/3*x - 1/5 Coercing strings:: @@ -377,10 +379,10 @@ def _element_constructor_(self, x=None, check=True, is_gen=False, This shows that the issue at :trac:`4106` is fixed:: - sage: x = var('x') # optional - sage.symbolic + sage: x = var('x') # needs sage.symbolic sage: R = IntegerModRing(4) - sage: S = R['x'] # optional - sage.symbolic - sage: S(x) # optional - sage.symbolic + sage: S = R['x'] # needs sage.symbolic + sage: S(x) # needs sage.symbolic x Throw a TypeError if any of the coefficients cannot be coerced @@ -393,16 +395,17 @@ def _element_constructor_(self, x=None, check=True, is_gen=False, Check that the bug in :trac:`11239` is fixed:: - sage: K. = GF(5^2, prefix='z') # optional - sage.rings.finite_rings - sage: L. = GF(5^4, prefix='z') # optional - sage.rings.finite_rings - sage: f = K['x'].gen() + a # optional - sage.rings.finite_rings - sage: L['x'](f) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: K. = GF(5^2, prefix='z') + sage: L. = GF(5^4, prefix='z') + sage: f = K['x'].gen() + a + sage: L['x'](f) x + b^3 + b^2 + b + 3 A test from :trac:`14485` :: - sage: x = SR.var('x') # optional - sage.symbolic - sage: QQbar[x](x^6 + x^5 + x^4 - x^3 + x^2 - x + 2/5) # optional - sage.rings.number_field sage.symbolic + sage: x = SR.var('x') # needs sage.symbolic + sage: QQbar[x](x^6 + x^5 + x^4 - x^3 + x^2 - x + 2/5) # needs sage.rings.number_field sage.symbolic x^6 + x^5 + x^4 - x^3 + x^2 - x + 2/5 Check support for unicode characters (:trac:`29280`):: @@ -694,7 +697,7 @@ def _coerce_map_from_base_ring(self): Polynomial base injection morphism: From: Rational Field To: Univariate Polynomial Ring in x over Rational Field - sage: R.coerce_map_from(GF(7)) # optional - sage.rings.finite_rings + sage: R.coerce_map_from(GF(7)) # needs sage.rings.finite_rings """ from .polynomial_element import PolynomialBaseringInjection @@ -762,20 +765,20 @@ def _coerce_map_from_(self, P): Over the integers, there is a coercion from the NTL and generic implementation to the default FLINT implementation:: - sage: R = PolynomialRing(ZZ, 't', implementation="NTL") # optional - sage.libs.ntl - sage: S = PolynomialRing(ZZ, 't', implementation="FLINT") # optional - sage.libs.flint + sage: R = PolynomialRing(ZZ, 't', implementation="NTL") # needs sage.libs.ntl + sage: S = PolynomialRing(ZZ, 't', implementation="FLINT") # needs sage.libs.flint sage: T = PolynomialRing(ZZ, 't', implementation="generic") - sage: R.has_coerce_map_from(S) # optional - sage.libs.flint sage.libs.ntl + sage: R.has_coerce_map_from(S) # needs sage.libs.flint sage.libs.ntl False - sage: R.has_coerce_map_from(T) # optional - sage.libs.ntl + sage: R.has_coerce_map_from(T) # needs sage.libs.ntl False - sage: S.has_coerce_map_from(T) # optional - sage.libs.flint + sage: S.has_coerce_map_from(T) # needs sage.libs.flint True - sage: S.has_coerce_map_from(R) # optional - sage.libs.flint sage.libs.ntl + sage: S.has_coerce_map_from(R) # needs sage.libs.flint sage.libs.ntl True - sage: T.has_coerce_map_from(R) # optional - sage.libs.ntl + sage: T.has_coerce_map_from(R) # needs sage.libs.ntl False - sage: T.has_coerce_map_from(S) # optional - sage.libs.flint + sage: T.has_coerce_map_from(S) # needs sage.libs.flint False """ base_ring = self.base_ring() @@ -836,19 +839,20 @@ def _magma_init_(self, magma): EXAMPLES:: + sage: # optional - magma sage: R = QQ['y'] - sage: R._magma_init_(magma) # optional - magma + sage: R._magma_init_(magma) 'SageCreateWithNames(PolynomialRing(_sage_ref...),["y"])' - sage: S = magma(R) # optional - magma - sage: S # optional - magma + sage: S = magma(R) + sage: S Univariate Polynomial Ring in y over Rational Field - sage: S.1 # optional - magma + sage: S.1 y - sage: magma(PolynomialRing(GF(7), 'x')) # optional - magma # optional - sage.rings.finite_rings + sage: magma(PolynomialRing(GF(7), 'x')) # needs sage.rings.finite_rings Univariate Polynomial Ring in x over GF(7) - sage: magma(PolynomialRing(GF(49,'a'), 'x')) # optional - magma # optional - sage.rings.finite_rings + sage: magma(PolynomialRing(GF(49,'a'), 'x')) # needs sage.rings.finite_rings Univariate Polynomial Ring in x over GF(7^2) - sage: magma(PolynomialRing(PolynomialRing(ZZ,'w'), 'x')) # optional - magma + sage: magma(PolynomialRing(PolynomialRing(ZZ,'w'), 'x')) Univariate Polynomial Ring in x over Univariate Polynomial Ring in w over Integer Ring Watch out, Magma has different semantics from Sage, i.e., in Magma @@ -858,19 +862,20 @@ def _magma_init_(self, magma): :: - sage: m = Magma() # new magma session; optional - magma - sage: m(QQ['w']) # optional - magma + sage: # optional - magma + sage: m = Magma() + sage: m(QQ['w']) Univariate Polynomial Ring in w over Rational Field - sage: m(QQ['x']) # optional - magma + sage: m(QQ['x']) Univariate Polynomial Ring in x over Rational Field - sage: m(QQ['w']) # same magma object, now prints as x; optional - magma + sage: m(QQ['w']) Univariate Polynomial Ring in x over Rational Field A nested example over a Givaro finite field:: - sage: k. = GF(9) # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: magma(a^2*x^3 + (a+1)*x + a) # optional - magma # optional - sage.rings.finite_rings + sage: k. = GF(9) # needs sage.rings.finite_rings + sage: R. = k[] # needs sage.rings.finite_rings + sage: magma(a^2*x^3 + (a+1)*x + a) # optional - magma, needs sage.rings.finite_rings a^2*x^3 + a^2*x + a """ B = magma(self.base_ring()) @@ -889,23 +894,24 @@ def _gap_init_(self, gap=None): EXAMPLES:: sage: R. = ZZ[] - sage: gap(R) # optional - sage.libs.gap + sage: gap(R) # needs sage.libs.gap PolynomialRing( Integers, ["z"] ) - sage: gap(R) is gap(R) # optional - sage.libs.gap + sage: gap(R) is gap(R) # needs sage.libs.gap True - sage: gap(z^2 + z) # optional - sage.libs.gap + sage: gap(z^2 + z) # needs sage.libs.gap z^2+z A univariate polynomial ring over a multivariate polynomial ring over a number field:: + sage: # needs sage.rings.number_field sage: Q. = QQ[] - sage: K. = NumberField(t^2 + t + 1) # optional - sage.rings.number_field - sage: P. = K[] # optional - sage.rings.number_field - sage: S. = P[] # optional - sage.rings.number_field - sage: gap(S) # optional - sage.libs.gap sage.rings.number_field + sage: K. = NumberField(t^2 + t + 1) + sage: P. = K[] + sage: S. = P[] + sage: gap(S) # needs sage.libs.gap PolynomialRing( PolynomialRing( , ["x", "y"] ), ["z"] ) - sage: gap(S) is gap(S) # optional - sage.libs.gap sage.rings.number_field + sage: gap(S) is gap(S) # needs sage.libs.gap True """ if gap is not None: @@ -921,11 +927,11 @@ def _sage_input_(self, sib, coerced): EXAMPLES:: - sage: sage_input(GF(5)['x']['y'], verify=True) # optional - sage.rings.finite_rings + sage: sage_input(GF(5)['x']['y'], verify=True) # needs sage.rings.finite_rings # Verified GF(5)['x']['y'] - sage: from sage.misc.sage_input import SageInputBuilder # optional - sage.rings.finite_rings - sage: ZZ['z']._sage_input_(SageInputBuilder(), False) # optional - sage.rings.finite_rings + sage: from sage.misc.sage_input import SageInputBuilder # needs sage.rings.finite_rings + sage: ZZ['z']._sage_input_(SageInputBuilder(), False) # needs sage.rings.finite_rings {constr_parent: {subscr: {atomic:ZZ}[{atomic:'z'}]} with gens: ('z',)} """ base = sib(self.base_ring()) @@ -964,9 +970,9 @@ def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None): EXAMPLES:: sage: R. = QQ[] - sage: R._is_valid_homomorphism_(GF(7), [5]) # optional - sage.rings.finite_rings + sage: R._is_valid_homomorphism_(GF(7), [5]) # needs sage.rings.finite_rings False - sage: R._is_valid_homomorphism_(Qp(7), [5]) # optional - sage.rings.padics + sage: R._is_valid_homomorphism_(Qp(7), [5]) # needs sage.rings.padics True """ # Since poly rings are free, any image of the gen @@ -1044,7 +1050,7 @@ def change_ring(self, R): sage: R. = RealIntervalField()[]; R Univariate Polynomial Ring in ZZZ over Real Interval Field with 53 bits of precision - sage: R.change_ring(GF(19^2, 'b')) # optional - sage.rings.finite_rings + sage: R.change_ring(GF(19^2, 'b')) # needs sage.rings.finite_rings Univariate Polynomial Ring in ZZZ over Finite Field in b of size 19^2 """ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -1148,9 +1154,9 @@ def characteristic(self): Univariate Polynomial Ring in ZZZ over Real Interval Field with 53 bits of precision sage: R.characteristic() 0 - sage: S = R.change_ring(GF(19^2, 'b')); S # optional - sage.rings.finite_rings + sage: S = R.change_ring(GF(19^2, 'b')); S # needs sage.rings.finite_rings Univariate Polynomial Ring in ZZZ over Finite Field in b of size 19^2 - sage: S.characteristic() + sage: S.characteristic() # needs sage.rings.finite_rings 19 """ return self.base_ring().characteristic() @@ -1165,23 +1171,24 @@ def cyclotomic_polynomial(self, n): EXAMPLES:: sage: R = ZZ['x'] - sage: R.cyclotomic_polynomial(8) # optional - sage.libs.pari + sage: R.cyclotomic_polynomial(8) # needs sage.libs.pari x^4 + 1 - sage: R.cyclotomic_polynomial(12) # optional - sage.libs.pari + sage: R.cyclotomic_polynomial(12) # needs sage.libs.pari x^4 - x^2 + 1 - sage: S = PolynomialRing(FiniteField(7), 'x') # optional - sage.rings.finite_rings - sage: S.cyclotomic_polynomial(12) # optional - sage.rings.finite_rings + + sage: S = PolynomialRing(FiniteField(7), 'x') # needs sage.rings.finite_rings + sage: S.cyclotomic_polynomial(12) # needs sage.rings.finite_rings x^4 + 6*x^2 + 1 - sage: S.cyclotomic_polynomial(1) # optional - sage.rings.finite_rings + sage: S.cyclotomic_polynomial(1) # needs sage.rings.finite_rings x + 6 TESTS: Make sure it agrees with other systems for the trivial case:: - sage: ZZ['x'].cyclotomic_polynomial(1) # optional - sage.libs.pari + sage: ZZ['x'].cyclotomic_polynomial(1) # needs sage.libs.pari x - 1 - sage: gp('polcyclo(1)') # optional - sage.libs.pari + sage: gp('polcyclo(1)') # needs sage.libs.pari x - 1 """ if n <= 0: @@ -1309,16 +1316,18 @@ def krull_dimension(self): sage: R. = QQ[] sage: R.krull_dimension() 1 - sage: R. = GF(9, 'a')[]; R # optional - sage.rings.finite_rings + + sage: # needs sage.rings.finite_rings + sage: R. = GF(9, 'a')[]; R Univariate Polynomial Ring in z over Finite Field in a of size 3^2 - sage: R.krull_dimension() # optional - sage.rings.finite_rings + sage: R.krull_dimension() 1 - sage: S. = R[] # optional - sage.rings.finite_rings - sage: S.krull_dimension() # optional - sage.rings.finite_rings + sage: S. = R[] + sage: S.krull_dimension() 2 - sage: for n in range(10): # optional - sage.rings.finite_rings + sage: for n in range(10): ....: S = PolynomialRing(S, 'w') - sage: S.krull_dimension() # optional - sage.rings.finite_rings + sage: S.krull_dimension() 12 """ return self.base_ring().krull_dimension() + 1 @@ -1392,13 +1401,13 @@ def random_element(self, degree=(-1,2), *args, **kwds): Check that :trac:`16682` is fixed:: - sage: R = PolynomialRing(GF(2), 'z') # optional - sage.rings.finite_rings - sage: for _ in range(100): # optional - sage.rings.finite_rings + sage: R = PolynomialRing(GF(2), 'z') # needs sage.rings.finite_rings + sage: for _ in range(100): # needs sage.rings.finite_rings ....: d = randint(-1,20) ....: P = R.random_element(degree=d) ....: assert P.degree() == d, "problem with {} which has not degree {}".format(P,d) - sage: R.random_element(degree=-2) # optional - sage.rings.finite_rings + sage: R.random_element(degree=-2) # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: degree should be an integer greater or equal than -1 @@ -1495,12 +1504,12 @@ def _Karatsuba_threshold(self): EXAMPLES:: - sage: R. = QQbar[] # optional - sage.rings.number_field - sage: R._Karatsuba_threshold # optional - sage.rings.number_field + sage: R. = QQbar[] # needs sage.rings.number_field + sage: R._Karatsuba_threshold # needs sage.rings.number_field 8 - sage: MS = MatrixSpace(ZZ, 2, 2) # optional - sage.modules - sage: R. = MS[] # optional - sage.modules - sage: R._Karatsuba_threshold # optional - sage.modules + sage: MS = MatrixSpace(ZZ, 2, 2) # needs sage.modules + sage: R. = MS[] # needs sage.modules + sage: R._Karatsuba_threshold # needs sage.modules 0 """ base_ring = self.base_ring() @@ -1569,8 +1578,9 @@ def polynomials( self, of_degree=None, max_degree=None ): EXAMPLES:: - sage: P = PolynomialRing(GF(3), 'y') # optional - sage.rings.finite_rings - sage: for p in P.polynomials(of_degree=2): print(p) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P = PolynomialRing(GF(3), 'y') + sage: for p in P.polynomials(of_degree=2): print(p) y^2 y^2 + 1 y^2 + 2 @@ -1589,7 +1599,7 @@ def polynomials( self, of_degree=None, max_degree=None ): 2*y^2 + 2*y 2*y^2 + 2*y + 1 2*y^2 + 2*y + 2 - sage: for p in P.polynomials(max_degree=1): print(p) # optional - sage.rings.finite_rings + sage: for p in P.polynomials(max_degree=1): print(p) 0 1 2 @@ -1599,7 +1609,7 @@ def polynomials( self, of_degree=None, max_degree=None ): 2*y 2*y + 1 2*y + 2 - sage: for p in P.polynomials(max_degree=1, of_degree=3): print(p) # optional - sage.rings.finite_rings + sage: for p in P.polynomials(max_degree=1, of_degree=3): print(p) Traceback (most recent call last): ... ValueError: you should pass exactly one of of_degree and max_degree @@ -1636,8 +1646,9 @@ def monics( self, of_degree=None, max_degree=None ): EXAMPLES:: - sage: P = PolynomialRing(GF(4, 'a'), 'y') # optional - sage.rings.finite_rings - sage: for p in P.monics(of_degree=2): print(p) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P = PolynomialRing(GF(4, 'a'), 'y') + sage: for p in P.monics(of_degree=2): print(p) y^2 y^2 + a y^2 + a + 1 @@ -1654,13 +1665,13 @@ def monics( self, of_degree=None, max_degree=None ): y^2 + y + a y^2 + y + a + 1 y^2 + y + 1 - sage: for p in P.monics(max_degree=1): print(p) # optional - sage.rings.finite_rings + sage: for p in P.monics(max_degree=1): print(p) 1 y y + a y + a + 1 y + 1 - sage: for p in P.monics(max_degree=1, of_degree=3): print(p) # optional - sage.rings.finite_rings + sage: for p in P.monics(max_degree=1, of_degree=3): print(p) Traceback (most recent call last): ... ValueError: you should pass exactly one of of_degree and max_degree @@ -1712,7 +1723,7 @@ def quotient_by_principal_ideal(self, f, names=None, **kwds): sage: R. = QQ[] sage: I = (x^2 - 1) * R - sage: R.quotient_by_principal_ideal(I) # optional - sage.libs.pari + sage: R.quotient_by_principal_ideal(I) # needs sage.libs.pari Univariate Quotient Polynomial Ring in xbar over Rational Field with modulus x^2 - 1 @@ -1720,7 +1731,7 @@ def quotient_by_principal_ideal(self, f, names=None, **kwds): and customizing the variable name:: sage: R. = QQ[] - sage: R.quotient_by_principal_ideal(x^2 - 1, names=('foo',)) # optional - sage.libs.pari + sage: R.quotient_by_principal_ideal(x^2 - 1, names=('foo',)) # needs sage.libs.pari Univariate Quotient Polynomial Ring in foo over Rational Field with modulus x^2 - 1 @@ -1729,9 +1740,9 @@ def quotient_by_principal_ideal(self, f, names=None, **kwds): Quotienting by the zero ideal returns ``self`` (:trac:`5978`):: sage: R = QQ['x'] - sage: R.quotient_by_principal_ideal(R.zero_ideal()) is R # optional - sage.libs.pari + sage: R.quotient_by_principal_ideal(R.zero_ideal()) is R # needs sage.libs.pari True - sage: R.quotient_by_principal_ideal(0) is R # optional - sage.libs.pari + sage: R.quotient_by_principal_ideal(0) is R # needs sage.libs.pari True """ from sage.rings.ideal import Ideal @@ -1749,9 +1760,9 @@ def weyl_algebra(self): EXAMPLES:: sage: R = QQ['x'] - sage: W = R.weyl_algebra(); W # optional - sage.combinat sage.modules + sage: W = R.weyl_algebra(); W # needs sage.combinat sage.modules Differential Weyl algebra of polynomials in x over Rational Field - sage: W.polynomial_ring() == R # optional - sage.combinat sage.modules + sage: W.polynomial_ring() == R # needs sage.combinat sage.modules True """ from sage.algebras.weyl_algebra import DifferentialWeylAlgebra @@ -1814,12 +1825,12 @@ def __init__(self, base_ring, name="x", sparse=False, implementation=None, sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_integral_domain as PRing sage: R = PRing(ZZ, 'x'); R Univariate Polynomial Ring in x over Integer Ring - sage: type(R.gen()) # optional - sage.libs.flint + sage: type(R.gen()) # needs sage.libs.flint - sage: R = PRing(ZZ, 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(ZZ, 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Integer Ring (using NTL) - sage: type(R.gen()) # optional - sage.libs.ntl + sage: type(R.gen()) # needs sage.libs.ntl """ self._implementation_repr = '' @@ -1887,20 +1898,20 @@ def weil_polynomials(self, d, q, sign=1, lead=1): EXAMPLES:: + sage: # needs sage.libs.flint sage: R. = ZZ[] - sage: L = R.weil_polynomials(4, 2) # optional - sage.libs.flint - sage: len(L) # optional - sage.libs.flint + sage: L = R.weil_polynomials(4, 2) + sage: len(L) 35 - sage: L[9] # optional - sage.libs.flint + sage: L[9] T^4 + T^3 + 2*T^2 + 2*T + 4 - sage: all(p.is_weil_polynomial() for p in L) # optional - sage.libs.flint + sage: all(p.is_weil_polynomial() for p in L) True Setting multiple leading coefficients:: sage: R. = QQ[] - sage: l = R.weil_polynomials(4, 2, lead=((1,0), (2,4), (1,2))) # optional - sage.libs.flint - sage: l # optional - sage.libs.flint + sage: l = R.weil_polynomials(4, 2, lead=((1,0), (2,4), (1,2))); l # needs sage.libs.flint [T^4 + 2*T^3 + 5*T^2 + 4*T + 4, T^4 + 2*T^3 + 3*T^2 + 4*T + 4, T^4 - 2*T^3 + 5*T^2 - 4*T + 4, @@ -1910,43 +1921,43 @@ def weil_polynomials(self, d, q, sign=1, lead=1): polynomials associated to K3 surfaces over `\GF{2}` of Picard number at least 12:: sage: R. = QQ[] - sage: l = R.weil_polynomials(10, 1, lead=2) # optional - sage.libs.flint - sage: len(l) # optional - sage.libs.flint + sage: l = R.weil_polynomials(10, 1, lead=2) # needs sage.libs.flint + sage: len(l) # needs sage.libs.flint 4865 - sage: l[len(l)//2] # optional - sage.libs.flint + sage: l[len(l)//2] # needs sage.libs.flint 2*T^10 + T^8 + T^6 + T^4 + T^2 + 2 TESTS: We check that products of Weil polynomials are also listed as Weil polynomials:: - sage: all((f * g) in R.weil_polynomials(6, q) for q in [3, 4] # optional - sage.libs.flint + sage: all((f * g) in R.weil_polynomials(6, q) for q in [3, 4] # needs sage.libs.flint ....: for f in R.weil_polynomials(2, q) for g in R.weil_polynomials(4, q)) True We check that irreducible Weil polynomials of degree 6 are CM:: - sage: simples = [f for f in R.weil_polynomials(6, 3) if f.is_irreducible()] # optional - sage.libs.flint - sage: len(simples) # optional - sage.libs.flint + sage: simples = [f for f in R.weil_polynomials(6, 3) if f.is_irreducible()] # needs sage.libs.flint + sage: len(simples) # needs sage.libs.flint 348 - sage: reals = [R([f[3+i] + sum((-3)^j * (i+2*j)/(i+j) * binomial(i+j,j) * f[3+i+2*j] # optional - sage.libs.flint + sage: reals = [R([f[3+i] + sum((-3)^j * (i+2*j)/(i+j) * binomial(i+j,j) * f[3+i+2*j] # needs sage.libs.flint ....: for j in range(1, (3+i)//2 + 1)) ....: for i in range(4)]) for f in simples] Check that every polynomial in this list has 3 real roots between `-2 \sqrt{3}` and `2 \sqrt{3}`:: - sage: roots = [f.roots(RR, multiplicities=False) for f in reals] # optional - sage.libs.flint - sage: all(len(L) == 3 and all(x^2 <= 12 for x in L) for L in roots) # optional - sage.libs.flint + sage: roots = [f.roots(RR, multiplicities=False) for f in reals] # needs sage.libs.flint + sage: all(len(L) == 3 and all(x^2 <= 12 for x in L) for L in roots) # needs sage.libs.flint True Finally, check that the original polynomials are reconstructed as CM polynomials:: - sage: all(f == T^3*r(T + 3/T) for (f, r) in zip(simples, reals)) # optional - sage.libs.flint + sage: all(f == T^3*r(T + 3/T) for (f, r) in zip(simples, reals)) # needs sage.libs.flint True A simple check (not sufficient):: - sage: all(f.number_of_real_roots() == 0 for f in simples) # optional - sage.libs.flint + sage: all(f.number_of_real_roots() == 0 for f in simples) # needs sage.libs.flint True """ R = self.base_ring() @@ -1985,7 +1996,7 @@ def _repr_(self): TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_integral_domain as PRing - sage: R = PRing(ZZ, 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(ZZ, 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Integer Ring (using NTL) """ s = PolynomialRing_commutative._repr_(self) @@ -2005,11 +2016,11 @@ def construction(self): sage: functor.implementation is None True - sage: R = PRing(ZZ, 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(ZZ, 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Integer Ring (using NTL) - sage: functor, arg = R.construction(); functor, arg # optional - sage.libs.ntl + sage: functor, arg = R.construction(); functor, arg # needs sage.libs.ntl (Poly[x], Integer Ring) - sage: functor.implementation # optional - sage.libs.ntl + sage: functor.implementation # needs sage.libs.ntl 'NTL' """ implementation = None @@ -2034,7 +2045,7 @@ def __init__(self, base_ring, name="x", sparse=False, implementation=None, sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_field as PRing sage: R = PRing(QQ, 'x'); R Univariate Polynomial Ring in x over Rational Field - sage: type(R.gen()) # optional - sage.libs.flint + sage: type(R.gen()) # needs sage.libs.flint sage: R = PRing(QQ, 'x', sparse=True); R Sparse Univariate Polynomial Ring in x over Rational Field @@ -2047,8 +2058,8 @@ def __init__(self, base_ring, name="x", sparse=False, implementation=None, Demonstrate that :trac:`8762` is fixed:: - sage: R. = PolynomialRing(GF(next_prime(10^20)), sparse=True) # optional - sage.rings.finite_rings - sage: x^(10^20) # this should be fast + sage: R. = PolynomialRing(GF(next_prime(10^20)), sparse=True) # needs sage.rings.finite_rings + sage: x^(10^20) # this should be fast # needs sage.rings.finite_rings x^100000000000000000000 """ def _element_class(): @@ -2094,8 +2105,8 @@ def _ideal_class_(self, n=0): EXAMPLES:: - sage: R. = GF(5)[] # optional - sage.rings.finite_rings - sage: R._ideal_class_() # optional - sage.rings.finite_rings + sage: R. = GF(5)[] # needs sage.rings.finite_rings + sage: R._ideal_class_() # needs sage.rings.finite_rings """ from sage.rings.polynomial.ideal import Ideal_1poly_field @@ -2247,15 +2258,17 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r -2 sage: f(-4) 9 - sage: R = PolynomialRing(GF(2**3, 'a'), 'x') # optional - sage.rings.finite_rings - sage: a = R.base_ring().gen() # optional - sage.rings.finite_rings - sage: f = R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)]); f # optional - sage.rings.finite_rings + + sage: # needs sage.rings.finite_rings + sage: R = PolynomialRing(GF(2**3, 'a'), 'x') + sage: a = R.base_ring().gen() + sage: f = R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)]); f a^2*x^2 + a^2*x + a^2 - sage: f(a^2 + a) # optional - sage.rings.finite_rings + sage: f(a^2 + a) a - sage: f(a) # optional - sage.rings.finite_rings + sage: f(a) 1 - sage: f(a^2) # optional - sage.rings.finite_rings + sage: f(a^2) a^2 + a + 1 Now use a memory efficient version of Neville's method:: @@ -2267,9 +2280,10 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r -11/7*x + 19/7, -17/42*x^2 - 83/42*x + 53/7, -23/84*x^3 - 11/84*x^2 + 13/7*x + 1] - sage: R = PolynomialRing(GF(2**3, 'a'), 'x') # optional - sage.rings.finite_rings - sage: a = R.base_ring().gen() # optional - sage.rings.finite_rings - sage: R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)], # optional - sage.rings.finite_rings + + sage: R = PolynomialRing(GF(2**3, 'a'), 'x') # needs sage.rings.finite_rings + sage: a = R.base_ring().gen() # needs sage.rings.finite_rings + sage: R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)], # needs sage.rings.finite_rings ....: algorithm="neville") [a^2 + a + 1, x + a + 1, a^2*x^2 + a^2*x + a^2] @@ -2281,10 +2295,12 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r sage: R.lagrange_polynomial([(0,1), (2,2), (3,-2), (-4,9)], ....: algorithm="neville", previous_row=p)[-1] -23/84*x^3 - 11/84*x^2 + 13/7*x + 1 - sage: R = PolynomialRing(GF(2**3, 'a'), 'x') # optional - sage.rings.finite_rings - sage: a = R.base_ring().gen() # optional - sage.rings.finite_rings - sage: p = R.lagrange_polynomial([(a^2+a, a), (a, 1)], algorithm="neville") # optional - sage.rings.finite_rings - sage: R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)], # optional - sage.rings.finite_rings + + sage: # needs sage.rings.finite_rings + sage: R = PolynomialRing(GF(2**3, 'a'), 'x') + sage: a = R.base_ring().gen() + sage: p = R.lagrange_polynomial([(a^2+a, a), (a, 1)], algorithm="neville") + sage: R.lagrange_polynomial([(a^2+a, a), (a, 1), (a^2, a^2+a+1)], ....: algorithm="neville", previous_row=p)[-1] a^2*x^2 + a^2*x + a^2 @@ -2327,10 +2343,10 @@ def lagrange_polynomial(self, points, algorithm="divided_difference", previous_r Check that base fields of positive characteristic are treated correctly (see :trac:`9787`):: - sage: R. = GF(101)[] # optional - sage.rings.finite_rings - sage: R.lagrange_polynomial([[1, 0], [2, 0]]) # optional - sage.rings.finite_rings + sage: R. = GF(101)[] # needs sage.rings.finite_rings + sage: R.lagrange_polynomial([[1, 0], [2, 0]]) # needs sage.rings.finite_rings 0 - sage: R.lagrange_polynomial([[1, 0], [2, 0], [3, 0]]) # optional - sage.rings.finite_rings + sage: R.lagrange_polynomial([[1, 0], [2, 0], [3, 0]]) # needs sage.rings.finite_rings 0 """ # Perhaps we should be slightly stricter on the input and use @@ -2416,8 +2432,8 @@ def fraction_field(self): EXAMPLES:: - sage: R. = GF(5)[] # optional - sage.rings.finite_rings - sage: R.fraction_field() # optional - sage.rings.finite_rings + sage: R. = GF(5)[] # needs sage.rings.finite_rings + sage: R.fraction_field() # needs sage.rings.finite_rings Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 5 @@ -2425,16 +2441,18 @@ def fraction_field(self): Check that :trac:`25449` has been resolved:: - sage: k = GF(25453) # optional - sage.rings.finite_rings - sage: F. = FunctionField(k) # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: t(x) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k = GF(25453) + sage: F. = FunctionField(k) + sage: R. = k[] + sage: t(x) x - sage: k = GF(55667) # optional - sage.rings.finite_rings - sage: F. = FunctionField(k) # optional - sage.rings.finite_rings - sage: R. = k[] # optional - sage.rings.finite_rings - sage: t(x) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: k = GF(55667) + sage: F. = FunctionField(k) + sage: R. = k[] + sage: t(x) x """ @@ -2454,24 +2472,24 @@ class PolynomialRing_dense_finite_field(PolynomialRing_field): EXAMPLES:: - sage: R = PolynomialRing(GF(27, 'a'), 'x') # optional - sage.rings.finite_rings - sage: type(R) # optional - sage.rings.finite_rings + sage: R = PolynomialRing(GF(27, 'a'), 'x') # needs sage.rings.finite_rings + sage: type(R) # needs sage.rings.finite_rings """ def __init__(self, base_ring, name="x", element_class=None, implementation=None): """ TESTS:: - sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field # optional - sage.rings.finite_rings - sage: R = PolynomialRing_dense_finite_field(GF(5), implementation='generic') # optional - sage.rings.finite_rings - sage: type(R(0)) # optional - sage.rings.finite_rings + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field + sage: R = PolynomialRing_dense_finite_field(GF(5), implementation='generic') + sage: type(R(0)) - sage: S = PolynomialRing_dense_finite_field(GF(25, 'a'), implementation='NTL') # optional - sage.rings.finite_rings - sage: type(S(0)) # optional - sage.rings.finite_rings + sage: S = PolynomialRing_dense_finite_field(GF(25, 'a'), implementation='NTL') # needs sage.rings.finite_rings + sage: type(S(0)) # needs sage.rings.finite_rings - sage: S = PolynomialRing_dense_finite_field(GF(64), implementation='superfast') # optional - sage.rings.finite_rings + sage: S = PolynomialRing_dense_finite_field(GF(64), implementation='superfast') # needs sage.rings.finite_rings Traceback (most recent call last): ... ValueError: unknown implementation 'superfast' for dense polynomial rings over Finite Field in z6 of size 2^6 @@ -2500,16 +2518,17 @@ def _implementation_names_impl(implementation, base_ring, sparse): """ TESTS:: - sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field # optional - sage.rings.finite_rings - sage: PolynomialRing_dense_finite_field._implementation_names_impl("NTL", GF(4), False) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field + sage: PolynomialRing_dense_finite_field._implementation_names_impl("NTL", GF(4), False) ['NTL', None] - sage: PolynomialRing_dense_finite_field._implementation_names_impl(None, GF(4), False) # optional - sage.rings.finite_rings + sage: PolynomialRing_dense_finite_field._implementation_names_impl(None, GF(4), False) ['NTL', None] - sage: PolynomialRing_dense_finite_field._implementation_names_impl("generic", GF(4), False) # optional - sage.rings.finite_rings + sage: PolynomialRing_dense_finite_field._implementation_names_impl("generic", GF(4), False) ['generic'] - sage: PolynomialRing_dense_finite_field._implementation_names_impl("FLINT", GF(4), False) # optional - sage.rings.finite_rings + sage: PolynomialRing_dense_finite_field._implementation_names_impl("FLINT", GF(4), False) NotImplemented - sage: PolynomialRing_dense_finite_field._implementation_names_impl(None, GF(4), True) # optional - sage.rings.finite_rings + sage: PolynomialRing_dense_finite_field._implementation_names_impl(None, GF(4), True) NotImplemented """ if sparse: @@ -2544,16 +2563,17 @@ def irreducible_element(self, n, algorithm=None): EXAMPLES:: - sage: f = GF(5^3, 'a')['x'].irreducible_element(2) # optional - sage.rings.finite_rings - sage: f.degree() # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: f = GF(5^3, 'a')['x'].irreducible_element(2) + sage: f.degree() 2 - sage: f.is_irreducible() # optional - sage.rings.finite_rings + sage: f.is_irreducible() True - sage: R = GF(19)['x'] # optional - sage.rings.finite_rings - sage: R.irreducible_element(21, algorithm="first_lexicographic") # optional - sage.rings.finite_rings + sage: R = GF(19)['x'] + sage: R.irreducible_element(21, algorithm="first_lexicographic") x^21 + x + 5 - sage: R = GF(5**2, 'a')['x'] # optional - sage.rings.finite_rings - sage: R.irreducible_element(17, algorithm="first_lexicographic") # optional - sage.rings.finite_rings + sage: R = GF(5**2, 'a')['x'] + sage: R.irreducible_element(17, algorithm="first_lexicographic") x^17 + a*x + 4*a + 3 AUTHORS: @@ -2614,15 +2634,16 @@ def _roth_ruckenstein(self, p, degree_bound, precision): EXAMPLES:: - sage: F = GF(17) # optional - sage.rings.finite_rings - sage: Px. = F[] # optional - sage.rings.finite_rings - sage: Pxy. = Px[] # optional - sage.rings.finite_rings - sage: p = (y - (x**2 + x + 1)) * (y**2 - x + 1) * (y - (x**3 + 4*x + 16)) # optional - sage.rings.finite_rings - sage: Px._roth_ruckenstein(p, 3, None) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: F = GF(17) + sage: Px. = F[] + sage: Pxy. = Px[] + sage: p = (y - (x**2 + x + 1)) * (y**2 - x + 1) * (y - (x**3 + 4*x + 16)) + sage: Px._roth_ruckenstein(p, 3, None) [x^3 + 4*x + 16, x^2 + x + 1] - sage: Px._roth_ruckenstein(p, 2, None) # optional - sage.rings.finite_rings + sage: Px._roth_ruckenstein(p, 2, None) [x^2 + x + 1] - sage: Px._roth_ruckenstein(p, 1, 2) # optional - sage.rings.finite_rings + sage: Px._roth_ruckenstein(p, 1, 2) [(4*x + 16, 2), (2*x + 13, 2), (15*x + 4, 2), (x + 1, 2)] """ def roth_rec(p, lam, k, g): @@ -2713,29 +2734,31 @@ def _alekhnovich(self, p, degree_bound, precision=None, dc_threshold=None): EXAMPLES:: - sage: R. = GF(17)[] # optional - sage.rings.finite_rings - sage: S. = R[] # optional - sage.rings.finite_rings - sage: p = (y - 2*x^2 - 3*x - 14) * (y - 3*x + 2) * (y - 1) # optional - sage.rings.finite_rings - sage: R._alekhnovich(p, 2) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(17)[] + sage: S. = R[] + sage: p = (y - 2*x^2 - 3*x - 14) * (y - 3*x + 2) * (y - 1) + sage: R._alekhnovich(p, 2) [3*x + 15, 2*x^2 + 3*x + 14, 1] - sage: R._alekhnovich(p, 1) # optional - sage.rings.finite_rings + sage: R._alekhnovich(p, 1) [3*x + 15, 1] - sage: R._alekhnovich(p, 1, precision=2) # optional - sage.rings.finite_rings + sage: R._alekhnovich(p, 1, precision=2) [(3*x + 15, 2), (3*x + 14, 2), (1, 2)] Example of benchmark to check that `dc_threshold = None` is better:: - sage: p = prod(y - R.random_element(20) # not tested # optional - sage.rings.finite_rings + sage: # not tested, needs sage.rings.finite_rings + sage: p = prod(y - R.random_element(20) ....: for _ in range(10)) * S.random_element(10,10) - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = None) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = None) 1 loop, best of 3: 418 ms per loop - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 1) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 1) 1 loop, best of 3: 416 ms per loop - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 2) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 2) 1 loop, best of 3: 418 ms per loop - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 3) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 3) 1 loop, best of 3: 454 ms per loop - sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 4) # not tested # optional - sage.rings.finite_rings + sage: %timeit _alekhnovich(R, p, 20, dc_threshold = 4) 1 loop, best of 3: 519 ms per loop AUTHORS: @@ -2825,24 +2848,25 @@ def _roots_univariate_polynomial(self, p, ring=None, multiplicities=False, algor EXAMPLES:: - sage: R. = GF(13)[] # optional - sage.rings.finite_rings - sage: S. = R[] # optional - sage.rings.finite_rings - sage: p = y^2 + (12*x^2 + x + 11)*y + x^3 + 12*x^2 + 12*x + 1 # optional - sage.rings.finite_rings - sage: p.roots(multiplicities=False) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(13)[] + sage: S. = R[] + sage: p = y^2 + (12*x^2 + x + 11)*y + x^3 + 12*x^2 + 12*x + 1 + sage: p.roots(multiplicities=False) [x^2 + 11*x + 1, x + 1] - sage: p.roots(multiplicities=False, degree_bound=1) # optional - sage.rings.finite_rings + sage: p.roots(multiplicities=False, degree_bound=1) [x + 1] - sage: p.roots(multiplicities=False, algorithm="Roth-Ruckenstein") # optional - sage.rings.finite_rings + sage: p.roots(multiplicities=False, algorithm="Roth-Ruckenstein") [x^2 + 11*x + 1, x + 1] TESTS: Check that :trac:`23639` is fixed:: - sage: R = GF(3)['x']['y'] # optional - sage.rings.finite_rings - sage: R.one().roots(multiplicities=False) # optional - sage.rings.finite_rings + sage: R = GF(3)['x']['y'] # needs sage.rings.finite_rings + sage: R.one().roots(multiplicities=False) # needs sage.rings.finite_rings [] - sage: R.zero().roots(multiplicities=False) # optional - sage.rings.finite_rings + sage: R.zero().roots(multiplicities=False) # needs sage.rings.finite_rings Traceback (most recent call last): ... ArithmeticError: roots of 0 are not defined @@ -2887,7 +2911,7 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: isinstance(S, PolynomialRing_cdvr) False - sage: S. = Zp(5)[] # optional - sage.rings.padics + sage: S. = Zp(5)[] # needs sage.rings.padics sage: isinstance(S, PolynomialRing_cdvr) True """ @@ -2918,8 +2942,8 @@ def __init__(self, base_ring, name=None, sparse=False, implementation=None, sage: isinstance(S, PolynomialRing_cdvf) False - sage: S. = Qp(5)[] # optional - sage.rings.padics - sage: isinstance(S, PolynomialRing_cdvf) # optional - sage.rings.padics + sage: S. = Qp(5)[] # needs sage.rings.padics + sage: isinstance(S, PolynomialRing_cdvf) # needs sage.rings.padics True """ if element_class is None: @@ -2951,11 +2975,11 @@ def _implementation_names_impl(implementation, base_ring, sparse): TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_ring_generic - sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl(None, Zp(2), False) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl(None, Zp(2), False) # needs sage.rings.padics [None] - sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl(None, Zp(2), True) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl(None, Zp(2), True) # needs sage.rings.padics NotImplemented - sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl("generic", Zp(2), False) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_ring_generic._implementation_names_impl("generic", Zp(2), False) # needs sage.rings.padics NotImplemented """ if implementation is None and not sparse: @@ -2980,11 +3004,11 @@ def _implementation_names_impl(implementation, base_ring, sparse): TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_field_generic - sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl(None, Qp(2), False) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl(None, Qp(2), False) # needs sage.rings.padics [None] - sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl(None, Qp(2), True) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl(None, Qp(2), True) # needs sage.rings.padics NotImplemented - sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl("generic", Qp(2), False) # optional - sage.rings.padics + sage: PolynomialRing_dense_padic_field_generic._implementation_names_impl("generic", Qp(2), False) # needs sage.rings.padics NotImplemented """ if implementation is None and not sparse: @@ -2998,9 +3022,9 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_ring_capped_relative as PRing - sage: R = PRing(Zp(13), name='t'); R # optional - sage.rings.padics + sage: R = PRing(Zp(13), name='t'); R # needs sage.rings.padics Univariate Polynomial Ring in t over 13-adic Ring with capped relative precision 20 - sage: type(R.gen()) # optional - sage.rings.padics + sage: type(R.gen()) # needs sage.rings.padics """ if element_class is None: @@ -3019,9 +3043,9 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_ring_capped_absolute as PRing - sage: R = PRing(Zp(13, type='capped-abs'), name='t'); R # optional - sage.rings.padics + sage: R = PRing(Zp(13, type='capped-abs'), name='t'); R # needs sage.rings.padics Univariate Polynomial Ring in t over 13-adic Ring with capped absolute precision 20 - sage: type(R.gen()) # optional - sage.rings.padics + sage: type(R.gen()) # needs sage.rings.padics """ if element_class is None: @@ -3039,10 +3063,10 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_ring_fixed_mod as PRing - sage: R = PRing(Zp(13, type='fixed-mod'), name='t'); R # optional - sage.rings.padics + sage: R = PRing(Zp(13, type='fixed-mod'), name='t'); R # needs sage.rings.padics Univariate Polynomial Ring in t over 13-adic Ring of fixed modulus 13^20 - sage: type(R.gen()) # optional - sage.rings.padics + sage: type(R.gen()) # needs sage.rings.padics """ if element_class is None: @@ -3060,9 +3084,9 @@ def __init__(self, base_ring, name=None, implementation=None, element_class=None TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_padic_field_capped_relative as PRing - sage: R = PRing(Qp(13), name='t'); R # optional - sage.rings.padics + sage: R = PRing(Qp(13), name='t'); R # needs sage.rings.padics Univariate Polynomial Ring in t over 13-adic Field with capped relative precision 20 - sage: type(R.gen()) # optional - sage.rings.padics + sage: type(R.gen()) # needs sage.rings.padics """ if element_class is None: @@ -3084,27 +3108,27 @@ def __init__(self, base_ring, name=None, element_class=None, sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_mod_n as PRing sage: R = PRing(Zmod(15), 'x'); R Univariate Polynomial Ring in x over Ring of integers modulo 15 - sage: type(R.gen()) # optional - sage.libs.flint + sage: type(R.gen()) # needs sage.libs.flint - sage: R = PRing(Zmod(15), 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(Zmod(15), 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Ring of integers modulo 15 (using NTL) - sage: type(R.gen()) # optional - sage.libs.ntl + sage: type(R.gen()) # needs sage.libs.ntl - sage: R = PRing(Zmod(2**63*3), 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(Zmod(2**63*3), 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Ring of integers modulo 27670116110564327424 (using NTL) - sage: type(R.gen()) # optional - sage.libs.ntl + sage: type(R.gen()) # needs sage.libs.ntl - sage: R = PRing(Zmod(2**63*3), 'x', implementation='FLINT') # optional - sage.libs.flint + sage: R = PRing(Zmod(2**63*3), 'x', implementation='FLINT') # needs sage.libs.flint Traceback (most recent call last): ... ValueError: FLINT does not support modulus 27670116110564327424 - sage: R = PRing(Zmod(2**63*3), 'x'); R # optional - sage.libs.ntl + sage: R = PRing(Zmod(2**63*3), 'x'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Ring of integers modulo 27670116110564327424 (using NTL) - sage: type(R.gen()) # optional - sage.libs.ntl + sage: type(R.gen()) # needs sage.libs.ntl """ if element_class is None: @@ -3195,7 +3219,7 @@ def _repr_(self): TESTS:: sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_integral_domain as PRing - sage: R = PRing(ZZ, 'x', implementation='NTL'); R # optional - sage.libs.ntl + sage: R = PRing(ZZ, 'x', implementation='NTL'); R # needs sage.libs.ntl Univariate Polynomial Ring in x over Integer Ring (using NTL) """ s = PolynomialRing_commutative._repr_(self) @@ -3207,38 +3231,39 @@ def residue_field(self, ideal, names=None): EXAMPLES:: - sage: R. = GF(2)[] # optional - sage.rings.finite_rings - sage: k. = R.residue_field(t^3 + t + 1); k # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: R. = GF(2)[] + sage: k. = R.residue_field(t^3 + t + 1); k Residue field in a of Principal ideal (t^3 + t + 1) of Univariate Polynomial Ring in t over Finite Field of size 2 (using GF2X) - sage: k.list() # optional - sage.rings.finite_rings + sage: k.list() [0, a, a^2, a + 1, a^2 + a, a^2 + a + 1, a^2 + 1, 1] - sage: R.residue_field(t) # optional - sage.rings.finite_rings + sage: R.residue_field(t) Residue field of Principal ideal (t) of Univariate Polynomial Ring in t over Finite Field of size 2 (using GF2X) - sage: P = R.irreducible_element(8) * R # optional - sage.rings.finite_rings - sage: P # optional - sage.rings.finite_rings + sage: P = R.irreducible_element(8) * R + sage: P Principal ideal (t^8 + t^4 + t^3 + t^2 + 1) of Univariate Polynomial Ring in t over Finite Field of size 2 (using GF2X) - sage: k. = R.residue_field(P); k # optional - sage.rings.finite_rings + sage: k. = R.residue_field(P); k Residue field in a of Principal ideal (t^8 + t^4 + t^3 + t^2 + 1) of Univariate Polynomial Ring in t over Finite Field of size 2 (using GF2X) - sage: k.cardinality() # optional - sage.rings.finite_rings + sage: k.cardinality() 256 Non-maximal ideals are not accepted:: - sage: R.residue_field(t^2 + 1) # optional - sage.rings.finite_rings + sage: R.residue_field(t^2 + 1) # needs sage.rings.finite_rings Traceback (most recent call last): ... ArithmeticError: ideal is not maximal - sage: R.residue_field(0) # optional - sage.rings.finite_rings + sage: R.residue_field(0) # needs sage.rings.finite_rings Traceback (most recent call last): ... ArithmeticError: ideal is not maximal - sage: R.residue_field(1) # optional - sage.rings.finite_rings + sage: R.residue_field(1) # needs sage.rings.finite_rings Traceback (most recent call last): ... ArithmeticError: ideal is not maximal @@ -3256,36 +3281,35 @@ def __init__(self, base_ring, name="x", implementation=None, element_class=None, """ TESTS:: - sage: P = GF(2)['x']; P # optional - sage.rings.finite_rings + sage: P = GF(2)['x']; P # needs sage.rings.finite_rings Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: type(P.gen()) # optional - sage.rings.finite_rings + sage: type(P.gen()) # needs sage.rings.finite_rings - sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_mod_p # optional - sage.rings.finite_rings - sage: P = PolynomialRing_dense_mod_p(GF(5), 'x'); P # optional - sage.rings.finite_rings + sage: from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_mod_p # needs sage.rings.finite_rings + sage: P = PolynomialRing_dense_mod_p(GF(5), 'x'); P # needs sage.rings.finite_rings Univariate Polynomial Ring in x over Finite Field of size 5 - sage: type(P.gen()) # optional - sage.rings.finite_rings + sage: type(P.gen()) # needs sage.rings.finite_rings - sage: P = PolynomialRing_dense_mod_p(GF(5), 'x', implementation='NTL'); P # optional - sage.rings.finite_rings + sage: P = PolynomialRing_dense_mod_p(GF(5), 'x', implementation='NTL'); P # needs sage.rings.finite_rings Univariate Polynomial Ring in x over Finite Field of size 5 (using NTL) - sage: type(P.gen()) # optional - sage.rings.finite_rings + sage: type(P.gen()) # needs sage.rings.finite_rings - sage: P = PolynomialRing_dense_mod_p(GF(9223372036854775837), 'x') # optional - sage.rings.finite_rings - sage: P # optional - sage.rings.finite_rings + sage: P = PolynomialRing_dense_mod_p(GF(9223372036854775837), 'x'); P # needs sage.rings.finite_rings Univariate Polynomial Ring in x over Finite Field of size 9223372036854775837 (using NTL) - sage: type(P.gen()) # optional - sage.rings.finite_rings + sage: type(P.gen()) # needs sage.rings.finite_rings This caching bug was fixed in :trac:`24264`:: sage: p = 2^64 + 13 - sage: A = GF(p^2) # optional - sage.rings.finite_rings - sage: B = GF(p^3) # optional - sage.rings.finite_rings - sage: R = A.modulus().parent() # optional - sage.rings.finite_rings - sage: S = B.modulus().parent() # optional - sage.rings.finite_rings - sage: R is S # optional - sage.rings.finite_rings + sage: A = GF(p^2) # needs sage.rings.finite_rings + sage: B = GF(p^3) # needs sage.rings.finite_rings + sage: R = A.modulus().parent() # needs sage.rings.finite_rings + sage: S = B.modulus().parent() # needs sage.rings.finite_rings + sage: R is S # needs sage.rings.finite_rings True """ if element_class is None: @@ -3327,15 +3351,16 @@ def _implementation_names_impl(implementation, base_ring, sparse): """ TESTS:: - sage: PolynomialRing(GF(2), 'x', implementation="GF2X") # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: PolynomialRing(GF(2), 'x', implementation="GF2X") Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: PolynomialRing(GF(2), 'x', implementation="NTL") # optional - sage.rings.finite_rings + sage: PolynomialRing(GF(2), 'x', implementation="NTL") Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: PolynomialRing(GF(2), 'x', implementation=None) # optional - sage.rings.finite_rings + sage: PolynomialRing(GF(2), 'x', implementation=None) Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: PolynomialRing(GF(2), 'x', implementation="FLINT") # optional - sage.rings.finite_rings + sage: PolynomialRing(GF(2), 'x', implementation="FLINT") Univariate Polynomial Ring in x over Finite Field of size 2 - sage: PolynomialRing(GF(3), 'x', implementation="GF2X") # optional - sage.rings.finite_rings + sage: PolynomialRing(GF(3), 'x', implementation="GF2X") Traceback (most recent call last): ... ValueError: GF2X only supports modulus 2 @@ -3407,35 +3432,36 @@ def irreducible_element(self, n, algorithm=None): EXAMPLES:: - sage: GF(5)['x'].irreducible_element(2) # optional - sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(2) x^2 + 4*x + 2 - sage: GF(5)['x'].irreducible_element(2, algorithm="adleman-lenstra") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(2, algorithm="adleman-lenstra") x^2 + x + 1 - sage: GF(5)['x'].irreducible_element(2, algorithm="primitive") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(2, algorithm="primitive") x^2 + 4*x + 2 - sage: GF(5)['x'].irreducible_element(32, algorithm="first_lexicographic") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(32, algorithm="first_lexicographic") x^32 + 2 - sage: GF(5)['x'].irreducible_element(32, algorithm="conway") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(32, algorithm="conway") Traceback (most recent call last): ... RuntimeError: requested Conway polynomial not in database. - sage: GF(5)['x'].irreducible_element(32, algorithm="primitive") # optional - sage.rings.finite_rings + sage: GF(5)['x'].irreducible_element(32, algorithm="primitive") x^32 + ... In characteristic 2:: - sage: GF(2)['x'].irreducible_element(33) # optional - sage.rings.finite_rings + sage: GF(2)['x'].irreducible_element(33) # needs sage.rings.finite_rings x^33 + x^13 + x^12 + x^11 + x^10 + x^8 + x^6 + x^3 + 1 - sage: GF(2)['x'].irreducible_element(33, algorithm="minimal_weight") # optional - sage.rings.finite_rings + sage: GF(2)['x'].irreducible_element(33, algorithm="minimal_weight") # needs sage.rings.finite_rings x^33 + x^10 + 1 In degree 1:: - sage: GF(97)['x'].irreducible_element(1) # optional - sage.rings.finite_rings + sage: GF(97)['x'].irreducible_element(1) # needs sage.rings.finite_rings x + 96 - sage: GF(97)['x'].irreducible_element(1, algorithm="conway") # optional - sage.rings.finite_rings + sage: GF(97)['x'].irreducible_element(1, algorithm="conway") # needs sage.rings.finite_rings x + 92 - sage: GF(97)['x'].irreducible_element(1, algorithm="adleman-lenstra") # optional - sage.rings.finite_rings + sage: GF(97)['x'].irreducible_element(1, algorithm="adleman-lenstra") # needs sage.rings.finite_rings x AUTHORS: diff --git a/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py b/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py index 443587bb9f6..a3104af8175 100644 --- a/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py +++ b/src/sage/schemes/cyclic_covers/cycliccover_finite_field.py @@ -1145,7 +1145,7 @@ def frobenius_polynomial(self): + 24687045654725446027864774006541463602997309796*x^10 + 11320844849639649951608809973589776933203136765026963553258401 - sage; h = PolynomialRing(GF(1009^2), 'x')([-1] + [0]*(5-1) + [1]) + sage: h = PolynomialRing(GF(1009^2), 'x')([-1] + [0]*(5-1) + [1]) sage: CyclicCover(3, h).frobenius_polynomial() # long time x^8 + 532*x^7 - 2877542*x^6 - 242628176*x^5 + 4390163797795*x^4 - 247015136050256*x^3 - 2982540407204025062*x^2 + 561382189105547134612*x + 1074309286591662654798721 diff --git a/src/sage/schemes/elliptic_curves/heegner.py b/src/sage/schemes/elliptic_curves/heegner.py index 71e620c643a..71f4b86d29b 100644 --- a/src/sage/schemes/elliptic_curves/heegner.py +++ b/src/sage/schemes/elliptic_curves/heegner.py @@ -47,8 +47,8 @@ sage: E = EllipticCurve('43a'); P = E.heegner_point(-7) sage: P.x_poly_exact() x - sage: P.point_exact() - (0 : 0 : 1) + sage: z = P.point_exact(); z == E(0,0,1) or -z == E(0,0,1) + True sage: E = EllipticCurve('997a') sage: E.rank() @@ -58,16 +58,17 @@ sage: P = E.heegner_point(-19) sage: P.x_poly_exact() x - 141/49 - sage: P.point_exact() - (141/49 : -162/343 : 1) + sage: z = P.point_exact(); z == E(141/49, -162/343, 1) or -z == E(141/49, -162/343, 1) + True Here we find that the Heegner point generates a subgroup of index 3:: sage: E = EllipticCurve('92b1') sage: E.heegner_discriminants_list(1) [-7] - sage: P = E.heegner_point(-7); z = P.point_exact(); z - (0 : 1 : 1) + sage: P = E.heegner_point(-7) + sage: z = P.point_exact(); z == E(0, 1, 1) or -z == E(0, 1, 1) + True sage: E.regulator() 0.0498083972980648 sage: z.height() @@ -6421,8 +6422,8 @@ def ell_heegner_point(self, D, c=ZZ(1), f=None, check=True): [-7, -11, -40, -47, -67, -71, -83, -84, -95, -104] sage: P = E.heegner_point(-7); P # indirect doctest Heegner point of discriminant -7 on elliptic curve of conductor 37 - sage: P.point_exact() - (0 : 0 : 1) + sage: z = P.point_exact(); z == E(0, 0, 1) or -z == E(0, 0, 1) + True sage: P.curve() Elliptic Curve defined by y^2 + y = x^3 - x over Rational Field sage: P = E.heegner_point(-40).point_exact(); P @@ -7139,14 +7140,15 @@ def heegner_sha_an(self, D, prec=53): 2.3 in [GZ1986]_ page 311, then that conjecture is false, as the following example shows:: - sage: E = EllipticCurve('65a') # long time - sage: E.heegner_sha_an(-56) # long time + sage: # long time + sage: E = EllipticCurve('65a') + sage: E.heegner_sha_an(-56) 1.00000000000000 - sage: E.torsion_order() # long time + sage: E.torsion_order() 2 - sage: E.tamagawa_product() # long time + sage: E.tamagawa_product() 1 - sage: E.quadratic_twist(-56).rank() # long time + sage: E.quadratic_twist(-56).rank() 2 """ # check conditions, then return from cache if possible. diff --git a/src/sage/schemes/elliptic_curves/hom_frobenius.py b/src/sage/schemes/elliptic_curves/hom_frobenius.py index 38f22f1fcdf..b5f2b51d470 100644 --- a/src/sage/schemes/elliptic_curves/hom_frobenius.py +++ b/src/sage/schemes/elliptic_curves/hom_frobenius.py @@ -269,10 +269,14 @@ def _eval(self, P): sage: from sage.schemes.elliptic_curves.hom_frobenius import EllipticCurveHom_frobenius sage: E = EllipticCurve(GF(11), [1,1]) sage: pi = EllipticCurveHom_frobenius(E) - sage: P = E.change_ring(GF(11^6)).lift_x(GF(11^3).gen()); P - (6*z6^5 + 8*z6^4 + 8*z6^3 + 6*z6^2 + 10*z6 + 5 : 2*z6^5 + 2*z6^4 + 2*z6^3 + 4*z6 + 6 : 1) - sage: pi._eval(P) - (z6^5 + 3*z6^4 + 3*z6^3 + 6*z6^2 + 9 : z6^5 + 10*z6^4 + 10*z6^3 + 5*z6^2 + 4*z6 + 8 : 1) + sage: Ebar = E.change_ring(GF(11^6)) + sage: z6 = GF(11^6).gen() + sage: P = Ebar.lift_x(GF(11^3).gen()) + sage: p = Ebar(6*z6^5 + 8*z6^4 + 8*z6^3 + 6*z6^2 + 10*z6 + 5, 2*z6^5 + 2*z6^4 + 2*z6^3 + 4*z6 + 6, 1) + sage: Q = pi._eval(P) + sage: q = Ebar(z6^5 + 3*z6^4 + 3*z6^3 + 6*z6^2 + 9, z6^5 + 10*z6^4 + 10*z6^3 + 5*z6^2 + 4*z6 + 8, 1) + sage: (P == p and Q == q) or (P == -p and Q == -q) + True """ if self._domain.defining_polynomial()(*P): raise ValueError(f'{P} not on {self._domain}') diff --git a/src/sage/schemes/projective/proj_bdd_height.py b/src/sage/schemes/projective/proj_bdd_height.py index a0658326140..c00bf2f22c3 100644 --- a/src/sage/schemes/projective/proj_bdd_height.py +++ b/src/sage/schemes/projective/proj_bdd_height.py @@ -52,7 +52,7 @@ def ZZ_points_of_bounded_height(PS, dim, bound): - an iterator of points of bounded height - EXAMPLES: + EXAMPLES:: sage: from sage.schemes.projective.proj_bdd_height import ZZ_points_of_bounded_height sage: PS = ProjectiveSpace(ZZ, 1) @@ -110,7 +110,7 @@ def QQ_points_of_bounded_height(PS, dim, bound, normalize=False): - an iterator of points of bounded height - EXAMPLES: + EXAMPLES:: sage: from sage.schemes.projective.proj_bdd_height import QQ_points_of_bounded_height sage: PS = ProjectiveSpace(QQ, 1) @@ -251,7 +251,7 @@ def points_of_bounded_height(PS, K, dim, bound, prec=53): - an iterator of points of bounded height - EXAMPLES: + EXAMPLES:: sage: from sage.schemes.projective.proj_bdd_height import points_of_bounded_height sage: x = polygen(ZZ, 'x') diff --git a/src/sage/tests/books/computational-mathematics-with-sagemath/lp_doctest.py b/src/sage/tests/books/computational-mathematics-with-sagemath/lp_doctest.py index f3aa2201ac8..da89f065214 100644 --- a/src/sage/tests/books/computational-mathematics-with-sagemath/lp_doctest.py +++ b/src/sage/tests/books/computational-mathematics-with-sagemath/lp_doctest.py @@ -221,7 +221,7 @@ Sage example in ./lp.tex, line 906:: sage: while not h.is_connected(): - ....: S = h.connected_components()[0] + ....: S = h.connected_components(sort=False)[0] ....: p.add_constraint( ....: p.sum( B(u,v) for u,v ....: in g.edge_boundary(S, labels = False)) diff --git a/src/sage/topology/simplicial_complex.py b/src/sage/topology/simplicial_complex.py index 20b89bb61c8..91f7deb1486 100644 --- a/src/sage/topology/simplicial_complex.py +++ b/src/sage/topology/simplicial_complex.py @@ -4078,7 +4078,7 @@ def connected_component(self, simplex=None): v = self.vertices()[0] else: v = simplex[0] - vertices = self.graph().connected_component_containing_vertex(v) + vertices = self.graph().connected_component_containing_vertex(v, sort=False) facets = [f for f in self.facets() if f.is_face(Simplex(vertices))] return SimplicialComplex(facets) diff --git a/src/sage/topology/simplicial_set_constructions.py b/src/sage/topology/simplicial_set_constructions.py index 316d30bb1a1..b2b6bbbae7c 100644 --- a/src/sage/topology/simplicial_set_constructions.py +++ b/src/sage/topology/simplicial_set_constructions.py @@ -1499,7 +1499,7 @@ def __init__(self, maps=None, vertex_name=None): for x in domain.n_cells(n): edges.extend([[(x, -1), (f(x), i)] for i, f in enumerate(maps)]) G = Graph([vertices, edges], format='vertices_and_edges') - data[n] = [set(_) for _ in G.connected_components()] + data[n] = [set(_) for _ in G.connected_components(sort=False)] # data is now a dictionary indexed by dimension, and data[n] # consists of sets of n-simplices of the domain and the # codomains, each set an equivalence class of n-simplices diff --git a/src/sage/version.py b/src/sage/version.py index 179d0ed42b3..4d55e0375ec 100644 --- a/src/sage/version.py +++ b/src/sage/version.py @@ -1,5 +1,5 @@ # Sage version information for Python scripts # This file is auto-generated by the sage-update-version script, do not edit! -version = '10.1.beta7' -date = '2023-07-20' -banner = 'SageMath version 10.1.beta7, Release Date: 2023-07-20' +version = '10.1.beta8' +date = '2023-07-30' +banner = 'SageMath version 10.1.beta8, Release Date: 2023-07-30' From 2ce7bfe3f1f96219e4616183b1d0c6b5af032432 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 28 Aug 2023 23:20:11 +0200 Subject: [PATCH 07/95] getting a line back in doctests for cone --- src/sage/geometry/hyperplane_arrangement/arrangement.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 651352731bc..699ef32ed9d 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -755,6 +755,7 @@ def cone(self, variable='t'): sage: # needs sage.combinat sage: a. = hyperplane_arrangements.semiorder(3) sage: H. = OrderedHyperplaneArrangements(QQ) + sage: a1 = H(a) sage: b = a.cone() sage: b1 = a1.cone() sage: a.characteristic_polynomial().factor() @@ -776,7 +777,7 @@ 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.hyperplanes().index(h) for h in b.hyperplanes()] # optional - sage.combinat + sage: [b1.hyperplanes().index(h) for h in b.hyperplanes()] [0, 2, 4, 6, 1, 3, 5] """ hyperplanes = [] From 22d67c1605a3be9412ce92db138ece5e90abf678 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 30 Aug 2023 08:25:42 +0200 Subject: [PATCH 08/95] try to catch exceptions --- 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 53eb56dd8b1..7fa211c18f5 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -66,6 +66,7 @@ from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField +import sys # from sage.sets.set import Set roots_interval_cache = dict() @@ -788,7 +789,9 @@ def populate_roots_interval_cache(inputs): for r in result: roots_interval_cache[r[0][0]] = r[1] problem_par = False - except TypeError: + except: # TypeError: + e = sys.exc_info()[0] + print(e, 'populate_roots_interval_cache') pass @@ -1242,7 +1245,9 @@ 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: # ChildProcessError: # hack to deal with random fails first time + e = sys.exc_info()[0] + print(e, 'populate_roots_interval_cache') pass B = BraidGroup(d) result = [] From 7f917080532f0cadc65b5e439519dacb4dce52e9 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 30 Aug 2023 09:06:39 +0200 Subject: [PATCH 09/95] check AttributeError --- 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 7fa211c18f5..8e25de5add7 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -563,6 +563,7 @@ def followstrand(f, factors, x0, x1, y0a, prec=53): points = contpath_mp(deg, coefs, yr, yi, prec) return points except Exception: + print("augmented precisionp= ", rec) return followstrand(f, factors, x0, x1, y0a, 2 * prec) @@ -789,7 +790,7 @@ def populate_roots_interval_cache(inputs): for r in result: roots_interval_cache[r[0][0]] = r[1] problem_par = False - except: # TypeError: + except (TypeError, AttributeError): e = sys.exc_info()[0] print(e, 'populate_roots_interval_cache') pass From 0d391226fdc6a58ed04ecec542a37ba2c77aee61 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 30 Aug 2023 11:37:40 +0200 Subject: [PATCH 10/95] add : after sage in failing doctest --- 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 8e25de5add7..d0d27212ccd 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1570,7 +1570,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): It is also possible to have coefficients in a number field with a fixed embedding in `\QQbar`:: - sage # optional - sirocco + sage: # optional - sirocco sage: from sage.schemes.curves.zariski_vankampen import fundamental_group sage: zeta = QQbar['x']('x^2 + x+ 1').roots(multiplicities=False)[0] sage: zeta From 7aabdf08296520c231eb5cf1b4d26235551aeed3 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 30 Aug 2023 12:55:41 +0200 Subject: [PATCH 11/95] merge with puiseux --- src/sage/schemes/curves/zariski_vankampen.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index d0d27212ccd..f1c07e627f9 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -66,7 +66,6 @@ from sage.rings.qqbar import QQbar from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField -import sys # from sage.sets.set import Set roots_interval_cache = dict() @@ -563,7 +562,6 @@ def followstrand(f, factors, x0, x1, y0a, prec=53): points = contpath_mp(deg, coefs, yr, yi, prec) return points except Exception: - print("augmented precisionp= ", rec) return followstrand(f, factors, x0, x1, y0a, 2 * prec) @@ -790,9 +788,7 @@ def populate_roots_interval_cache(inputs): for r in result: roots_interval_cache[r[0][0]] = r[1] problem_par = False - except (TypeError, AttributeError): - e = sys.exc_info()[0] - print(e, 'populate_roots_interval_cache') + except TypeError: pass @@ -1246,9 +1242,7 @@ 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 - e = sys.exc_info()[0] - print(e, 'populate_roots_interval_cache') + except ChildProcessError: # ChildProcessError: # hack to deal with random fails first time pass B = BraidGroup(d) result = [] From e096e9fcdfd23578d65cc775471aa96faa582822 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 2 Sep 2023 14:24:18 +0200 Subject: [PATCH 12/95] method fundamental group does not work on degree 1 --- src/sage/schemes/curves/affine_curve.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 326c0d86e77..5c2141ae186 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1809,7 +1809,16 @@ def fundamental_group(self, simplified=True, puiseux=False): This functionality requires the sirocco package to be installed. """ 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) + bm = self.braid_monodromy() + if bm == []: + f = self.defining_polynomial() + x, y = f.parent().gens() + d0 = f.degree(y) + f0 = f.coefficient({y: d0}) + d = d0 + f0.degree(x) + else: + d = bm[0].parent().strands() + return fundamental_group_from_braid_mon(self.braid_monodromy(), degree=d, simplified=simplified, puiseux=puiseux) @cached_method def braid_monodromy(self): From 4dafc7f808b5cd5db7842893eb8691f1668718a0 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 2 Sep 2023 15:16:39 +0200 Subject: [PATCH 13/95] add tests and fix trivial cases in projective case --- src/sage/schemes/curves/affine_curve.py | 3 +++ src/sage/schemes/curves/projective_curve.py | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 5c2141ae186..4834aec54c2 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1803,6 +1803,9 @@ def fundamental_group(self, simplified=True, puiseux=False): sage: C = A.curve(y^2 - a*x^3 - x^2) sage: C.fundamental_group() # optional - sirocco Finitely presented group < x0 | > + sage: C = A.curve(x * (x - 1)) + sage: C.fundamental_group() # optional - sirocco + Finitely presented group < x0, x1 | > .. WARNING:: diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 958a402da19..1da5ccdf63c 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1765,7 +1765,9 @@ def fundamental_group(self): sage: G.is_isomorphic(G0) # optional - sirocco #I Forcing finiteness test True - + sage: C = P.curve(z) + sage: C.fundamental_group() + Finitely presented group < | > """ from sage.schemes.curves.zariski_vankampen import fundamental_group F = self.base_ring() @@ -1773,7 +1775,11 @@ def fundamental_group(self): if QQbar.coerce_map_from(F) is None: raise NotImplementedError("the base field must have an embedding" " to the algebraic field") - f = self.affine_patch(2).defining_polynomial() + g = self.defining_polynomial() + ring = self.ambient_space().affine_patch(2).coordinate_ring() + if g.degree() == 1: + return fundamental_group(ring(1)) + f = ring(self.affine_patch(2).defining_polynomial()) if f.degree() == self.degree(): return fundamental_group(f, projective=True) else: # in this case, the line at infinity is part of the curve, so the complement lies in the affine patch From 51cfe00a3da99b414bd90b2d1e176db24d13993e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 30 Sep 2023 18:30:07 +0200 Subject: [PATCH 14/95] first step for vertical --- src/sage/schemes/curves/zariski_vankampen.py | 188 ++++++++++++------- 1 file changed, 118 insertions(+), 70 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 00f3aca0f0c..743f5a66181 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -28,7 +28,7 @@ sage: R. = QQ[] sage: f = y^3 + x^3 - 1 sage: braid_monodromy(f) - ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0}) + ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0}, {}) sage: fundamental_group(f) Finitely presented group < x0 | > """ @@ -226,7 +226,7 @@ def corrected_voronoi_diagram(points): INPUT: - - ``points`` -- a list of complex numbers + - ``points`` -- a tuple of complex numbers OUTPUT: @@ -371,7 +371,7 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False): print(prec) -def voronoi_cells(V): +def voronoi_cells(V, vertical_lines=[]): 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. @@ -389,13 +389,16 @@ def voronoi_cells(V): 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. + - ``vertical_regions`` -- dictionnary for the regions associated with vertical lines. 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: G, E, p, EC, DG, VR = voronoi_cells(V) + sage: VR == dict() + True sage: Gv = G.vertices(sort=True) sage: Ge = G.edges(sort=True) sage: len(Gv), len(Ge) @@ -444,7 +447,10 @@ def voronoi_cells(V): sage: edg[-1] in Ge True """ - compact_regions = [_ for _ in V.regions().values() if _.is_compact()] + regions = V.regions() + points = [p for p in V.regions().keys() if V.regions()[p].is_compact()] + compact_regions = [regions[p] for p in points] + vertical_regions = {j: regions[points[j]] for j in vertical_lines} 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') @@ -461,7 +467,7 @@ def voronoi_cells(V): 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) + return (G, E, p, EC, DG, vertical_regions) def followstrand(f, factors, x0, x1, y0a, prec=53): @@ -908,7 +914,7 @@ def braid_in_segment(glist, x0, x1, precision=dict()): return initialbraid * centralbraid * finalbraid -def geometric_basis(G, E, EC0, p, dual_graph): +def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): r""" Return a geometric basis, based on a vertex. @@ -929,59 +935,48 @@ def geometric_basis(G, E, EC0, p, dual_graph): by a tuple whose first element is the an integer for the position and the second one is the cyclic ordered list of vertices in the region. - OUTPUT: A geometric basis. It is formed by a list of sequences of paths. - Each path is a list of vertices, that form a closed path in ``G``, based at - ``p``, that goes to a region, surrounds it, and comes back by the same - path it came. The concatenation of all these paths is equivalent to ``E``. + - ``vertical_regions`` -- dictionary with keys the vertices of ``dual_graph`` + to fix regions associated with vertical lines ++ + OUTPUT: A geometric basis and a dictionnary. + + The geometric basis is formed by a list of sequences of paths. Each path is a + ist 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``. + + The dictionnary fixes the positions of the generators associated with the vertical lines. EXAMPLES:: sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, voronoi_cells sage: points = [(-3,0),(3,0),(0,3),(0,-3)]+ [(0,0),(0,-1),(0,1),(1,0),(-1,0)] sage: V = VoronoiDiagram(points) - sage: G, E, p, EC, DG = voronoi_cells(V) - sage: geometric_basis(G, E, EC, p, DG) - [[A vertex at (-2, -2), - A vertex at (2, -2), - A vertex at (2, 2), - A vertex at (1/2, 1/2), - A vertex at (1/2, -1/2), - A vertex at (2, -2), - A vertex at (-2, -2)], - [A vertex at (-2, -2), - A vertex at (2, -2), - A vertex at (1/2, -1/2), - A vertex at (1/2, 1/2), - A vertex at (-1/2, 1/2), - A vertex at (-1/2, -1/2), - A vertex at (1/2, -1/2), - A vertex at (2, -2), - A vertex at (-2, -2)], - [A vertex at (-2, -2), - A vertex at (2, -2), - A vertex at (1/2, -1/2), - A vertex at (-1/2, -1/2), - A vertex at (-2, -2)], - [A vertex at (-2, -2), - A vertex at (-1/2, -1/2), - A vertex at (-1/2, 1/2), - A vertex at (1/2, 1/2), - A vertex at (2, 2), - A vertex at (-2, 2), - A vertex at (-1/2, 1/2), - A vertex at (-1/2, -1/2), - A vertex at (-2, -2)], - [A vertex at (-2, -2), - A vertex at (-1/2, -1/2), - A vertex at (-1/2, 1/2), - A vertex at (-2, 2), - A vertex at (-2, -2)]] + sage: G, E, p, EC, DG, VR = voronoi_cells(V) + sage: geometric_basis(G, E, EC, p, DG, dict()) + ([[A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), + A vertex at (1/2, 1/2), A vertex at (1/2, -1/2), + A vertex at (2, -2), A vertex at (-2, -2)], + [A vertex at (-2, -2), A vertex at (2, -2), A vertex at (1/2, -1/2), + A vertex at (1/2, 1/2), A vertex at (-1/2, 1/2), + A vertex at (-1/2, -1/2), A vertex at (1/2, -1/2), + A vertex at (2, -2), A vertex at (-2, -2)], + [A vertex at (-2, -2), A vertex at (2, -2), A vertex at (1/2, -1/2), + A vertex at (-1/2, -1/2), A vertex at (-2, -2)], + [A vertex at (-2, -2), A vertex at (-1/2, -1/2), + A vertex at (-1/2, 1/2), A vertex at (1/2, 1/2), + A vertex at (2, 2), A vertex at (-2, 2), A vertex at (-1/2, 1/2), + A vertex at (-1/2, -1/2), A vertex at (-2, -2)], + [A vertex at (-2, -2), A vertex at (-1/2, -1/2), + A vertex at (-1/2, 1/2), A vertex at (-2, 2), + A vertex at (-2, -2)]], {}) """ 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] + vert_dict = vert_dict = {0: vertical_regions[p] for p in vertical_regions.keys()} + return [[EC], vert_dict] 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) @@ -1066,9 +1061,18 @@ def geometric_basis(G, E, EC0, p, dual_graph): 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) - gb2 = geometric_basis(G2, E2, EC2, q, Gd2) + regs1 = [v[1] for v in Gd1.vertices()] + VR1 = {p: vertical_regions[p] for p in vertical_regions.keys() if p in regs1} + regs2 = [v[1] for v in Gd2.vertices()] + VR2 = {p: vertical_regions[p] for p in vertical_regions.keys() if p in regs2} + + gb1, vd1 = geometric_basis(G1, E1, EC1, q, Gd1, VR1) + gb2, vd2 = geometric_basis(G2, E2, EC2, q, Gd2, VR2) + vd = {j: vd1[j] for j in vd1.keys()} + m = len(gb1) + for j in vd2.keys(): + vd[j + m] = vd2[j] reverse_connecting = list(reversed(connecting_path)) resul = [connecting_path + path + reverse_connecting for path in gb1 + gb2] @@ -1082,7 +1086,7 @@ def geometric_basis(G, E, EC0, p, dual_graph): i -= 1 else: i += 1 - return resul + return (resul, vd) def strand_components(f, flist, p1): @@ -1131,7 +1135,7 @@ def strand_components(f, flist, p1): return (roots_base, strands) -def braid_monodromy(f, arrangement=()): +def braid_monodromy(f, arrangement=(), vertical=False): r""" Compute the braid monodromy of a projection of the curve defined by a polynomial. @@ -1144,6 +1148,12 @@ def braid_monodromy(f, arrangement=()): - ``arrangement`` -- an optional tuple of polynomials whose product equals ``f``. + - `vertical` .. boolean (default: ``False`). If set to ``True``, ``arrangements`` + contains more than one polynomial, some of them is of degree `1` in `x` + and degree `0` in `y`, and none of the other components have vertical asymptotes, + then these components are marked and not used for + the computation of the braid monodromy. + OUTPUT: A list of braids and a dictionary. @@ -1151,12 +1161,15 @@ def braid_monodromy(f, arrangement=()): 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``. + If ``vertical`` is set to ``True`` only the vertical lines are not used + and the list of their indices are is the second element of the output. .. 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. + into the previous case except if the only vertical asymptotes are lines + and ``vertical=True``.. .. TODO:: @@ -1173,7 +1186,7 @@ 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], {0: 0, 1: 0, 2: 0, 3: 0}) + s1*s0*s2*s0^-1*s2*s1^-1], {0: 0, 1: 0, 2: 0, 3: 0}, {}) sage: flist = (x^2 - y^3, x + 3*y - 5) sage: bm1 = braid_monodromy(f, arrangement=flist) sage: bm1[0] == bm[0] @@ -1181,9 +1194,9 @@ def braid_monodromy(f, arrangement=()): sage: bm1[1] {0: 0, 1: 1, 2: 0, 3: 0} sage: braid_monodromy(R(1)) - ([], {}) + ([], {}, {}) sage: braid_monodromy(x*y^2 - 1) - ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0}) + ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0}, {}) """ global roots_interval_cache F = fieldI(f.base_ring()) @@ -1194,29 +1207,52 @@ def braid_monodromy(f, arrangement=()): 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()) + dic_vertical = dict() + if vertical: + for f0 in arrangement1: + vertical_asymptote = f0.degree(y) < f0.degree() and f0.degree() > 1 + if vertical_asymptote: + dic_vertical = dict() + break + dic_vertical[f0] = f0.degree(y) < f0.degree(x) and f0.degree(x) == 1 + glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor() + if f0 not in dic_vertical.keys()) 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) + if not dic_vertical: + 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) if d > 0: disc = discrim(glist) else: disc = [] - if len(disc) == 0: - result = [] + vertical_braid = dict() + transversal = [] + for f0 in arrangement1: + if f0 in dic_vertical.keys(): + pt = [j for j, t in enumerate(disc) if f0(x=t) == 0] + if pt: + vertical_braid[f0] = pt[0] + else: + transversal.append() + if not disc: + vertical_braids = {j: j for j, p in enumerate(transversal)} + if d > 1: + result = [BraidGroup(d).one() for p in transversal] + else: + result = [() for p in transversal] p1 = F(0) roots_base, strands = strand_components(g, arrangement1, p1) - return ([], strands) + return (result, strands, vertical_braids) V = corrected_voronoi_diagram(tuple(disc)) - G, E, p, EC, DG = voronoi_cells(V) + G, E, p, EC, DG, VR = voronoi_cells(V) p0 = (p[0], p[1]) p1 = p0[0] + I1 * p0[1] roots_base, strands = strand_components(g, arrangement1, p1) - geombasis = geometric_basis(G, E, EC, p, DG) + geombasis, vd = geometric_basis(G, E, EC, p, DG, VR) segs = set() for p in geombasis: for s in zip(p[:-1], p[1:]): @@ -1252,7 +1288,19 @@ def braid_monodromy(f, arrangement=()): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - return (result, strands) + vertical_braids = dict() + r = len(result) + t = 0 + for j, f0 in enumerate(arrangement1): + if f0 in dic_vertical.keys(): + result.append(B.one()) + if f0 in vertical_braid.keys(): + k = vertical_braid[f0] + vertical_braids[k] = j + else: + vertical_braids[r + t] = j + t += 1 + return (result, strands, vertical_braids) def conjugate_positive_form(braid): @@ -1713,7 +1761,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis bm = [] dic = dict() else: - bm, dic = braid_monodromy(f, flist1) + bm, dic, dv = 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() From d289c2cf1b1eb5c82d1f51097741b6116409de4e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 30 Sep 2023 20:33:01 +0200 Subject: [PATCH 15/95] unwanted character --- 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 743f5a66181..0c087b2224a 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -937,7 +937,7 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): - ``vertical_regions`` -- dictionary with keys the vertices of ``dual_graph`` to fix regions associated with vertical lines -+ + OUTPUT: A geometric basis and a dictionnary. The geometric basis is formed by a list of sequences of paths. Each path is a From c996a59a934e78e278bcecf7694cd3339df782a1 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 30 Sep 2023 21:44:30 +0200 Subject: [PATCH 16/95] first vertical tests --- src/sage/schemes/curves/zariski_vankampen.py | 61 +++++++++++--------- 1 file changed, 34 insertions(+), 27 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 0c087b2224a..ce5db6b0aa4 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1207,51 +1207,59 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: arrangement1 = tuple(_.change_ring(F) for _ in arrangement) x, y = f.parent().gens() - dic_vertical = dict() + dic_vertical = {f0: False for f0 in arrangement1} if vertical: for f0 in arrangement1: - vertical_asymptote = f0.degree(y) < f0.degree() and f0.degree() > 1 - if vertical_asymptote: - dic_vertical = dict() + if f0.degree(y) < f0.degree() and f0.degree() > 1: + dic_vertical = {f0: False for f0 in arrangement1} break - dic_vertical[f0] = f0.degree(y) < f0.degree(x) and f0.degree(x) == 1 - glist = tuple(_[0] for f0 in arrangement1 for _ in f0.factor() - if f0 not in dic_vertical.keys()) + else: + dic_vertical[f0] = f0.degree(y) < f0.degree(x) and f0.degree(x) == 1 + arrangement_h = () + arrangement_v = () + for f0 in arrangement1: + if dic_vertical[f0]: + arrangement_v += (f0, ) + else: + arrangement_h += (f0, ) + glist = tuple(_[0] for f0 in arrangement_h for _ in f0.factor()) g = f.parent()(prod(glist)) d = g.degree(y) if not dic_vertical: 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) + arrangement_h = tuple(f1.subs({x: x + y}) for f1 in arrangement_h) glist = tuple(f1.subs({x: x + y}) for f1 in glist) if d > 0: disc = discrim(glist) else: disc = [] vertical_braid = dict() - transversal = [] - for f0 in arrangement1: - if f0 in dic_vertical.keys(): - pt = [j for j, t in enumerate(disc) if f0(x=t) == 0] - if pt: - vertical_braid[f0] = pt[0] - else: - transversal.append() + transversal = dict() + for f0 in arrangement_v: + pt = [j for j, t in enumerate(disc) if f0(x=t) == 0] + if pt: + vertical_braid[f0] = (disc.index(pt[0]), arrangement1.index(f0)) + else: + transversal[f0] = arrangement1.index(f0) if not disc: - vertical_braids = {j: j for j, p in enumerate(transversal)} + vertical_braids = {i: j for i, (p, j) in enumerate(transversal)} if d > 1: result = [BraidGroup(d).one() for p in transversal] else: result = [() for p in transversal] p1 = F(0) - roots_base, strands = strand_components(g, arrangement1, p1) + if d > 0: + roots_base, strands = strand_components(g, arrangement_h, p1) + else: + strands = dict() return (result, strands, vertical_braids) V = corrected_voronoi_diagram(tuple(disc)) G, E, p, EC, DG, VR = voronoi_cells(V) p0 = (p[0], p[1]) p1 = p0[0] + I1 * p0[1] - roots_base, strands = strand_components(g, arrangement1, p1) + roots_base, strands = strand_components(g, arrangement_h, p1) geombasis, vd = geometric_basis(G, E, EC, p, DG, VR) segs = set() for p in geombasis: @@ -1291,15 +1299,14 @@ def braid_monodromy(f, arrangement=(), vertical=False): vertical_braids = dict() r = len(result) t = 0 - for j, f0 in enumerate(arrangement1): - if f0 in dic_vertical.keys(): + for f0 in arrangement_v: + if f0 in vertical_braid.keys(): + k, j = vertical_braid[f0] + vertical_braids[k] = j + else: + vertical_braids[r + t] = transversal[f0] + t += 1 result.append(B.one()) - if f0 in vertical_braid.keys(): - k = vertical_braid[f0] - vertical_braids[k] = j - else: - vertical_braids[r + t] = j - t += 1 return (result, strands, vertical_braids) From af6853da893674044c6e08a2734464b2ceb94315 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 30 Sep 2023 23:29:16 +0200 Subject: [PATCH 17/95] add vertical options for fundamental_group_arrangement --- src/sage/schemes/curves/projective_curve.py | 42 ++++++++++---------- src/sage/schemes/curves/zariski_vankampen.py | 31 ++++++++------- 2 files changed, 37 insertions(+), 36 deletions(-) diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 1da5ccdf63c..3ed093c50a8 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -437,12 +437,12 @@ def projection(self, P=None, PS=None): if self.defining_polynomials()[i] != 0: F = self.defining_polynomials()[i] # find a point on which it doesn't vanish - l = list(PP.gens()) + ll = list(PP.gens()) for i in range(n + 1): - l[i] = 0 - while F(l) == 0: - l[i] += 1 - Q = PP(l) # will be a point not on the curve + ll[i] = 0 + while F(ll) == 0: + ll[i] += 1 + Q = PP(ll) # will be a point not on the curve else: # if the base ring is a finite field, iterate over all points in the ambient space and check which # are on this curve @@ -487,12 +487,12 @@ def projection(self, P=None, PS=None): # defining polynomials of this curve with the polynomials defining the inverse of the change of coordinates invcoords = [Q[i]*PP.gens()[j] + PP.gens()[i] for i in range(n + 1)] invcoords[j] = Q[j]*PP.gens()[j] - I = PP.coordinate_ring().ideal([f(invcoords) for f in self.defining_polynomials()]) - J = I.elimination_ideal(PP.gens()[j]) + id = PP.coordinate_ring().ideal([f(invcoords) for f in self.defining_polynomials()]) + J = id.elimination_ideal(PP.gens()[j]) K = Hom(PP.coordinate_ring(), PP2.coordinate_ring()) - l = list(PP2.gens()) - l.insert(j, 0) - phi = K(l) + ll = list(PP2.gens()) + ll.insert(j, 0) + phi = K(ll) G = [phi(f) for f in J.gens()] C = PP2.curve(G) return tuple([psi, C]) @@ -1224,8 +1224,8 @@ def excellent_position(self, Q): P = [0]*3 P[i] = 1 P = PP(P) - l = [0, 1, 2] - l.pop(i) + ll = [0, 1, 2] + ll.pop(i) # choose points forming a triangle with one vertex at P to map to the coordinate triangle good = False a = 0 @@ -1233,11 +1233,11 @@ def excellent_position(self, Q): a = a + 1 # find points to map to (1 : 0 : 0) and (0 : 1 : 0), not on the curve Px = [0]*3 - Px[l[0]] = a - Px[l[1]] = 1 + Px[ll[0]] = a + Px[ll[1]] = 1 Py = [0]*3 - Py[l[0]] = -a - Py[l[1]] = 1 + Py[ll[0]] = -a + Py[ll[1]] = 1 Py[i] = 1 try: Px = baseC(Px) @@ -1277,11 +1277,11 @@ def excellent_position(self, Q): if j == 0: div_pow = min(e[1] for e in npoly.exponents()) npoly = PP.coordinate_ring()({(v0, v1 - div_pow, v2): g - for (v0, v1, v2), g in npoly.dict().items()}) + for (v0, v1, v2), g in npoly.dict().items()}) else: div_pow = min(e[0] for e in npoly.exponents()) npoly = PP.coordinate_ring()({(v0 - div_pow, v1, v2): g - for (v0, v1, v2), g in npoly.dict().items()}) + for (v0, v1, v2), g in npoly.dict().items()}) # check the degree again if npoly.degree() != d - r: need_continue = True @@ -1645,9 +1645,9 @@ def is_complete_intersection(self): False """ singular.lib("sing.lib") - I = singular.simplify(self.defining_ideal(), 10) - L = singular.is_ci(I).sage() - return len(self.ambient_space().gens()) - len(I.sage().gens()) == L[-1] + id = singular.simplify(self.defining_ideal(), 10) + L = singular.is_ci(id).sage() + return len(self.ambient_space().gens()) - len(id.sage().gens()) == L[-1] def tangent_line(self, p): """ diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index ce5db6b0aa4..e64acc1e28f 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -28,7 +28,7 @@ sage: R. = QQ[] sage: f = y^3 + x^3 - 1 sage: braid_monodromy(f) - ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0}, {}) + ([s1*s0, s1*s0, s1*s0], {0: 0, 1: 0, 2: 0}, {}, 3) sage: fundamental_group(f) Finitely presented group < x0 | > """ @@ -1186,7 +1186,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): ([s1*s0*(s1*s2)^2*s0*s2^2*s0^-1*(s2^-1*s1^-1)^2*s0^-1*s1^-1, s1*s0*(s1*s2)^2*(s0*s2^-1*s1*s2*s1*s2^-1)^2*(s2^-1*s1^-1)^2*s0^-1*s1^-1, s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - s1*s0*s2*s0^-1*s2*s1^-1], {0: 0, 1: 0, 2: 0, 3: 0}, {}) + s1*s0*s2*s0^-1*s2*s1^-1], {0: 0, 1: 0, 2: 0, 3: 0}, {}, 4) sage: flist = (x^2 - y^3, x + 3*y - 5) sage: bm1 = braid_monodromy(f, arrangement=flist) sage: bm1[0] == bm[0] @@ -1194,9 +1194,9 @@ def braid_monodromy(f, arrangement=(), vertical=False): sage: bm1[1] {0: 0, 1: 1, 2: 0, 3: 0} sage: braid_monodromy(R(1)) - ([], {}, {}) + ([], {}, {}, 0) sage: braid_monodromy(x*y^2 - 1) - ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0}, {}) + ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0}, {}, 3) """ global roots_interval_cache F = fieldI(f.base_ring()) @@ -1244,7 +1244,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: transversal[f0] = arrangement1.index(f0) if not disc: - vertical_braids = {i: j for i, (p, j) in enumerate(transversal)} + vertical_braids = {i: j for i, j in enumerate(transversal)} if d > 1: result = [BraidGroup(d).one() for p in transversal] else: @@ -1254,7 +1254,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): roots_base, strands = strand_components(g, arrangement_h, p1) else: strands = dict() - return (result, strands, vertical_braids) + return (result, strands, vertical_braids, d) V = corrected_voronoi_diagram(tuple(disc)) G, E, p, EC, DG, VR = voronoi_cells(V) p0 = (p[0], p[1]) @@ -1307,7 +1307,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): vertical_braids[r + t] = transversal[f0] t += 1 result.append(B.one()) - return (result, strands, vertical_braids) + return (result, strands, vertical_braids, d) def conjugate_positive_form(braid): @@ -1528,6 +1528,10 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv return None F = FreeGroup(d) Fv = FreeGroup(d + v) + if d == 0: + return Fv / [] + if d == 1: + return Fv / [(1, j, -1, -j) for j in range(2, d + v + 1)] bmh = [br for j, br in enumerate(bm) if j + 1 not in vertical0] if not puiseux: relations_h = (relation([(x, b) for x in F.gens() for b in bmh])) @@ -1667,7 +1671,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): +def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, vertical=False): r""" Compute the fundamental group of the complement of a curve defined by a list of polynomials with the extra information @@ -1753,14 +1757,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis R = PolynomialRing(QQ, ('x', 'y')) f = R(1) x, y = R.gens() - F = R.base_ring() flist1 = [_ for _ in flist] - d = f.degree(y) - while not f.coefficient(y**d) in F: - flist1 = [g.subs({x: x + y}) for g in flist1] - f = prod(flist1) - d = f.degree(y) + d = f.degree() + vertical0 = bool(vertical) if projective: + vertical0 = False while f.degree(y) < f.degree(): flist1 = [g.subs({x: x + y}) for g in flist] f = prod(flist1) @@ -1768,7 +1769,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis bm = [] dic = dict() else: - bm, dic, dv = braid_monodromy(f, flist1) + bm, dic, dv, d = braid_monodromy(f, flist1, vertical=vertical0) g = fundamental_group_from_braid_mon(bm, degree=d, simplified=False, projective=projective, puiseux=puiseux) if simplified: hom = g.simplification_isomorphism() From cd323ec8e4d570c13f2f5fa2c579808728c4892e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 1 Oct 2023 13:16:02 +0200 Subject: [PATCH 18/95] Create the class of affine plane curve arrangements --- .../hyperplane_arrangement/arrangement.py | 4 +- .../schemes/curves/plane_curve_arrangement.py | 807 ++++++++++++++++++ 2 files changed, 809 insertions(+), 2 deletions(-) create mode 100644 src/sage/schemes/curves/plane_curve_arrangement.py diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 699ef32ed9d..467008d0260 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -310,7 +310,7 @@ low dimensions. See :meth:`~HyperplaneArrangementElement.plot` for details. -For ordere hyperplane arrangements hyperplane sections and fundamental group are also defined. +For ordered hyperplane arrangements hyperplane sections and fundamental group are also defined. TESTS:: @@ -514,7 +514,7 @@ def hyperplanes(self): OUTPUT: - An integer. + A tuple. EXAMPLES:: diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py new file mode 100644 index 00000000000..74ff81c9ffe --- /dev/null +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -0,0 +1,807 @@ +# -*- coding: utf-8 -*- +r""" +Affine Plane Curve Arrangements + +We create an :class:`OrderedAffinePlaneCurveArrangements` +object +# to define the variables `x`, `y`, `z`:: +# +# sage: H. = HyperplaneArrangements(QQ) +# sage: h = 3*x + 2*y - 5*z - 7; h +# Hyperplane 3*x + 2*y - 5*z - 7 +# sage: h.normal() +# (3, 2, -5) +# sage: h.constant_term() +# -7 + +The individual curves will be in :class:`AffinePlaneCurve`:: + + # sage: -2*h + # Hyperplane -6*x - 4*y + 10*z + 14 + # sage: x, y, z + # (Hyperplane x + 0*y + 0*z + 0, + # Hyperplane 0*x + y + 0*z + 0, + # Hyperplane 0*x + 0*y + z + 0) + +The default base field is `\QQ`, the rational numbers. +Number fields are also possible (also with fixed embeddings in ``QQbar``):: + + # sage: # needs sage.rings.number_field + # sage: x = polygen(QQ, 'x') + # sage: NF. = NumberField(x**4 - 5*x**2 + 5, embedding=1.90) + # sage: H. = HyperplaneArrangements(NF) + # sage: A = H([[(-a**3 + 3*a, -a**2 + 4), 1], [(a**3 - 4*a, -1), 1], + # ....: [(0, 2*a**2 - 6), 1], [(-a**3 + 4*a, -1), 1], + # ....: [(a**3 - 3*a, -a**2 + 4), 1]]) + # sage: A + # Arrangement of 5 hyperplanes of dimension 2 and rank 2 + # sage: A.base_ring() + # Number Field in a with defining polynomial x^4 - 5*x^2 + 5 + # with a = 1.902113032590308? + + +AUTHORS: + +- Enrique Artal (2023-10): initial version +""" + +# ***************************************************************************** +# Copyright (C) 2013 David Perkinson +# Volker Braun +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License 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/ +# ***************************************************************************** + +# Possible extensions: + +# from sage.categories.fields import Fields +# from sage.categories.homset import Hom, End, hom +# from sage.categories.number_fields import NumberFields +from sage.combinat.permutation import Permutation +from sage.groups.free_group import FreeGroup +# from sage.interfaces.singular import singular +# from sage.matrix.constructor import matrix, vector +from sage.misc.cachefunc import cached_method +# from sage.misc.lazy_attribute import lazy_attribute +# from sage.rings.number_field.number_field import NumberField +# from sage.rings.polynomial.multi_polynomial_element import degree_lowest_rational_function +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +# from sage.rings.qqbar import number_field_elements_from_algebraics +from sage.rings.qqbar import QQbar +# from sage.rings.rational_field import QQ, is_RationalField +# from sage.rings.rational_field import is_RationalField +# from sage.rings.infinity import infinity +from sage.schemes.affine.affine_space import AffineSpace +# from sage.schemes.affine.affine_space import is_AffineSpace +# from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine +# from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine_field +from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement +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 .affine_curve import AffinePlaneCurve +# from .closed_point import IntegralAffineCurveClosedPoint +# from .curve import Curve_generic +# from .point import (AffineCurvePoint_field, +# AffinePlaneCurvePoint_field, +# AffinePlaneCurvePoint_finite_field, +# IntegralAffineCurvePoint, +# IntegralAffineCurvePoint_finite_field, +# IntegralAffinePlaneCurvePoint, +# IntegralAffinePlaneCurvePoint_finite_field) + + +class OrderedAffinePlaneCurveArrangementsElement(Element): + """ + An ordered affine plane curve arrangement. + # + # .. WARNING:: + # + # You should never create + # :class:`HyperplaneArrangementElement` instances directly, + # always use the parent. + """ + + def __init__(self, parent, curves, check=True): + """ + Construct an ordered affine plane curve arrangement. + + INPUT: + + - ``parent`` -- the parent :class:`HyperplaneArrangements` + + - ``hyperplanes`` -- a tuple of hyperplanes + + - ``check`` -- boolean (optional; default ``True``); whether + to check input + + - ``backend`` -- string (optional; default: ``None``); the backend to + use for the related polyhedral objects + + # EXAMPLES:: + # + # sage: H. = HyperplaneArrangements(QQ) + # sage: elt = H(x, y); elt + # Arrangement + # sage: TestSuite(elt).run() + """ + super().__init__(parent) + self._curves = curves + if check: + if not isinstance(curves, tuple): + raise ValueError("the curves must be given as a tuple") + if not all(isinstance(h, AffinePlaneCurve) for h in curves): + raise ValueError("not all elements are curves") + if not all(h.parent() is self.parent().ambient_space() for h in curves): + raise ValueError("not all curves are in the same ambient space") + + def __getitem__(self, i): + """ + Return the `i`-th curve. + + INPUT: + + - ``i`` -- integer + + OUTPUT: + + The `i`-th curve. + + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: h = x|y; h + # Arrangement + # sage: h[0] + # Hyperplane 0*x + y + 0 + # sage: h[1] + # Hyperplane x + 0*y + 0 + """ + return self._curves[i] + + def __hash__(self): + r""" + TESTS:: + + sage: H. = HyperplaneArrangements(QQ) + sage: h = x|y; h + Arrangement + sage: len_dict = {h: len(h)} + """ + return hash(self.curves()) + + def n_curves(self): + r""" + Return the number of curves in the arrangement. + + OUTPUT: + + An integer. + + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: A = H([1,1,0], [2,3,-1], [4,5,3]) + # sage: A.n_hyperplanes() + # 3 + # sage: len(A) # equivalent + # 3 + """ + return len(self._curves) + + __len__ = n_curves + + def curves(self): + r""" + Return the curves in the arrangement as a tuple. + + OUTPUT: + + An tuple. + + EXAMPLES:: + + sage: H. = HyperplaneArrangements(QQ) + sage: A = H([1,1,0], [2,3,-1], [4,5,3]) + sage: A.hyperplanes() + (Hyperplane x + 0*y + 1, Hyperplane 3*x - y + 2, Hyperplane 5*x + 3*y + 4) + + Note that the hyperplanes can be indexed as if they were a list:: + + sage: A[0] + Hyperplane x + 0*y + 1 + """ + return self._hyperplanes + + def _repr_(self): + r""" + String representation for a curve arrangement. + + OUTPUT: + + A string. + + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: H(x, y, x-1, y-1) + # Arrangement + # sage: x | y | x - 1 | y - 1 | x + y | x - y + # Arrangement of 6 hyperplanes of dimension 2 and rank 2 + # sage: H() + # Empty hyperplane arrangement of dimension 2 + """ + if len(self) == 0: + return 'Empty curve arrangement' + elif len(self) < 5: + curves = ' | '.join(h._repr_linear(include_zero=False) for h in self._curves) + return 'Arrangement <{0}>'.format(curves) + return 'Arrangement of {0} curves'.format(len(self)) + + def _richcmp_(self, other, op): + """ + Compare two hyperplane arrangements. + + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: H(x) == H(y) + # False + + TESTS:: + + # sage: H(x) == 0 + # False + """ + return richcmp(self._curves, other._curves, op) + + def union(self, other): + r""" + The union of ``self`` with ``other``. + + INPUT: + + - ``other`` -- a curvee arrangement or something that can + be converted into a curve arrangement + + OUTPUT: + + A new curve arrangement. + + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: H1. = OrderedHyperplaneArrangements(QQ) + # sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0]) + # sage: B = H([1,1,1], [1,-1,1], [1,0,-1]) + # sage: C = A.union(B); C + # Arrangement of 8 hyperplanes of dimension 2 and rank 2 + # sage: C == A | B # syntactic sugar + # True + # sage: A1 = H1(A) + # sage: B1 = H1(B) + # sage: C1 = A1.union(B1); C1 + # Arrangement of 8 hyperplanes of dimension 2 and rank 2 + # sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()] + # [0, 5, 6, 1, 2, 3, 7, 4] + # + # A single hyperplane is coerced into a hyperplane arrangement + # if necessary:: + # + # sage: A.union(x+y-1) + # Arrangement of 6 hyperplanes of dimension 2 and rank 2 + # sage: A.add_hyperplane(x+y-1) # alias + # Arrangement of 6 hyperplanes of dimension 2 and rank 2 + # + # sage: P. = HyperplaneArrangements(RR) + # sage: C = P(2*x + 4*y + 5) + # sage: C.union(A) + # Arrangement of 6 hyperplanes of dimension 2 and rank 2 + """ + P = self.parent() + other_h = P(other) + curves0 = self._curves + other_h._curves + curves = () + for h in curves0: + if h not in curves: + curves += (h, ) + else: + print("Repeated curve") + result = P(*curves) + return result + + add_curve = union + + __or__ = union + + def plot(self, **kwds): + """ + Plot an arrangement of curves. + + OUTPUT: + + A graphics object. + + EXAMPLES:: + + # sage: L. = HyperplaneArrangements(QQ) + # sage: L(x, y, x+y-2).plot() # needs sage.plot + # Graphics object consisting of 3 graphics primitives + """ + from sage.geometry.hyperplane_arrangement.plot import plot + return plot(self, **kwds) + + def deletion(self, curves): + r""" + Return the curve arrangement obtained by removing ``h``. + + INPUT: + + - ``h`` -- a curve or curve arrangement + + OUTPUT: + + A new curve arrangement with the given curve(s) + ``h`` removed. + + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: A = H([0,1,0], [1,0,1], [-1,0,1], [0,1,-1], [0,1,1]); A + # Arrangement of 5 hyperplanes of dimension 2 and rank 2 + # sage: A.deletion(x) + # Arrangement + # sage: h = H([0,1,0], [0,1,1]) + # sage: A.deletion(h) + # Arrangement + """ + parent = self.parent() + curves = parent(curves) + planes = list(self) + for curve in curves: + try: + planes.remove(curve) + except ValueError: + raise ValueError('curve is not in the arrangement') + return parent(*planes) + + def change_ring(self, base_ring): + """ + Return curve arrangement over the new base ring. + + INPUT: + + - ``base_ring`` -- the new base ring; must be a field for + curve arrangements + + OUTPUT: + + The curve arrangement obtained by changing the base + field, as a new curve arrangement. + + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: A = H([(1,1), 0], [(2,3), -1]) + # sage: A.change_ring(FiniteField(2)) + # Arrangement + """ + parent = self.parent().change_ring(base_ring) + return parent(self) + + def defining_polynomial(self): + r""" + Return the defining polynomial of ``A``. + + # Let `A = (H_i)_i` be a hyperplane arrangement in a vector space `V` + # corresponding to the null spaces of `\alpha_{H_i} \in V^*`. Then + # the *defining polynomial* of `A` is given by + # + # .. MATH:: + # + # Q(A) = \prod_i \alpha_{H_i} \in S(V^*). + + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: A = H([2*x + y - z, -x - 2*y + z]) + # sage: p = A.defining_polynomial(); p + # -2*x^2 - 5*x*y - 2*y^2 + 3*x*z + 3*y*z - z^2 + # sage: p.factor() + # (-1) * (x + 2*y - z) * (2*x + y - z) + """ + S = self.parent().ambient_space() + return S.prod(h.defining_polynomial() for h in self) + + 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. = OrderedHyperplaneArrangements(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 x + 0*y + 0, + Hyperplane 0*x + y + 0, + Hyperplane x + 0*y + 1, + Hyperplane 0*x + y + 1, + Hyperplane x - y + 0] + 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: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 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: [x0], 1: [x2], 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: A3. = OrderedHyperplaneArrangements(QQ) + sage: H = A3(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. + """ + 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]) + infinity0 = [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 infinity0, simplified=False) + if infinity0: + 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) + + +class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): + """ + Curve arrangements. + + # For more information on hyperplane arrangements, see + # :mod:`sage.geometry.hyperplane_arrangement.arrangement`. + + INPUT: + + - ``base_ring`` -- ring; the base ring + + - ``names`` -- tuple of strings; the variable names + + EXAMPLES:: + + sage: H. = HyperplaneArrangements(QQ) + sage: x + Hyperplane x + 0*y + 0 + sage: x + y + Hyperplane x + y + 0 + sage: H(x, y, x-1, y-1) + Arrangement + """ + Element = OrderedAffinePlaneCurveArrangementsElement + + def __init__(self, base_ring, names=tuple()): + """ + Initialize ``self``. + + TESTS:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: K = HyperplaneArrangements(QQ, names=('x', 'y')) + # sage: H is K + # True + # sage: type(K) + # + # sage: K.change_ring(RR).gen(0) + # Hyperplane 1.00000000000000*x + 0.000000000000000*y + 0.000000000000000 + + TESTS:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: TestSuite(H).run() + # sage: K = HyperplaneArrangements(QQ) + # sage: TestSuite(K).run() + """ + from sage.categories.sets_cat import Sets + from sage.rings.ring import _Fields + if base_ring not in _Fields: + raise ValueError('base ring must be a field') + super().__init__(category=Sets()) + self._base_ring = base_ring + self._names = names + + def base_ring(self): + """ + Return the base ring. + + OUTPUT: + + The base ring of the curve arrangement. + + EXAMPLES:: + + # sage: L. = HyperplaneArrangements(QQ) + # sage: L.base_ring() + # Rational Field + """ + return self._base_ring + + def change_ring(self, base_ring): + """ + Return curve arrangements over a different base ring. + + INPUT: + + - ``base_ring`` -- a ring; the new base ring. + + OUTPUT: + + A new :class:`OrderedAffinePlaneCurveArrangements` instance over the new + base ring. + + EXAMPLES:: + # + # sage: L. = HyperplaneArrangements(QQ) + # sage: L.gen(0) + # Hyperplane x + 0*y + 0 + # sage: L.change_ring(RR).gen(0) + # Hyperplane 1.00000000000000*x + 0.000000000000000*y + 0.000000000000000 + + TESTS:: + + # sage: L.change_ring(QQ) is L + # True + """ + return OrderedAffinePlaneCurveArrangements(base_ring, names=self._names) + + @cached_method + def ambient_space(self): + """ + Return the ambient space. + + # The ambient space is the parent of hyperplanes. That is, new + # hyperplanes are always constructed internally from the ambient + # space instance. + + EXAMPLES:: + + sage: L. = HyperplaneArrangements(QQ) + sage: L.ambient_space()([(1,0), 0]) + Hyperplane x + 0*y + 0 + sage: L.ambient_space()([(1,0), 0]) == x + True + """ + return AffineSpace(self.base_ring(), self._names) + + def _repr_(self): + """ + Return a string representation. + + OUTPUT: + + A string. + + EXAMPLES:: + + # sage: L. = HyperplaneArrangements(QQ); L + # Hyperplane arrangements in 2-dimensional linear space over Rational Field with coordinates x, y + """ + return 'Curve arrangements in {0}'.format(self.ambient_space()) + + def _element_constructor_(self, *args, **kwds): + """ + Construct an element of ``self``. + + INPUT: + + - ``*args`` -- positional arguments, each defining a + hyperplane; alternatively, a single polytope or a single + hyperplane arrangement + + - ``warn_duplicates`` -- boolean (optional, default: ``False``); + whether to issue a warning if duplicate hyperplanes were + passed -- note that duplicate hyperplanes are always removed, + whether or not there is a warning shown + + + EXAMPLES:: + + # sage: L. = HyperplaneArrangements(QQ) + # sage: L._element_constructor_(x, y) + # Arrangement + # sage: L._element_constructor_([x, y]) + # Arrangement + # sage: L._element_constructor_([0, 1, 0], [0, 0, 1]) + # Arrangement + # sage: L._element_constructor_([[0, 1, 0], [0, 0, 1]]) + # Arrangement + # + # sage: L._element_constructor_(polytopes.hypercube(2)) + # Arrangement <-x + 1 | -y + 1 | y + 1 | x + 1> + # + # sage: L(x, x, warn_duplicates=True) + # doctest:...: UserWarning: Input contained 2 hyperplanes, but only 1 are distinct. + # Arrangement + # sage: L(-x, x + y - 1, signed=False) + # Arrangement <-x - y + 1 | x> + # + # TESTS:: + # + # sage: L() + # Empty hyperplane arrangement of dimension 2 + # sage: L(0) # zero is equivalent to no argument, Issue #8648 + # Empty hyperplane arrangement of dimension 2 + # sage: L(0*x) # degenerate hyperplane is NOT allowed + # Traceback (most recent call last): + # ... + # ValueError: linear expression must be non-constant to define a hyperplane + # sage: L(0*x, y) # ditto + # Traceback (most recent call last): + # ... + # ValueError: linear expression must be non-constant to define a hyperplane + """ + if len(args) == 1: + arg = args[0] + if isinstance(arg, OrderedAffinePlaneCurveArrangementsElement) and args[0].parent() is self: + # optimization if argument is already a hyperplane arrangement + return arg + if arg == 0 and not isinstance(arg, AffinePlaneCurve): + # zero = neutral element under addition = the empty hyperplane arrangement + args = [] + # process keyword arguments + warn_duplicates = kwds.pop('warn_duplicates', False) + if len(kwds) > 0: + raise ValueError('unknown keyword argument') + # process positional arguments + AA = self.ambient_space() + try: + curves = [AA(_) for _ in args] + except (TypeError, ValueError, AttributeError): + if len(args) > 1: + raise + arg = args[0] + curves0 = [h.primitive() for h in curves] + n = len(curves0) + curves = () + for h in curves0: + if h not in curves: + curves += (h, ) + if warn_duplicates and n != len(curves): + from warnings import warn + warn('Input contained {0} curves, but only {1} are distinct.'.format(n, len(curves))) + # argument checking (optional but recommended) + return self.element_class(self, curves) + + @cached_method + def gens(self): + """ + Return the coordinates. + + OUTPUT: + + A tuple of linear expressions, one for each linear variable. + + EXAMPLES:: + + # sage: L = HyperplaneArrangements(QQ, ('x', 'y', 'z')) + # sage: L.gens() + # (Hyperplane x + 0*y + 0*z + 0, + # Hyperplane 0*x + y + 0*z + 0, + # Hyperplane 0*x + 0*y + z + 0) + """ + return self.ambient_space().gens() + + def gen(self, i): + """ + Return the `i`-th coordinate. + + INPUT: + + - ``i`` -- integer + + OUTPUT: + + A linear expression. + + EXAMPLES:: + + # sage: L. = HyperplaneArrangements(QQ); L + # 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 + """ + return self.gens()[i] + + def _coerce_map_from_(self, P): + """ + Return whether there is a coercion. + + TESTS:: + + sage: L. = HyperplaneArrangements(QQ); L + Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x + sage: M. = HyperplaneArrangements(RR); M + Hyperplane arrangements in 1-dimensional linear space over Real Field with 53 bits of precision with coordinate y + + sage: L.coerce_map_from(ZZ) + Coercion map: + From: Integer Ring + To: Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x + sage: M.coerce_map_from(L) + Coercion map: + From: Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x + To: Hyperplane arrangements in 1-dimensional linear space over Real Field with 53 bits of precision with coordinate y + sage: L.coerce_map_from(M) + """ + if self.ambient_space().has_coerce_map_from(P): + return True + if isinstance(P, OrderedAffinePlaneCurveArrangements): + return self.base_ring().has_coerce_map_from(P.base_ring()) + return super()._coerce_map_from_(P) From a6a2481360d9e8821f70df0cf7c2eff67ae40c61 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 1 Oct 2023 17:48:02 +0200 Subject: [PATCH 19/95] errors in zvk, still not class of arrangements of curves --- src/sage/schemes/curves/all.py | 5 ++- .../schemes/curves/plane_curve_arrangement.py | 33 ++++++++++++++----- src/sage/schemes/curves/zariski_vankampen.py | 11 ++++--- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/sage/schemes/curves/all.py b/src/sage/schemes/curves/all.py index 147c2e1e6fe..c623b85e560 100644 --- a/src/sage/schemes/curves/all.py +++ b/src/sage/schemes/curves/all.py @@ -22,4 +22,7 @@ from .constructor import Curve -from .projective_curve import Hasse_bounds +from .projective_curve import Hasse_bounds + +from .plane_curve_arrangement import OrderedAffinePlaneCurveArrangements + diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 74ff81c9ffe..d58458bfdea 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -58,14 +58,17 @@ # Possible extensions: + # from sage.categories.fields import Fields # from sage.categories.homset import Hom, End, hom # from sage.categories.number_fields import NumberFields +from sage.categories.sets_cat import Sets from sage.combinat.permutation import Permutation from sage.groups.free_group import FreeGroup # from sage.interfaces.singular import singular # from sage.matrix.constructor import matrix, vector from sage.misc.cachefunc import cached_method +# from sage.rings.infinity import infinity # from sage.misc.lazy_attribute import lazy_attribute # from sage.rings.number_field.number_field import NumberField # from sage.rings.polynomial.multi_polynomial_element import degree_lowest_rational_function @@ -74,7 +77,7 @@ from sage.rings.qqbar import QQbar # from sage.rings.rational_field import QQ, is_RationalField # from sage.rings.rational_field import is_RationalField -# from sage.rings.infinity import infinity +from sage.rings.ring import _Fields from sage.schemes.affine.affine_space import AffineSpace # from sage.schemes.affine.affine_space import is_AffineSpace # from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine @@ -86,7 +89,7 @@ from sage.structure.unique_representation import UniqueRepresentation -from .affine_curve import AffinePlaneCurve +from sage.schemes.curves.affine_curve import AffinePlaneCurve # from .closed_point import IntegralAffineCurveClosedPoint # from .curve import Curve_generic # from .point import (AffineCurvePoint_field, @@ -218,7 +221,7 @@ def curves(self): sage: A[0] Hyperplane x + 0*y + 1 """ - return self._hyperplanes + return self._curves def _repr_(self): r""" @@ -525,9 +528,6 @@ class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): """ Curve arrangements. - # For more information on hyperplane arrangements, see - # :mod:`sage.geometry.hyperplane_arrangement.arrangement`. - INPUT: - ``base_ring`` -- ring; the base ring @@ -568,8 +568,6 @@ def __init__(self, base_ring, names=tuple()): # sage: K = HyperplaneArrangements(QQ) # sage: TestSuite(K).run() """ - from sage.categories.sets_cat import Sets - from sage.rings.ring import _Fields if base_ring not in _Fields: raise ValueError('base ring must be a field') super().__init__(category=Sets()) @@ -738,6 +736,25 @@ def _element_constructor_(self, *args, **kwds): # argument checking (optional but recommended) return self.element_class(self, curves) + @cached_method + def ngens(self): + """ + Return the number of linear variables. + + OUTPUT: + + An integer. + + EXAMPLES:: + + sage: L. = HyperplaneArrangements(QQ); L + Hyperplane arrangements in 3-dimensional linear space + over Rational Field with coordinates x, y, z + sage: L.ngens() + 3 + """ + return len(self._names) + @cached_method def gens(self): """ diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index e64acc1e28f..9f85aa4ff25 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1225,7 +1225,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): glist = tuple(_[0] for f0 in arrangement_h for _ in f0.factor()) g = f.parent()(prod(glist)) d = g.degree(y) - if not dic_vertical: + if not arrangement_v: while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) @@ -1240,7 +1240,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): for f0 in arrangement_v: pt = [j for j, t in enumerate(disc) if f0(x=t) == 0] if pt: - vertical_braid[f0] = (disc.index(pt[0]), arrangement1.index(f0)) + vertical_braid[f0] = (pt[0], arrangement1.index(f0)) else: transversal[f0] = arrangement1.index(f0) if not disc: @@ -1762,8 +1762,8 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis vertical0 = bool(vertical) if projective: vertical0 = False - while f.degree(y) < f.degree(): - flist1 = [g.subs({x: x + y}) for g in flist] + while f.degree(y) < d: + flist1 = [g.subs({x: x + y}) for g in flist1] f = prod(flist1) if len(flist1) == 0: bm = [] @@ -1782,7 +1782,8 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis for i in range(len(flist1)): L = [j1 for j1 in dic.keys() if dic[j1] == i] dic1[i] = [hom(g.gen(j)) for j in L] - if not projective and f.degree(y) == f.degree(): + # if not projective and f.degree(y) == f.degree(): + if not projective: t = prod(hom(x) for x in g.gens()).inverse() dic1[len(flist1)] = [t] n = g1.ngens() From 1715071553783bde6b99fbf48d6df140c8b05ead Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 2 Oct 2023 00:20:24 +0200 Subject: [PATCH 20/95] new method for arrangements of curves --- .../schemes/curves/plane_curve_arrangement.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index d58458bfdea..661af2829ce 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -145,6 +145,22 @@ def __init__(self, parent, curves, check=True): if not all(h.parent() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") + def _first_ngens(self, n): + """ + Workaround to support the construction with names. + + INPUT/OUTPUT: + + See :meth:`OrderedAffinePlaneCurveArrangements._first_ngens`. + + EXAMPLES:: + + sage: a. = hyperplane_arrangements.braid(3) # indirect doctest # needs sage.graphs + sage: (x, y) == a._first_ngens(2) # needs sage.graphs + True + """ + return self.parent()._first_ngens(n) + def __getitem__(self, i): """ Return the `i`-th curve. From a7c2f9b0b72a970deeecfb69fe5b547d39bd3fb8 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 2 Oct 2023 08:07:15 +0200 Subject: [PATCH 21/95] lint --- src/sage/schemes/curves/all.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/curves/all.py b/src/sage/schemes/curves/all.py index c623b85e560..24adf1e2249 100644 --- a/src/sage/schemes/curves/all.py +++ b/src/sage/schemes/curves/all.py @@ -2,7 +2,7 @@ Plane curves """ -#***************************************************************************** +# ***************************************************************************** # # Sage: Open Source Mathematical Software # @@ -18,11 +18,10 @@ # The full text of the GPL is available at: # # http://www.gnu.org/licenses/ -#***************************************************************************** +# ***************************************************************************** from .constructor import Curve -from .projective_curve import Hasse_bounds +from .projective_curve import Hasse_bounds from .plane_curve_arrangement import OrderedAffinePlaneCurveArrangements - From fff657b66962155b5f9426da0e4ec3cd7d3d08cf Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 2 Oct 2023 15:11:22 +0200 Subject: [PATCH 22/95] the class exists --- .../schemes/curves/plane_curve_arrangement.py | 235 +++++++++--------- src/sage/schemes/curves/zariski_vankampen.py | 7 +- 2 files changed, 122 insertions(+), 120 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 661af2829ce..ed02db82494 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -63,16 +63,18 @@ # from sage.categories.homset import Hom, End, hom # from sage.categories.number_fields import NumberFields from sage.categories.sets_cat import Sets -from sage.combinat.permutation import Permutation -from sage.groups.free_group import FreeGroup +from sage.combinat.combination import Combinations +# from sage.combinat.permutation import Permutation +# from sage.groups.free_group import FreeGroup # from sage.interfaces.singular import singular # from sage.matrix.constructor import matrix, vector from sage.misc.cachefunc import cached_method +from sage.misc.misc_c import prod # from sage.rings.infinity import infinity # from sage.misc.lazy_attribute import lazy_attribute # from sage.rings.number_field.number_field import NumberField # from sage.rings.polynomial.multi_polynomial_element import degree_lowest_rational_function -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +# from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing # from sage.rings.qqbar import number_field_elements_from_algebraics from sage.rings.qqbar import QQbar # from sage.rings.rational_field import QQ, is_RationalField @@ -82,14 +84,14 @@ # from sage.schemes.affine.affine_space import is_AffineSpace # from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine # from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine_field +from sage.schemes.curves.affine_curve import AffinePlaneCurve +from sage.schemes.curves.constructor import Curve from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement 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.schemes.curves.affine_curve import AffinePlaneCurve # from .closed_point import IntegralAffineCurveClosedPoint # from .curve import Curve_generic # from .point import (AffineCurvePoint_field, @@ -142,7 +144,7 @@ def __init__(self, parent, curves, check=True): raise ValueError("the curves must be given as a tuple") if not all(isinstance(h, AffinePlaneCurve) for h in curves): raise ValueError("not all elements are curves") - if not all(h.parent() is self.parent().ambient_space() for h in curves): + if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") def _first_ngens(self, n): @@ -260,8 +262,8 @@ def _repr_(self): if len(self) == 0: return 'Empty curve arrangement' elif len(self) < 5: - curves = ' | '.join(h._repr_linear(include_zero=False) for h in self._curves) - return 'Arrangement <{0}>'.format(curves) + curves = ', '.join(h._repr_() for h in self._curves) + return 'Arrangement ({0})'.format(curves) return 'Arrangement of {0} curves'.format(len(self)) def _richcmp_(self, other, op): @@ -415,9 +417,9 @@ def change_ring(self, base_ring): parent = self.parent().change_ring(base_ring) return parent(self) - def defining_polynomial(self): + def defining_polynomials(self): r""" - Return the defining polynomial of ``A``. + Return the defining polynomials of ``A``. # Let `A = (H_i)_i` be a hyperplane arrangement in a vector space `V` # corresponding to the null spaces of `\alpha_{H_i} \in V^*`. Then @@ -436,107 +438,108 @@ def defining_polynomial(self): # sage: p.factor() # (-1) * (x + 2*y - z) * (2*x + y - z) """ - S = self.parent().ambient_space() - return S.prod(h.defining_polynomial() for h in self) + return tuple(h.defining_polynomial() for h in self) - def fundamental_group(self, proj=False): + def defining_polynomial(self, simplified=True): 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`` + Return the defining polynomial of ``A``. - INPUT: + # Let `A = (H_i)_i` be a hyperplane arrangement in a vector space `V` + # corresponding to the null spaces of `\alpha_{H_i} \in V^*`. Then + # the *defining polynomial* of `A` is given by + # + # .. MATH:: + # + # Q(A) = \prod_i \alpha_{H_i} \in S(V^*). - - ``proj`` -- (optional, default ``False``). It decides if it computes the - fundamental group of the complement in the affine or projective space + EXAMPLES:: + + # sage: H. = HyperplaneArrangements(QQ) + # sage: A = H([2*x + y - z, -x - 2*y + z]) + # sage: p = A.defining_polynomial(); p + # -2*x^2 - 5*x*y - 2*y^2 + 3*x*z + 3*y*z - z^2 + # sage: p.factor() + # (-1) * (x + 2*y - z) * (2*x + y - z) + """ + return prod(self.defining_polynomials()) + + def have_common_factors(self): + L = [c.defining_polynomial() for c in self] + C = Combinations(L, 2) + for f1, f2 in C: + if f1.gcd(f2).degree() > 0: + return True + return False + + def reduce(self): + P = self.parent() + L = [c.defining_polynomial().radical() for c in self] + return P(*L) + + def fundamental_group(self, vertical=True): + r""" + It computes the fundamental group of the complement of the union + of affine projective curves in `\mathbb{C}^2`. OUTPUT: - A group finitely presented with the assignation of each hyperplane to - a member of a group (meridian). + A group finitely presented with the assignation of each curve to + a set of meridians, including the line at infinity. EXAMPLES:: - sage: A. = OrderedHyperplaneArrangements(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 x + 0*y + 0, - Hyperplane 0*x + y + 0, - Hyperplane x + 0*y + 1, - Hyperplane 0*x + y + 1, - Hyperplane x - y + 0] - 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: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 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: [x0], 1: [x2], 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: A3. = OrderedHyperplaneArrangements(QQ) - sage: H = A3(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} + # sage: A. = OrderedHyperplaneArrangements(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 x + 0*y + 0, + # Hyperplane 0*x + y + 0, + # Hyperplane x + 0*y + 1, + # Hyperplane 0*x + y + 1, + # Hyperplane x - y + 0] + # 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: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 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: [x0], 1: [x2], 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: A3. = OrderedHyperplaneArrangements(QQ) + # sage: H = A3(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. """ - 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]) - infinity0 = [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 infinity0, simplified=False) - if infinity0: - p = Permutation([r] + [j for j in range(1, r)]) - dic = {j: dic[p(j + 1) - 1] for j in range(r)} + C = self.reduce() + L = C.defining_polynomials() + G, dic = fundamental_group_arrangement(L, puiseux=True, vertical=vertical) return (G, dic) @@ -651,7 +654,7 @@ def ambient_space(self): sage: L.ambient_space()([(1,0), 0]) == x True """ - return AffineSpace(self.base_ring(), self._names) + return AffineSpace(self.base_ring(), 2, self._names) def _repr_(self): """ @@ -720,36 +723,32 @@ def _element_constructor_(self, *args, **kwds): # ... # ValueError: linear expression must be non-constant to define a hyperplane """ - if len(args) == 1: - arg = args[0] - if isinstance(arg, OrderedAffinePlaneCurveArrangementsElement) and args[0].parent() is self: - # optimization if argument is already a hyperplane arrangement - return arg - if arg == 0 and not isinstance(arg, AffinePlaneCurve): - # zero = neutral element under addition = the empty hyperplane arrangement - args = [] + if len(args) == 1 and not (isinstance(args[0], (tuple, list))): + arg = (args[0], ) + elif len(args) == 1: + arg = tuple(args[0]) + else: + arg = tuple(args) # process keyword arguments - warn_duplicates = kwds.pop('warn_duplicates', False) if len(kwds) > 0: raise ValueError('unknown keyword argument') # process positional arguments AA = self.ambient_space() - try: - curves = [AA(_) for _ in args] - except (TypeError, ValueError, AttributeError): - if len(args) > 1: - raise - arg = args[0] - curves0 = [h.primitive() for h in curves] - n = len(curves0) + R = AA.coordinate_ring() curves = () - for h in curves0: - if h not in curves: - curves += (h, ) - if warn_duplicates and n != len(curves): - from warnings import warn - warn('Input contained {0} curves, but only {1} are distinct.'.format(n, len(curves))) - # argument checking (optional but recommended) + for h in arg: + try: + ambient = h.ambient_space() + if ambient == AA: + curves += (h, ) + else: + return None + except AttributeError: + try: + h = R(h) + curves += (Curve(h), ) + except TypeError: + return None return self.element_class(self, curves) @cached_method diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 9f85aa4ff25..fdc66806a2c 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1768,9 +1768,12 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis if len(flist1) == 0: bm = [] dic = dict() + dv = {j: j for j, f in flist} + d1 = 0 else: - bm, dic, dv, d = braid_monodromy(f, flist1, vertical=vertical0) - g = fundamental_group_from_braid_mon(bm, degree=d, simplified=False, projective=projective, puiseux=puiseux) + bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical0) + vert_lines = [] + g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False, projective=projective, puiseux=puiseux, vertical=vert_lines) if simplified: hom = g.simplification_isomorphism() else: From 06b614a9e0c49cb704e734b55f2a404131236e28 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 2 Oct 2023 18:39:25 +0200 Subject: [PATCH 23/95] corrections --- .../schemes/curves/plane_curve_arrangement.py | 4 ++ src/sage/schemes/curves/zariski_vankampen.py | 48 +++++++++++++------ 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index ed02db82494..e54b637afe6 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -86,6 +86,7 @@ # from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine_field from sage.schemes.curves.affine_curve import AffinePlaneCurve from sage.schemes.curves.constructor import Curve +from sage.schemes.curves.zariski_vankampen import braid_monodromy from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement from sage.structure.parent import Parent from sage.structure.element import Element @@ -542,6 +543,9 @@ def fundamental_group(self, vertical=True): G, dic = fundamental_group_arrangement(L, puiseux=True, vertical=vertical) return (G, dic) + def braid_monodromy(self, vertical=False): + L = self.defining_polynomials() + return braid_monodromy(prod(L), arrangement=L, vertical=vertical) class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): """ diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index fdc66806a2c..ae7f10707da 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1207,21 +1207,23 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: arrangement1 = tuple(_.change_ring(F) for _ in arrangement) x, y = f.parent().gens() - dic_vertical = {f0: False for f0 in arrangement1} + dic_vertical = {j: False for j, f0 in enumerate(arrangement1)} if vertical: - for f0 in arrangement1: + for j, f0 in arrangement1: if f0.degree(y) < f0.degree() and f0.degree() > 1: - dic_vertical = {f0: False for f0 in arrangement1} + dic_vertical = {j1: False for j1, f1 in arrangement1} break else: - dic_vertical[f0] = f0.degree(y) < f0.degree(x) and f0.degree(x) == 1 + dic_vertical[j] = f0.degree(y) < f0.degree(x) and f0.degree(x) == 1 arrangement_h = () + indices_h = () arrangement_v = () - for f0 in arrangement1: - if dic_vertical[f0]: + for j, f0 in enumerate(arrangement1): + if dic_vertical[j]: arrangement_v += (f0, ) else: arrangement_h += (f0, ) + indices_h += (j, ) glist = tuple(_[0] for f0 in arrangement_h for _ in f0.factor()) g = f.parent()(prod(glist)) d = g.degree(y) @@ -1230,6 +1232,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): g = g.subs({x: x + y}) d = g.degree(y) arrangement_h = tuple(f1.subs({x: x + y}) for f1 in arrangement_h) + arrangement1 = arrangement_h glist = tuple(f1.subs({x: x + y}) for f1 in glist) if d > 0: disc = discrim(glist) @@ -1244,7 +1247,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: transversal[f0] = arrangement1.index(f0) if not disc: - vertical_braids = {i: j for i, j in enumerate(transversal)} + vertical_braids = {i + d: transversal[f0] for i, f0 in enumerate(transversal)} if d > 1: result = [BraidGroup(d).one() for p in transversal] else: @@ -1252,14 +1255,24 @@ def braid_monodromy(f, arrangement=(), vertical=False): p1 = F(0) if d > 0: roots_base, strands = strand_components(g, arrangement_h, p1) + strands1 = dict() + for j in range(d): + i = strands[j] + k = arrangement1.index(arrangement_h[i]) + strands1[j] = k else: - strands = dict() - return (result, strands, vertical_braids, d) + strands1 = dict() + return (result, strands1, vertical_braids, d) V = corrected_voronoi_diagram(tuple(disc)) G, E, p, EC, DG, VR = voronoi_cells(V) p0 = (p[0], p[1]) p1 = p0[0] + I1 * p0[1] roots_base, strands = strand_components(g, arrangement_h, p1) + strands1 = dict() + for j in range(d): + i = strands[j] + k = arrangement1.index(arrangement_h[i]) + strands1[j] = k geombasis, vd = geometric_basis(G, E, EC, p, DG, VR) segs = set() for p in geombasis: @@ -1307,7 +1320,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): vertical_braids[r + t] = transversal[f0] t += 1 result.append(B.one()) - return (result, strands, vertical_braids, d) + return (result, strands1, vertical_braids, d) def conjugate_positive_form(braid): @@ -1738,8 +1751,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis (Finitely presented group < | >, {}) sage: g, dic = fundamental_group_arrangement([x * y]) sage: g.sorted_presentation(), dic - (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, - {0: [x0, x1], 1: [x1^-1*x0^-1]}) + (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, {0: [x0, x1]}) sage: fundamental_group_arrangement([y + x^2], projective=True) (Finitely presented group < x | x^2 >, {0: [x0, x0]}) @@ -1765,6 +1777,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis while f.degree(y) < d: flist1 = [g.subs({x: x + y}) for g in flist1] f = prod(flist1) + if not vertical0: + infinity = f.degree(y) == f.degree() + if vertical0: + flist_a = [g for g in flist1 if g.degree(y) < g.degree() > 1] + infinity = not flist_a if len(flist1) == 0: bm = [] dic = dict() @@ -1772,7 +1789,10 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis d1 = 0 else: bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical0) - vert_lines = [] + vert_lines = [] + if vertical0: + for j in dv.keys(): + dic[d + j] = dv[j] g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False, projective=projective, puiseux=puiseux, vertical=vert_lines) if simplified: hom = g.simplification_isomorphism() @@ -1786,7 +1806,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis 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(): - if not projective: + if not projective and infinity: t = prod(hom(x) for x in g.gens()).inverse() dic1[len(flist1)] = [t] n = g1.ngens() From e97e7aa124b592ac6b268961e22462782a595b22 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 2 Oct 2023 20:02:23 +0200 Subject: [PATCH 24/95] doctesting voronoi_cells --- .../schemes/curves/plane_curve_arrangement.py | 1 + src/sage/schemes/curves/zariski_vankampen.py | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index e54b637afe6..eaebfd911ed 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -547,6 +547,7 @@ def braid_monodromy(self, vertical=False): L = self.defining_polynomials() return braid_monodromy(prod(L), arrangement=L, vertical=vertical) + class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): """ Curve arrangements. diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index ae7f10707da..bf1fd42951f 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -380,6 +380,8 @@ def voronoi_cells(V, vertical_lines=[]): - ``V`` -- a corrected Voronoi diagram + - ``vertical_lines`` -- list (default: `[]`) indices of the vertical lines + OUTPUT: - ``G`` -- the graph of the 1-skeleton of ``V`` @@ -396,9 +398,7 @@ def voronoi_cells(V, vertical_lines=[]): 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, VR = voronoi_cells(V) - sage: VR == dict() - True + sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=(1,)) sage: Gv = G.vertices(sort=True) sage: Ge = G.edges(sort=True) sage: len(Gv), len(Ge) @@ -446,11 +446,18 @@ def voronoi_cells(V, vertical_lines=[]): (A vertex at (2000001/2000000, 500001/1000000), A vertex at (11/4, 4), None)) sage: edg[-1] in Ge True + sage: VR + {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))} """ regions = V.regions() points = [p for p in V.regions().keys() if V.regions()[p].is_compact()] compact_regions = [regions[p] for p in points] - vertical_regions = {j: regions[points[j]] for j in vertical_lines} + vertical_regions = dict() # {j: regions[points[j]] for j in vertical_lines} 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') @@ -460,6 +467,8 @@ def voronoi_cells(V, vertical_lines=[]): DG = Graph() for i, reg in enumerate(compact_regions): Greg0 = orient_circuit(reg.graph().eulerian_circuit(), convex=True) + if i in vertical_lines: + vertical_regions[i] = Greg0 # Greg = (Greg0[0][0],) + tuple(e[1] for e in Greg0) DG.add_vertex((i, Greg0)) for e in G.edges(sort=True): @@ -1209,9 +1218,9 @@ def braid_monodromy(f, arrangement=(), vertical=False): x, y = f.parent().gens() dic_vertical = {j: False for j, f0 in enumerate(arrangement1)} if vertical: - for j, f0 in arrangement1: + for j, f0 in enumerate(arrangement1): if f0.degree(y) < f0.degree() and f0.degree() > 1: - dic_vertical = {j1: False for j1, f1 in arrangement1} + dic_vertical = {j1: False for j1, f1 in enumerate(arrangement1)} break else: dic_vertical[j] = f0.degree(y) < f0.degree(x) and f0.degree(x) == 1 @@ -1232,7 +1241,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): g = g.subs({x: x + y}) d = g.degree(y) arrangement_h = tuple(f1.subs({x: x + y}) for f1 in arrangement_h) - arrangement1 = arrangement_h + arrangement1 = arrangement_h glist = tuple(f1.subs({x: x + y}) for f1 in glist) if d > 0: disc = discrim(glist) @@ -1240,12 +1249,15 @@ def braid_monodromy(f, arrangement=(), vertical=False): disc = [] vertical_braid = dict() transversal = dict() + vl = [] for f0 in arrangement_v: pt = [j for j, t in enumerate(disc) if f0(x=t) == 0] if pt: vertical_braid[f0] = (pt[0], arrangement1.index(f0)) + vl.append(j) else: transversal[f0] = arrangement1.index(f0) + vl.sort() if not disc: vertical_braids = {i + d: transversal[f0] for i, f0 in enumerate(transversal)} if d > 1: @@ -1263,7 +1275,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: strands1 = dict() return (result, strands1, vertical_braids, d) - V = corrected_voronoi_diagram(tuple(disc)) + V = corrected_voronoi_diagram(tuple(disc), vertical_lines=vl) G, E, p, EC, DG, VR = voronoi_cells(V) p0 = (p[0], p[1]) p1 = p0[0] + I1 * p0[1] From afc4f229cca25e1434678eefd4b36ea51d598d20 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 3 Oct 2023 01:00:05 +0200 Subject: [PATCH 25/95] geometric basis with dictionnary --- src/sage/schemes/curves/zariski_vankampen.py | 80 +++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index bf1fd42951f..e487f7f3e7f 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -220,7 +220,7 @@ def discrim_pairs(f, g): @cached_function def corrected_voronoi_diagram(points): - r""" + 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. @@ -954,38 +954,44 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): to a region, surrounds it, and comes back by the same path it came. The concatenation of all these paths is equivalent to ``E``. - The dictionnary fixes the positions of the generators associated with the vertical lines. + The dictionnary associates to each vertical line the index of the generator + of the geometric basis associated to it. EXAMPLES:: - sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, voronoi_cells - sage: points = [(-3,0),(3,0),(0,3),(0,-3)]+ [(0,0),(0,-1),(0,1),(1,0),(-1,0)] - sage: V = VoronoiDiagram(points) - sage: G, E, p, EC, DG, VR = voronoi_cells(V) - sage: geometric_basis(G, E, EC, p, DG, dict()) - ([[A vertex at (-2, -2), A vertex at (2, -2), A vertex at (2, 2), - A vertex at (1/2, 1/2), A vertex at (1/2, -1/2), - A vertex at (2, -2), A vertex at (-2, -2)], - [A vertex at (-2, -2), A vertex at (2, -2), A vertex at (1/2, -1/2), - A vertex at (1/2, 1/2), A vertex at (-1/2, 1/2), - A vertex at (-1/2, -1/2), A vertex at (1/2, -1/2), - A vertex at (2, -2), A vertex at (-2, -2)], - [A vertex at (-2, -2), A vertex at (2, -2), A vertex at (1/2, -1/2), - A vertex at (-1/2, -1/2), A vertex at (-2, -2)], - [A vertex at (-2, -2), A vertex at (-1/2, -1/2), - A vertex at (-1/2, 1/2), A vertex at (1/2, 1/2), - A vertex at (2, 2), A vertex at (-2, 2), A vertex at (-1/2, 1/2), - A vertex at (-1/2, -1/2), A vertex at (-2, -2)], - [A vertex at (-2, -2), A vertex at (-1/2, -1/2), - A vertex at (-1/2, 1/2), A vertex at (-2, 2), - A vertex at (-2, -2)]], {}) + sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, corrected_voronoi_diagram, voronoi_cells + sage: points = (0, -1, I, 1, -I) + sage: V = corrected_voronoi_diagram(points) + sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=[0, 1, 2, 3, 4]) + sage: gb, vd = geometric_basis(G, E, EC, p, DG, VR) + sage: gb + [[A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (-5/2, 5/2), + A vertex at (-1/2, 1/2), A vertex at (-1/2, -1/2), A vertex at (1/2, -1/2), + A vertex at (1/2, 1/2), A vertex at (-1/2, 1/2), A vertex at (-5/2, 5/2), + A vertex at (5/2, 5/2), A vertex at (5/2, -5/2)], + [A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (-5/2, 5/2), + A vertex at (-1/2, 1/2), A vertex at (1/2, 1/2), A vertex at (5/2, 5/2), + A vertex at (5/2, -5/2)], + [A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (1/2, 1/2), + A vertex at (1/2, -1/2), A vertex at (5/2, -5/2)], [A vertex at (5/2, -5/2), + A vertex at (1/2, -1/2), A vertex at (-1/2, -1/2), A vertex at (-1/2, 1/2), + A vertex at (-5/2, 5/2), A vertex at (-5/2, -5/2), A vertex at (-1/2, -1/2), + A vertex at (1/2, -1/2), A vertex at (5/2, -5/2)], + [A vertex at (5/2, -5/2), A vertex at (1/2, -1/2), A vertex at (-1/2, -1/2), + A vertex at (-5/2, -5/2), A vertex at (5/2, -5/2)]] + sage: vd + {0: 0, 1: 3, 2: 1, 3: 2, 4: 4} """ i = EC0.index(p) - EC = EC0[i:-1] + EC0[:i + 1] # A counterclockwise eulerian circuit on the boundary, starting and ending at 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(): - vert_dict = vert_dict = {0: vertical_regions[p] for p in vertical_regions.keys()} - return [[EC], vert_dict] + j = list(dual_graph.vertices())[0][0] + if j in vertical_regions: + vd = {j: 0} + else: + vd = dict() + return [EC], vd 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) @@ -1070,18 +1076,18 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): EC1 = list(EC[qi:] + EC[1:ri]) + list(reversed(cutpath)) EC2 = cutpath + list(EC[ri + 1:qi + 1]) - regs1 = [v[1] for v in Gd1.vertices()] - VR1 = {p: vertical_regions[p] for p in vertical_regions.keys() if p in regs1} - regs2 = [v[1] for v in Gd2.vertices()] - VR2 = {p: vertical_regions[p] for p in vertical_regions.keys() if p in regs2} + # regs1 = [v[1] for v in Gd1.vertices()] + # VR1 = {p: vertical_regions[p] for p in vertical_regions.keys() if p in regs1} + # regs2 = [v[1] for v in Gd2.vertices()] + # VR2 = {p: vertical_regions[p] for p in vertical_regions.keys() if p in regs2} - gb1, vd1 = geometric_basis(G1, E1, EC1, q, Gd1, VR1) - gb2, vd2 = geometric_basis(G2, E2, EC2, q, Gd2, VR2) + gb1, vd1 = geometric_basis(G1, E1, EC1, q, Gd1, vertical_regions) + gb2, vd2 = geometric_basis(G2, E2, EC2, q, Gd2, vertical_regions) - vd = {j: vd1[j] for j in vd1.keys()} + vd = {j: vd1[j] for j in vd1} m = len(gb1) for j in vd2.keys(): - vd[j + m] = vd2[j] + vd[j] = vd2[j] + m reverse_connecting = list(reversed(connecting_path)) resul = [connecting_path + path + reverse_connecting for path in gb1 + gb2] @@ -1275,8 +1281,8 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: strands1 = dict() return (result, strands1, vertical_braids, d) - V = corrected_voronoi_diagram(tuple(disc), vertical_lines=vl) - G, E, p, EC, DG, VR = voronoi_cells(V) + V = corrected_voronoi_diagram(tuple(disc)) + G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=vl) p0 = (p[0], p[1]) p1 = p0[0] + I1 * p0[1] roots_base, strands = strand_components(g, arrangement_h, p1) @@ -1327,7 +1333,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): for f0 in arrangement_v: if f0 in vertical_braid.keys(): k, j = vertical_braid[f0] - vertical_braids[k] = j + vertical_braids[j] = vd[k] else: vertical_braids[r + t] = transversal[f0] t += 1 From bd6df7cb2c9168451dba3e68c3fb4784cf87972a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 3 Oct 2023 02:10:24 +0200 Subject: [PATCH 26/95] typo --- 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 e487f7f3e7f..e4dc5270e17 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -220,7 +220,7 @@ def discrim_pairs(f, g): @cached_function def corrected_voronoi_diagram(points): - r"""+ + 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. From 4e3acdb671e9cce659b0ec9ccfb8c5b41bab55d2 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 3 Oct 2023 18:39:51 +0200 Subject: [PATCH 27/95] correction of braid in segment --- src/sage/schemes/curves/zariski_vankampen.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index e4dc5270e17..3eb0c813158 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -866,8 +866,6 @@ def braid_in_segment(glist, x0, x1, precision=dict()): g = prod(glist) F1 = g.base_ring() x, y = g.parent().gens() - X0 = F1(x0) - X1 = F1(x1) intervals = {} if not precision1: # new precision1 = {f: 53 for f in glist} # new @@ -876,7 +874,7 @@ def braid_in_segment(glist, x0, x1, precision=dict()): if f.variables() == (y,): f0 = F1[y](f) else: - f0 = F1[y](f.subs({x: X0})) + f0 = F1[y](f.subs({x: F1(x0)})) y0sf = f0.roots(QQbar, multiplicities=False) y0s += list(y0sf) while True: @@ -894,8 +892,8 @@ def braid_in_segment(glist, x0, x1, precision=dict()): centralbraid = braid_from_piecewise(complexstrands) initialstrands = [] finalstrands = [] - initialintervals = roots_interval_cached(g, X0) - finalintervals = roots_interval_cached(g, 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] From 8241fe072b97a6828481ddcce6734693a6a6bdba Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 3 Oct 2023 18:47:14 +0200 Subject: [PATCH 28/95] more methods for affine plane curves --- src/sage/schemes/curves/affine_curve.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 4834aec54c2..b6d0fb90b29 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1739,6 +1739,20 @@ def tangent_line(self, p): return Curve(I0, A) + def has_vertical_asymptote(self): + A = self.ambient_space() + R = A.coordinate_ring() + x, y = R.gens() + f = self.defining_polynomial().radical() + return f.degree(y) < f.degree() > 1 + + def is_vertical_line(self): + A = self.ambient_space() + R = A.coordinate_ring() + x, y = R.gens() + f = self.defining_polynomial().radical() + return f.degree(y) == 0 and f.degree() == 1 + class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): """ From 6b69c2f10d3d85822478d7f050243f290190a92c Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 4 Oct 2023 00:08:21 +0200 Subject: [PATCH 29/95] to do: doctests for fundamental_group_arrangement --- src/sage/schemes/curves/zariski_vankampen.py | 47 +++++++++----------- 1 file changed, 20 insertions(+), 27 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 3eb0c813158..de1cd710d8a 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -67,6 +67,7 @@ from sage.rings.rational_field import QQ from sage.rings.real_mpfr import RealField # from sage.sets.set import Set +from .constructor import Curve roots_interval_cache = dict() @@ -1158,7 +1159,7 @@ def braid_monodromy(f, arrangement=(), vertical=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 + - ``arrangement`` -- tuple (default: ``[]``). An optional tuple of polynomials whose product equals ``f``. - `vertical` .. boolean (default: ``False`). If set to ``True``, ``arrangements`` @@ -1169,25 +1170,22 @@ def braid_monodromy(f, arrangement=(), vertical=False): OUTPUT: - A list of braids and a dictionary. + A list of braids, two dictionaries, and the number + of strands of the braids (specially relevant if the list + pf braids is empty). The braids correspond to paths based in the same point; each of these paths is the conjugated of a loop around one of the points in the discriminant of the projection of ``f``. The dictionary assigns each strand to the index of the corresponding factor in ``arrangement``. If ``vertical`` is set to ``True`` only the vertical lines are not used - and the list of their indices are is the second element of the output. + and the list of their indices are the second element of the output .. 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 except if the only vertical asymptotes are lines - and ``vertical=True``.. - - .. TODO:: - - Create a class ``arrangements_of_curves`` with a ``braid_monodromy`` - method; it can be also a method for affine line arrangements. + and ``vertical=True``. EXAMPLES:: @@ -1210,6 +1208,9 @@ def braid_monodromy(f, arrangement=(), vertical=False): ([], {}, {}, 0) sage: braid_monodromy(x*y^2 - 1) ([s0*s1*s0^-1*s1*s0*s1^-1*s0^-1, s0*s1*s0^-1, s0], {0: 0, 1: 0, 2: 0}, {}, 3) + sage: L = [x, y, x - 1, x -y] + sage: braid_monodromy(prod(L), arrangement=L, vertical=True) + ([s^2, 1], {0: 1, 1: 3}, {0: 0, 1: 2}, 2) """ global roots_interval_cache F = fieldI(f.base_ring()) @@ -1220,14 +1221,10 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: arrangement1 = tuple(_.change_ring(F) for _ in arrangement) x, y = f.parent().gens() - dic_vertical = {j: False for j, f0 in enumerate(arrangement1)} - if vertical: - for j, f0 in enumerate(arrangement1): - if f0.degree(y) < f0.degree() and f0.degree() > 1: - dic_vertical = {j1: False for j1, f1 in enumerate(arrangement1)} - break - else: - dic_vertical[j] = f0.degree(y) < f0.degree(x) and f0.degree(x) == 1 + if not vertical or any([Curve(g).has_vertical_asymptote() for g in arrangement]): + dic_vertical = {j: False for j, f0 in enumerate(arrangement1)} + elif vertical: + dic_vertical = {j: Curve(f0).is_vertical_line() for j, f0 in enumerate(arrangement1)} arrangement_h = () indices_h = () arrangement_v = () @@ -1258,7 +1255,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): pt = [j for j, t in enumerate(disc) if f0(x=t) == 0] if pt: vertical_braid[f0] = (pt[0], arrangement1.index(f0)) - vl.append(j) + vl.append(pt[0]) else: transversal[f0] = arrangement1.index(f0) vl.sort() @@ -1726,6 +1723,10 @@ 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``. + - ``vertical`` -- boolean (default: ``False``); if set to ``True``, + whenever no curve has vertical asymptotes the computation of braid + monodromy is simpler if some lines are vertical. + OUTPUT: - A list of braids. The braids correspond to paths based in the same point; @@ -1770,13 +1771,6 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, {0: [x0, x1]}) sage: fundamental_group_arrangement([y + x^2], projective=True) (Finitely presented group < x | x^2 >, {0: [x0, x0]}) - - .. TODO:: - - Create a class ``arrangements_of_curves`` with a ``fundamental_group`` - method it can be also a method for affine or projective line - arrangements, even for hyperplane arrangements defined over a number - subfield of ``QQbar`` after applying a generic line section. """ if len(flist) > 0: f = prod(flist) @@ -1796,8 +1790,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis if not vertical0: infinity = f.degree(y) == f.degree() if vertical0: - flist_a = [g for g in flist1 if g.degree(y) < g.degree() > 1] - infinity = not flist_a + infinity = all([not Curve(g).has_vertical_asymptote() for g in flist1]) if len(flist1) == 0: bm = [] dic = dict() From e5a44d0fb36008daafb4f41a26d9f0e1c9580a32 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 4 Oct 2023 09:49:19 +0200 Subject: [PATCH 30/95] first pass of doctest in zvk, improving pca --- src/sage/schemes/curves/affine_curve.py | 39 +++- .../schemes/curves/plane_curve_arrangement.py | 188 ++++++------------ src/sage/schemes/curves/zariski_vankampen.py | 38 +++- 3 files changed, 121 insertions(+), 144 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index b6d0fb90b29..9262a9b5e38 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1719,7 +1719,6 @@ def tangent_line(self, p): defined by: -2*y + z + 1, x + y + z sage: _ == C.tangent_line(p) True - """ A = self.ambient_space() R = A.coordinate_ring() @@ -1739,7 +1738,25 @@ def tangent_line(self, p): return Curve(I0, A) + +class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): + """ + Affine plane curves over fields. + """ + _point = AffinePlaneCurvePoint_field + def has_vertical_asymptote(self): + """ + Check if the curve is not a line and has vertical asymptotes. + + EXAMPLES:: + + sage: A2. = AffineSpace(2, QQ) + sage: Curve(x).has_vertical_asymptote() + False + sage: Curve(y^2 * x + x + y).has_vertical_asymptote() + True + """ A = self.ambient_space() R = A.coordinate_ring() x, y = R.gens() @@ -1747,19 +1764,25 @@ def has_vertical_asymptote(self): return f.degree(y) < f.degree() > 1 def is_vertical_line(self): + """ + Check if the curve is a vertical line. + + EXAMPLES:: + + sage: A2. = AffineSpace(2, QQ) + sage: Curve(x - 1).is_vertical_line() + True + sage: Curve(x - y).is_vertical_line() + False + sage: Curve(y^2 * x + x + y).has_vertical_asymptote() + False + """ A = self.ambient_space() R = A.coordinate_ring() x, y = R.gens() f = self.defining_polynomial().radical() return f.degree(y) == 0 and f.degree() == 1 - -class AffinePlaneCurve_field(AffinePlaneCurve, AffineCurve_field): - """ - Affine plane curves over fields. - """ - _point = AffinePlaneCurvePoint_field - @cached_method def fundamental_group(self, simplified=True, puiseux=False): r""" diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index eaebfd911ed..c6f381ec055 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -3,41 +3,32 @@ Affine Plane Curve Arrangements We create an :class:`OrderedAffinePlaneCurveArrangements` -object -# to define the variables `x`, `y`, `z`:: -# -# sage: H. = HyperplaneArrangements(QQ) -# sage: h = 3*x + 2*y - 5*z - 7; h -# Hyperplane 3*x + 2*y - 5*z - 7 -# sage: h.normal() -# (3, 2, -5) -# sage: h.constant_term() -# -7 +object following the properties of :class:`HyperplaneArrangements` + + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: C = H(3*x + 2*y - x^2 + y^3 - 7); C + Arrangement (Affine Plane Curve over Rational Field defined by y^3 - x^2 + 3*x + 2*y - 7) The individual curves will be in :class:`AffinePlaneCurve`:: - # sage: -2*h - # Hyperplane -6*x - 4*y + 10*z + 14 - # sage: x, y, z - # (Hyperplane x + 0*y + 0*z + 0, - # Hyperplane 0*x + y + 0*z + 0, - # Hyperplane 0*x + 0*y + z + 0) + sage: C[0].parent() + The default base field is `\QQ`, the rational numbers. Number fields are also possible (also with fixed embeddings in ``QQbar``):: - # sage: # needs sage.rings.number_field - # sage: x = polygen(QQ, 'x') - # sage: NF. = NumberField(x**4 - 5*x**2 + 5, embedding=1.90) - # sage: H. = HyperplaneArrangements(NF) - # sage: A = H([[(-a**3 + 3*a, -a**2 + 4), 1], [(a**3 - 4*a, -1), 1], - # ....: [(0, 2*a**2 - 6), 1], [(-a**3 + 4*a, -1), 1], - # ....: [(a**3 - 3*a, -a**2 + 4), 1]]) - # sage: A - # Arrangement of 5 hyperplanes of dimension 2 and rank 2 - # sage: A.base_ring() - # Number Field in a with defining polynomial x^4 - 5*x^2 + 5 - # with a = 1.902113032590308? + sage: # needs sage.rings.number_field + sage: x = polygen(QQ, 'x') + sage: NF. = NumberField(x^4 - 5 * x^2 + 5, embedding=1.90) + sage: H. = OrderedAffinePlaneCurveArrangements(NF) + sage: A = H(y^2 - a * z, y^2 + a * z); A + Arrangement (Affine Plane Curve over Number Field in a with defining polynomial + x^4 - 5*x^2 + 5 with a = 1.902113032590308? defined by y^2 + (-a)*z, + Affine Plane Curve over Number Field in a with defining polynomial + x^4 - 5*x^2 + 5 with a = 1.902113032590308? defined by y^2 + a*z) + sage: A.base_ring() + Number Field in a with defining polynomial x^4 - 5*x^2 + 5 + with a = 1.902113032590308? AUTHORS: @@ -46,8 +37,7 @@ """ # ***************************************************************************** -# Copyright (C) 2013 David Perkinson -# Volker Braun +# Copyright (C) 2023 Enrique Artal # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -56,34 +46,13 @@ # http://www.gnu.org/licenses/ # ***************************************************************************** -# Possible extensions: - - -# from sage.categories.fields import Fields -# from sage.categories.homset import Hom, End, hom -# from sage.categories.number_fields import NumberFields from sage.categories.sets_cat import Sets from sage.combinat.combination import Combinations -# from sage.combinat.permutation import Permutation -# from sage.groups.free_group import FreeGroup -# from sage.interfaces.singular import singular -# from sage.matrix.constructor import matrix, vector from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod -# from sage.rings.infinity import infinity -# from sage.misc.lazy_attribute import lazy_attribute -# from sage.rings.number_field.number_field import NumberField -# from sage.rings.polynomial.multi_polynomial_element import degree_lowest_rational_function -# from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -# from sage.rings.qqbar import number_field_elements_from_algebraics from sage.rings.qqbar import QQbar -# from sage.rings.rational_field import QQ, is_RationalField -# from sage.rings.rational_field import is_RationalField from sage.rings.ring import _Fields from sage.schemes.affine.affine_space import AffineSpace -# from sage.schemes.affine.affine_space import is_AffineSpace -# from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine -# from sage.schemes.affine.affine_subscheme import AlgebraicScheme_subscheme_affine_field from sage.schemes.curves.affine_curve import AffinePlaneCurve from sage.schemes.curves.constructor import Curve from sage.schemes.curves.zariski_vankampen import braid_monodromy @@ -93,16 +62,6 @@ from sage.structure.richcmp import richcmp from sage.structure.unique_representation import UniqueRepresentation -# from .closed_point import IntegralAffineCurveClosedPoint -# from .curve import Curve_generic -# from .point import (AffineCurvePoint_field, -# AffinePlaneCurvePoint_field, -# AffinePlaneCurvePoint_finite_field, -# IntegralAffineCurvePoint, -# IntegralAffineCurvePoint_finite_field, -# IntegralAffinePlaneCurvePoint, -# IntegralAffinePlaneCurvePoint_finite_field) - class OrderedAffinePlaneCurveArrangementsElement(Element): """ @@ -123,20 +82,15 @@ def __init__(self, parent, curves, check=True): - ``parent`` -- the parent :class:`HyperplaneArrangements` - - ``hyperplanes`` -- a tuple of hyperplanes - - - ``check`` -- boolean (optional; default ``True``); whether - to check input - - - ``backend`` -- string (optional; default: ``None``); the backend to - use for the related polyhedral objects + - ``curves`` -- a tuple of curves - # EXAMPLES:: - # - # sage: H. = HyperplaneArrangements(QQ) - # sage: elt = H(x, y); elt - # Arrangement - # sage: TestSuite(elt).run() + EXAMPLES:: + + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: elt = H(x, y); elt + Arrangement (Affine Plane Curve over Rational Field defined by x, + Affine Plane Curve over Rational Field defined by y) + sage: TestSuite(elt).run() """ super().__init__(parent) self._curves = curves @@ -148,22 +102,6 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - def _first_ngens(self, n): - """ - Workaround to support the construction with names. - - INPUT/OUTPUT: - - See :meth:`OrderedAffinePlaneCurveArrangements._first_ngens`. - - EXAMPLES:: - - sage: a. = hyperplane_arrangements.braid(3) # indirect doctest # needs sage.graphs - sage: (x, y) == a._first_ngens(2) # needs sage.graphs - True - """ - return self.parent()._first_ngens(n) - def __getitem__(self, i): """ Return the `i`-th curve. @@ -178,13 +116,9 @@ def __getitem__(self, i): EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: h = x|y; h - # Arrangement - # sage: h[0] - # Hyperplane 0*x + y + 0 - # sage: h[1] - # Hyperplane x + 0*y + 0 + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: h = H(y^2 - x, y^3 + 2 * x^2, x^4 + y^4 + 1); h + Affine Plane Curve over Rational Field defined by y^2 - x """ return self._curves[i] @@ -192,9 +126,8 @@ def __hash__(self): r""" TESTS:: - sage: H. = HyperplaneArrangements(QQ) - sage: h = x|y; h - Arrangement + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: h = H((x * y, x + y +1)) sage: len_dict = {h: len(h)} """ return hash(self.curves()) @@ -209,12 +142,12 @@ def n_curves(self): EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: A = H([1,1,0], [2,3,-1], [4,5,3]) - # sage: A.n_hyperplanes() - # 3 - # sage: len(A) # equivalent - # 3 + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: h = H((x * y, x + y +1)) + sage: h.n_curves() + 2 + sage: len(h) # equivalent + 2 """ return len(self._curves) @@ -226,19 +159,20 @@ def curves(self): OUTPUT: - An tuple. + A tuple. EXAMPLES:: - sage: H. = HyperplaneArrangements(QQ) - sage: A = H([1,1,0], [2,3,-1], [4,5,3]) - sage: A.hyperplanes() - (Hyperplane x + 0*y + 1, Hyperplane 3*x - y + 2, Hyperplane 5*x + 3*y + 4) + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: h = H((x * y, x + y + 1)) + sage: h.curves() + (Affine Plane Curve over Rational Field defined by x*y, + Affine Plane Curve over Rational Field defined by x + y + 1) Note that the hyperplanes can be indexed as if they were a list:: - sage: A[0] - Hyperplane x + 0*y + 1 + sage: h[1] + Affine Plane Curve over Rational Field defined by x + y + 1 """ return self._curves @@ -252,13 +186,11 @@ def _repr_(self): EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: H(x, y, x-1, y-1) - # Arrangement - # sage: x | y | x - 1 | y - 1 | x + y | x - y - # Arrangement of 6 hyperplanes of dimension 2 and rank 2 - # sage: H() - # Empty hyperplane arrangement of dimension 2 + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) + Arrangement of 5 curves + sage: H(()) + Empty curve arrangement """ if len(self) == 0: return 'Empty curve arrangement' @@ -269,18 +201,20 @@ def _repr_(self): def _richcmp_(self, other, op): """ - Compare two hyperplane arrangements. + Compare two curve arrangements. EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: H(x) == H(y) - # False + sage: H(x) == H(y) + False + sage: H(x) == H(2 * x) + True + TESTS:: - # sage: H(x) == 0 - # False + sage: H(x) == 0 + False """ return richcmp(self._curves, other._curves, op) @@ -290,7 +224,7 @@ def union(self, other): INPUT: - - ``other`` -- a curvee arrangement or something that can + - ``other`` -- a curve arrangement or something that can be converted into a curve arrangement OUTPUT: diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index de1cd710d8a..f2203bb103b 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1145,7 +1145,7 @@ def strand_components(f, flist, p1): rt = h1.roots(QQbar, multiplicities=False) roots_base += [(_, i) for _ in rt] roots_base.sort() - strands = {i: par[1] for i, par in enumerate(roots_base)} # quitar +1 despues de revision + strands = {i: par[1] for i, par in enumerate(roots_base)} return (roots_base, strands) @@ -1486,7 +1486,8 @@ def relation(x, b): return x * b / x -def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projective=False, puiseux=False, vertical=[]): +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. @@ -1525,6 +1526,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv EXAMPLES:: + sage: # optional - sirocco 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, @@ -1539,9 +1541,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 > - sage: fundamental_group_from_braid_mon([]) is None # optional - sirocco + sage: fundamental_group_from_braid_mon([]) is None True - sage: fundamental_group_from_braid_mon([], degree=2) # optional - sirocco + sage: fundamental_group_from_braid_mon([], degree=2) Finitely presented group < x0, x1 | > """ vertical0 = sorted(vertical) @@ -1771,6 +1773,24 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, {0: [x0, x1]}) sage: fundamental_group_arrangement([y + x^2], projective=True) (Finitely presented group < x | x^2 >, {0: [x0, x0]}) + sage: L = [x, y, x - 1, x -y] + sage: fundamental_group_arrangement(L) + (Finitely presented group < x0, x1, x2, x3 | + x2*x3^-1*x2^-1*x3, + x0*x1*x3*x0^-1*x3^-1*x1^-1, + x3*x0*x1*x3^-1*x1^-1*x0^-1, + x1*x2*x1^-1*x0*x1*x2^-1*x1^-1*x0^-1 >, + {0: [x1], 1: [x3], 2: [x2], 3: [x0], + 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) + sage: fundamental_group_arrangement(L, vertical=True) + (Finitely presented group < x0, x1, x2, x3 | + x1*x0*x1^-1*x0^-1, + x2*x0*x2^-1*x0^-1, + x2*x1*x2^-1*x1^-1, + x3*x0*x3^-1*x0^-1, + x3*x1*x3^-1*x1^-1 >, + {0: [x2], 1: [x0], 2: [x3], 3: [x1], + 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) """ if len(flist) > 0: f = prod(flist) @@ -1788,7 +1808,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis flist1 = [g.subs({x: x + y}) for g in flist1] f = prod(flist1) if not vertical0: - infinity = f.degree(y) == f.degree() + infinity = all([g.degree(y) == g.degree() or Curve(g).is_vertical_line() for g in flist1]) if vertical0: infinity = all([not Curve(g).has_vertical_asymptote() for g in flist1]) if len(flist1) == 0: @@ -1798,10 +1818,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis d1 = 0 else: bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical0) - vert_lines = [] + vert_lines = list(dv.values()) + vert_lines.sort() if vertical0: - for j in dv.keys(): - dic[d + j] = dv[j] + for j in dv: + dic[d1 + j] = dv[j] g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False, projective=projective, puiseux=puiseux, vertical=vert_lines) if simplified: hom = g.simplification_isomorphism() @@ -1814,7 +1835,6 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis for i in range(len(flist1)): L = [j1 for j1 in dic.keys() if dic[j1] == i] dic1[i] = [hom(g.gen(j)) for j in L] - # if not projective and f.degree(y) == f.degree(): if not projective and infinity: t = prod(hom(x) for x in g.gens()).inverse() dic1[len(flist1)] = [t] From 88577a7354b0a1620bbe07641a3c1f111e13d86c Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 4 Oct 2023 22:24:06 +0200 Subject: [PATCH 31/95] more doctests --- .../schemes/curves/plane_curve_arrangement.py | 357 +++++++----------- 1 file changed, 137 insertions(+), 220 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index c6f381ec055..28a61a255df 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -85,7 +85,7 @@ def __init__(self, parent, curves, check=True): - ``curves`` -- a tuple of curves EXAMPLES:: - + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) sage: elt = H(x, y); elt Arrangement (Affine Plane Curve over Rational Field defined by x, @@ -118,7 +118,9 @@ def __getitem__(self, i): sage: H. = OrderedAffinePlaneCurveArrangements(QQ) sage: h = H(y^2 - x, y^3 + 2 * x^2, x^4 + y^4 + 1); h - Affine Plane Curve over Rational Field defined by y^2 - x + Arrangement (Affine Plane Curve over Rational Field defined by y^2 - x, + Affine Plane Curve over Rational Field defined by y^3 + 2*x^2, + Affine Plane Curve over Rational Field defined by x^4 + y^4 + 1) """ return self._curves[i] @@ -188,6 +190,7 @@ def _repr_(self): sage: H. = OrderedAffinePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) + sage: h Arrangement of 5 curves sage: H(()) Empty curve arrangement @@ -205,16 +208,16 @@ def _richcmp_(self, other, op): EXAMPLES:: + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) sage: H(x) == H(y) False sage: H(x) == H(2 * x) True - TESTS:: - sage: H(x) == 0 - False + sage: H(x) == 0 + False """ return richcmp(self._curves, other._curves, op) @@ -233,33 +236,14 @@ def union(self, other): EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: H1. = OrderedHyperplaneArrangements(QQ) - # sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0]) - # sage: B = H([1,1,1], [1,-1,1], [1,0,-1]) - # sage: C = A.union(B); C - # Arrangement of 8 hyperplanes of dimension 2 and rank 2 - # sage: C == A | B # syntactic sugar - # True - # sage: A1 = H1(A) - # sage: B1 = H1(B) - # sage: C1 = A1.union(B1); C1 - # Arrangement of 8 hyperplanes of dimension 2 and rank 2 - # sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()] - # [0, 5, 6, 1, 2, 3, 7, 4] - # - # A single hyperplane is coerced into a hyperplane arrangement - # if necessary:: - # - # sage: A.union(x+y-1) - # Arrangement of 6 hyperplanes of dimension 2 and rank 2 - # sage: A.add_hyperplane(x+y-1) # alias - # Arrangement of 6 hyperplanes of dimension 2 and rank 2 - # - # sage: P. = HyperplaneArrangements(RR) - # sage: C = P(2*x + 4*y + 5) - # sage: C.union(A) - # Arrangement of 6 hyperplanes of dimension 2 and rank 2 + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) + sage: C = Curve(x^8 - y^8 -x^4 * y^4) + sage: h1 = h.union(C); h1 + Arrangement of 6 curves + sage: h1 == h1.union(C) + Repeated curve + True """ P = self.parent() other_h = P(other) @@ -277,23 +261,6 @@ def union(self, other): __or__ = union - def plot(self, **kwds): - """ - Plot an arrangement of curves. - - OUTPUT: - - A graphics object. - - EXAMPLES:: - - # sage: L. = HyperplaneArrangements(QQ) - # sage: L(x, y, x+y-2).plot() # needs sage.plot - # Graphics object consisting of 3 graphics primitives - """ - from sage.geometry.hyperplane_arrangement.plot import plot - return plot(self, **kwds) - def deletion(self, curves): r""" Return the curve arrangement obtained by removing ``h``. @@ -309,15 +276,15 @@ def deletion(self, curves): EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: A = H([0,1,0], [1,0,1], [-1,0,1], [0,1,-1], [0,1,1]); A - # Arrangement of 5 hyperplanes of dimension 2 and rank 2 - # sage: A.deletion(x) - # Arrangement - # sage: h = H([0,1,0], [0,1,1]) - # sage: A.deletion(h) - # Arrangement - """ + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) + sage: C = h[-1] + sage: h.deletion(C) + Arrangement (Affine Plane Curve over Rational Field defined by x*y, + Affine Plane Curve over Rational Field defined by x + y + 1, + Affine Plane Curve over Rational Field defined by -y^5 + x^3, + Affine Plane Curve over Rational Field defined by x^5 + y^5 + x^2*y^2) + """ parent = self.parent() curves = parent(curves) planes = list(self) @@ -326,7 +293,7 @@ def deletion(self, curves): planes.remove(curve) except ValueError: raise ValueError('curve is not in the arrangement') - return parent(*planes) + return parent(planes) def change_ring(self, base_ring): """ @@ -344,57 +311,42 @@ def change_ring(self, base_ring): EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: A = H([(1,1), 0], [(2,3), -1]) - # sage: A.change_ring(FiniteField(2)) - # Arrangement + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) + sage: K. = CyclotomicField(3) # optional - sage.rings.number_field + sage: A.change_ring(K) # optional - sage.rings.number_field + Arrangement (Affine Plane Curve over Cyclotomic Field of order 3 and degree 2 defined by -x^3 + y^2, + Affine Plane Curve over Cyclotomic Field of order 3 and degree 2 defined by x, + Affine Plane Curve over Cyclotomic Field of order 3 and degree 2 defined by y, + Affine Plane Curve over Cyclotomic Field of order 3 and degree 2 defined by x^2 + x*y + y^2) """ parent = self.parent().change_ring(base_ring) - return parent(self) + curves = tuple(c.change_ring(base_ring) for c in self) + return parent(curves) def defining_polynomials(self): r""" - Return the defining polynomials of ``A``. - - # Let `A = (H_i)_i` be a hyperplane arrangement in a vector space `V` - # corresponding to the null spaces of `\alpha_{H_i} \in V^*`. Then - # the *defining polynomial* of `A` is given by - # - # .. MATH:: - # - # Q(A) = \prod_i \alpha_{H_i} \in S(V^*). + Return the defining polynomials of the elements of``self``. EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: A = H([2*x + y - z, -x - 2*y + z]) - # sage: p = A.defining_polynomial(); p - # -2*x^2 - 5*x*y - 2*y^2 + 3*x*z + 3*y*z - z^2 - # sage: p.factor() - # (-1) * (x + 2*y - z) * (2*x + y - z) + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) + sage: A.defining_polynomials() + (-x^3 + y^2, x, y, x^2 + x*y + y^2) """ return tuple(h.defining_polynomial() for h in self) def defining_polynomial(self, simplified=True): r""" - Return the defining polynomial of ``A``. - - # Let `A = (H_i)_i` be a hyperplane arrangement in a vector space `V` - # corresponding to the null spaces of `\alpha_{H_i} \in V^*`. Then - # the *defining polynomial* of `A` is given by - # - # .. MATH:: - # - # Q(A) = \prod_i \alpha_{H_i} \in S(V^*). + Return the defining polynomial of the union of the curves in ``self``. EXAMPLES:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: A = H([2*x + y - z, -x - 2*y + z]) - # sage: p = A.defining_polynomial(); p - # -2*x^2 - 5*x*y - 2*y^2 + 3*x*z + 3*y*z - z^2 - # sage: p.factor() - # (-1) * (x + 2*y - z) * (2*x + y - z) + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y ** 2 + x ** 2, x, y) + sage: prod(A.defining_polynomials()) == A.defining_polynomial() + True """ return prod(self.defining_polynomials()) @@ -494,13 +446,12 @@ class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): EXAMPLES:: - sage: H. = HyperplaneArrangements(QQ) - sage: x - Hyperplane x + 0*y + 0 - sage: x + y - Hyperplane x + y + 0 - sage: H(x, y, x-1, y-1) - Arrangement + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H(x, y^2, x-1, y-1) + Arrangement (Affine Plane Curve over Rational Field defined by x, + Affine Plane Curve over Rational Field defined by y^2, + Affine Plane Curve over Rational Field defined by x - 1, + Affine Plane Curve over Rational Field defined by y - 1) """ Element = OrderedAffinePlaneCurveArrangementsElement @@ -510,21 +461,17 @@ def __init__(self, base_ring, names=tuple()): TESTS:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: K = HyperplaneArrangements(QQ, names=('x', 'y')) - # sage: H is K - # True - # sage: type(K) - # - # sage: K.change_ring(RR).gen(0) - # Hyperplane 1.00000000000000*x + 0.000000000000000*y + 0.000000000000000 + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: K = OrderedAffinePlaneCurveArrangements(QQ, names=('x', 'y')) + sage: H is K + True + sage: type(K) + TESTS:: - # sage: H. = HyperplaneArrangements(QQ) - # sage: TestSuite(H).run() - # sage: K = HyperplaneArrangements(QQ) - # sage: TestSuite(K).run() + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: TestSuite(H).run() """ if base_ring not in _Fields: raise ValueError('base ring must be a field') @@ -542,9 +489,9 @@ def base_ring(self): EXAMPLES:: - # sage: L. = HyperplaneArrangements(QQ) - # sage: L.base_ring() - # Rational Field + sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L.base_ring() + Rational Field """ return self._base_ring @@ -562,17 +509,17 @@ def change_ring(self, base_ring): base ring. EXAMPLES:: - # - # sage: L. = HyperplaneArrangements(QQ) - # sage: L.gen(0) - # Hyperplane x + 0*y + 0 - # sage: L.change_ring(RR).gen(0) - # Hyperplane 1.00000000000000*x + 0.000000000000000*y + 0.000000000000000 + + sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L.gen(0) + x + sage: L.change_ring(RR).base_ring() + Real Field with 53 bits of precision TESTS:: - # sage: L.change_ring(QQ) is L - # True + sage: L.change_ring(QQ) is L + True """ return OrderedAffinePlaneCurveArrangements(base_ring, names=self._names) @@ -581,17 +528,11 @@ def ambient_space(self): """ Return the ambient space. - # The ambient space is the parent of hyperplanes. That is, new - # hyperplanes are always constructed internally from the ambient - # space instance. - EXAMPLES:: - sage: L. = HyperplaneArrangements(QQ) - sage: L.ambient_space()([(1,0), 0]) - Hyperplane x + 0*y + 0 - sage: L.ambient_space()([(1,0), 0]) == x - True + sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Affine Space of dimension 2 over Rational Field """ return AffineSpace(self.base_ring(), 2, self._names) @@ -605,8 +546,8 @@ def _repr_(self): EXAMPLES:: - # sage: L. = HyperplaneArrangements(QQ); L - # Hyperplane arrangements in 2-dimensional linear space over Rational Field with coordinates x, y + sage: L. = OrderedAffinePlaneCurveArrangements(QQ); L + Curve arrangements in Affine Space of dimension 2 over Rational Field """ return 'Curve arrangements in {0}'.format(self.ambient_space()) @@ -616,51 +557,20 @@ def _element_constructor_(self, *args, **kwds): INPUT: - - ``*args`` -- positional arguments, each defining a - hyperplane; alternatively, a single polytope or a single - hyperplane arrangement - - - ``warn_duplicates`` -- boolean (optional, default: ``False``); - whether to issue a warning if duplicate hyperplanes were - passed -- note that duplicate hyperplanes are always removed, - whether or not there is a warning shown - + - ``*args`` -- positional arguments, each defining a curve EXAMPLES:: - # sage: L. = HyperplaneArrangements(QQ) - # sage: L._element_constructor_(x, y) - # Arrangement - # sage: L._element_constructor_([x, y]) - # Arrangement - # sage: L._element_constructor_([0, 1, 0], [0, 0, 1]) - # Arrangement - # sage: L._element_constructor_([[0, 1, 0], [0, 0, 1]]) - # Arrangement - # - # sage: L._element_constructor_(polytopes.hypercube(2)) - # Arrangement <-x + 1 | -y + 1 | y + 1 | x + 1> - # - # sage: L(x, x, warn_duplicates=True) - # doctest:...: UserWarning: Input contained 2 hyperplanes, but only 1 are distinct. - # Arrangement - # sage: L(-x, x + y - 1, signed=False) - # Arrangement <-x - y + 1 | x> - # - # TESTS:: - # - # sage: L() - # Empty hyperplane arrangement of dimension 2 - # sage: L(0) # zero is equivalent to no argument, Issue #8648 - # Empty hyperplane arrangement of dimension 2 - # sage: L(0*x) # degenerate hyperplane is NOT allowed - # Traceback (most recent call last): - # ... - # ValueError: linear expression must be non-constant to define a hyperplane - # sage: L(0*x, y) # ditto - # Traceback (most recent call last): - # ... - # ValueError: linear expression must be non-constant to define a hyperplane + sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = L._element_constructor_(x, y); A + Arrangement (Affine Plane Curve over Rational Field defined by x, + Affine Plane Curve over Rational Field defined by y) + sage: L._element_constructor_([x, y]) == A + True + sage: L._element_constructor_(Curve(x), Curve(y)) == A + True + sage: L._element_constructor_(y, x) == A + False """ if len(args) == 1 and not (isinstance(args[0], (tuple, list))): arg = (args[0], ) @@ -690,22 +600,33 @@ def _element_constructor_(self, *args, **kwds): return None return self.element_class(self, curves) + def _an_element_(self): + """ + Dirty trick to avoid test run failure. + + TEST:: + + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H._an_element_() + Arrangement (Affine Plane Curve over Rational Field defined by t) + """ + x = self.gen(0) + return self(x) + @cached_method def ngens(self): """ - Return the number of linear variables. + Return the number of variables, i.e. 2, kept for completness. OUTPUT: - An integer. + An integer (2). EXAMPLES:: - sage: L. = HyperplaneArrangements(QQ); L - Hyperplane arrangements in 3-dimensional linear space - over Rational Field with coordinates x, y, z + sage: L. = OrderedAffinePlaneCurveArrangements(QQ) sage: L.ngens() - 3 + 2 """ return len(self._names) @@ -720,11 +641,9 @@ def gens(self): EXAMPLES:: - # sage: L = HyperplaneArrangements(QQ, ('x', 'y', 'z')) - # sage: L.gens() - # (Hyperplane x + 0*y + 0*z + 0, - # Hyperplane 0*x + y + 0*z + 0, - # Hyperplane 0*x + 0*y + z + 0) + sage: L = OrderedAffinePlaneCurveArrangements(QQ, ('x', 'y')) + sage: L.gens() + (x, y) """ return self.ambient_space().gens() @@ -738,41 +657,39 @@ def gen(self, i): OUTPUT: - A linear expression. + A variable. EXAMPLES:: - # sage: L. = HyperplaneArrangements(QQ); L - # 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 + sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L.gen(1) + y """ return self.gens()[i] - def _coerce_map_from_(self, P): - """ - Return whether there is a coercion. - - TESTS:: - - sage: L. = HyperplaneArrangements(QQ); L - Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x - sage: M. = HyperplaneArrangements(RR); M - Hyperplane arrangements in 1-dimensional linear space over Real Field with 53 bits of precision with coordinate y - - sage: L.coerce_map_from(ZZ) - Coercion map: - From: Integer Ring - To: Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x - sage: M.coerce_map_from(L) - Coercion map: - From: Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x - To: Hyperplane arrangements in 1-dimensional linear space over Real Field with 53 bits of precision with coordinate y - sage: L.coerce_map_from(M) - """ - if self.ambient_space().has_coerce_map_from(P): - return True - if isinstance(P, OrderedAffinePlaneCurveArrangements): - return self.base_ring().has_coerce_map_from(P.base_ring()) - return super()._coerce_map_from_(P) + # def _coerce_map_from_(self, P): + # """ + # Return whether there is a coercion. + # + # TESTS:: + # + # sage: L. = OrderedAffinePlaneCurveArrangements(QQ); L + # Curve arrangements in Affine Space of dimension 2 over Rational Field + # sage: M. = HyperplaneArrangements(RR); M + # Hyperplane arrangements in 2-dimensional linear space over Real Field with 53 bits of precision with coordinates x, y + # + # sage: L.coerce_map_from(ZZ) + # Coercion map: + # From: Integer Ring + # To: Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x + # sage: M.coerce_map_from(L) + # Coercion map: + # From: Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x + # To: Hyperplane arrangements in 1-dimensional linear space over Real Field with 53 bits of precision with coordinate y + # sage: L.coerce_map_from(M) + # """ + # if self.ambient_space().has_coerce_map_from(P): + # return True + # if isinstance(P, OrderedAffinePlaneCurveArrangements): + # return self.base_ring().has_coerce_map_from(P.base_ring()) + # return super()._coerce_map_from_(P) From bb0f7c2be69ebb12d6787537eb6ebe2936a91f6a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 5 Oct 2023 15:07:49 +0200 Subject: [PATCH 32/95] arrangements in documentation --- src/doc/en/reference/curves/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/curves/index.rst b/src/doc/en/reference/curves/index.rst index 2bc0d098cba..281ff2fcfaf 100644 --- a/src/doc/en/reference/curves/index.rst +++ b/src/doc/en/reference/curves/index.rst @@ -7,6 +7,7 @@ Curves sage/schemes/curves/constructor sage/schemes/curves/curve sage/schemes/curves/affine_curve + sage/schemes/curves/plane_curve_arrangement sage/schemes/curves/projective_curve sage/schemes/curves/point sage/schemes/curves/closed_point From ce641a79fd37944c38eb5f27bf420146f8e10f8b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 5 Oct 2023 22:23:00 +0200 Subject: [PATCH 33/95] last doctests --- .../schemes/curves/plane_curve_arrangement.py | 139 +++++++++++------- src/sage/schemes/curves/zariski_vankampen.py | 40 ++--- 2 files changed, 107 insertions(+), 72 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 28a61a255df..3e5162e6da1 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -208,16 +208,16 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) - sage: H(x) == H(y) - False - sage: H(x) == H(2 * x) - True + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H(x) == H(y) + False + sage: H(x) == H(2 * x) + True TESTS:: - sage: H(x) == 0 - False + sage: H(x) == 0 + False """ return richcmp(self._curves, other._curves, op) @@ -351,6 +351,16 @@ def defining_polynomial(self, simplified=True): return prod(self.defining_polynomials()) def have_common_factors(self): + r""" + Check if th curves have common factors. + + EXAMPLES:: + + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(x * y, x^2 + x* y^3) + sage: A.have_common_factors() + True + """ L = [c.defining_polynomial() for c in self] C = Combinations(L, 2) for f1, f2 in C: @@ -359,14 +369,31 @@ def have_common_factors(self): return False def reduce(self): + r""" + Replace the curves by their reduction. + + EXAMPLES:: + + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2)) + sage: A.reduce() + Arrangement (Affine Plane Curve over Rational Field defined by y, + Affine Plane Curve over Rational Field defined by x^3 + 2*x^2*y + 2*x*y^2 + y^3) + """ P = self.parent() L = [c.defining_polynomial().radical() for c in self] return P(*L) - def fundamental_group(self, vertical=True): + def fundamental_group(self, vertical=False): r""" It computes the fundamental group of the complement of the union - of affine projective curves in `\mathbb{C}^2`. + of affine plane curves in `\mathbb{C}^2`. + + INPUT: + + - ``vertical`` -- boolean (default: False). If it is ``True``, there + are no vertical asymptotes, and there are vertical lines, then a + simplified braid braid_monodromy is used. OUTPUT: @@ -375,47 +402,19 @@ def fundamental_group(self, vertical=True): EXAMPLES:: - # sage: A. = OrderedHyperplaneArrangements(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 x + 0*y + 0, - # Hyperplane 0*x + y + 0, - # Hyperplane x + 0*y + 1, - # Hyperplane 0*x + y + 1, - # Hyperplane x - y + 0] - # 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: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 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: [x0], 1: [x2], 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: A3. = OrderedHyperplaneArrangements(QQ) - # sage: H = A3(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} + sage: # needs sirocco + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x, y + x - 1, x) + sage: A.fundamental_group() + (Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, + x1*x0*x1^-1*x0^-1, + (x0*x2)^2*(x0^-1*x2^-1)^2 >, + {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], + 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]}) + sage: A.fundamental_group(vertical=True) + (Finitely presented group < x0, x1, x2 | x1*x0^-1*x1^-1*x0, + x2*x1*x2^-1*x1^-1, x2*x0*x2^-1*x0^-1 >, + {0: [x1], 1: [x0], 2: [x2], 3: [x2^-1*x1^-2*x0^-1]}) .. WARNING:: @@ -430,8 +429,44 @@ def fundamental_group(self, vertical=True): return (G, dic) def braid_monodromy(self, vertical=False): + r""" + It computes the braid monodromy of the complement of the union + of affine plane curves in `\mathbb{C}^2`. If there are vertical + asymptotes a change of variable is done. + + INPUT: + + - ``vertical`` -- boolean (default: False). If it is ``True``, there + are no vertical asymptotes, and there are vertical lines, then a + simplified braid braid_monodromy is computed. + + OUTPUT: + + A braid monodromy with dictionnaries identifying strans with components + and braids with vertical lines.. + + EXAMPLES:: + + sage: # needs sirocco + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x, y + x - 1, x) + sage: A.braid_monodromy() + ([s1*s0*(s1*s2*s1)^2*s2*(s1^-1*s2^-1)^2*s1^-1*s0^-1*s1^-1, + s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s1*s2*(s1*s2^-1)^2*s0*s1*s2*s1*s0*s2^-1*s1^-3*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s1*s2*s1*s2^-1*s1^4*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s1*s2*s1*s2^-1*s1^-1*s2*s0^-1*s1^-1], + {0: 2, 1: 1, 2: 0, 3: 0}, {}) + sage: A.braid_monodromy(vertical=True) + ([s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0], + {0: 1, 1: 0, 2: 0}, {1: 2}) + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ L = self.defining_polynomials() - return braid_monodromy(prod(L), arrangement=L, vertical=vertical) + return braid_monodromy(prod(L), arrangement=L, vertical=vertical)[:-1] class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): @@ -604,7 +639,7 @@ def _an_element_(self): """ Dirty trick to avoid test run failure. - TEST:: + TESTS:: sage: H. = OrderedAffinePlaneCurveArrangements(QQ) sage: H._an_element_() diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index f2203bb103b..45efea572bd 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1175,10 +1175,11 @@ def braid_monodromy(f, arrangement=(), vertical=False): pf braids is empty). The braids correspond to paths based in the same point; each of these paths is the conjugated of a loop around one of the points - in the discriminant of the projection of ``f``. The dictionary assigns each + in the discriminant of the projection of ``f``. The first dictionary assigns each strand to the index of the corresponding factor in ``arrangement``. - If ``vertical`` is set to ``True`` only the vertical lines are not used - and the list of their indices are the second element of the output + If ``vertical`` is set to ``True`` only the vertical lines are not used. + The second dictionnary assign to each vertical line the corresponding braid. + The fourth one is the number of strands of the braids. .. NOTE:: @@ -1328,7 +1329,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): for f0 in arrangement_v: if f0 in vertical_braid.keys(): k, j = vertical_braid[f0] - vertical_braids[j] = vd[k] + vertical_braids[vd[k]] = j else: vertical_braids[r + t] = transversal[f0] t += 1 @@ -1753,7 +1754,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2^-1*x0*x2*x0, x1^-1*x0^-1*x1*x0 > sage: dic - {0: [x0, x2, x0], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]} + {0: [x0, x2], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]} sage: g, dic = fundamental_group_arrangement(flist, simplified=False) sage: g.sorted_presentation(), dic (Finitely presented group @@ -1765,14 +1766,14 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis x1^-1*x0^-1*x1*x0 >, {0: [x0, x2, x3], 1: [x1], 2: [x3^-1*x2^-1*x1^-1*x0^-1]}) sage: fundamental_group_arrangement(flist, projective=True) - (Finitely presented group < x | >, {0: [x0, x0, x0], 1: [x0^-3]}) + (Finitely presented group < x | >, {0: [x], 1: [x^-3]}) sage: fundamental_group_arrangement([]) (Finitely presented group < | >, {}) sage: g, dic = fundamental_group_arrangement([x * y]) sage: g.sorted_presentation(), dic (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, {0: [x0, x1]}) sage: fundamental_group_arrangement([y + x^2], projective=True) - (Finitely presented group < x | x^2 >, {0: [x0, x0]}) + (Finitely presented group < x | x^2 >, {0: [x]}) sage: L = [x, y, x - 1, x -y] sage: fundamental_group_arrangement(L) (Finitely presented group < x0, x1, x2, x3 | @@ -1783,14 +1784,10 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis {0: [x1], 1: [x3], 2: [x2], 3: [x0], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) sage: fundamental_group_arrangement(L, vertical=True) - (Finitely presented group < x0, x1, x2, x3 | - x1*x0*x1^-1*x0^-1, - x2*x0*x2^-1*x0^-1, - x2*x1*x2^-1*x1^-1, - x3*x0*x3^-1*x0^-1, - x3*x1*x3^-1*x1^-1 >, - {0: [x2], 1: [x0], 2: [x3], 3: [x1], - 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) + (Finitely presented group < x0, x1, x2, x3 | x2*x0*x2^-1*x0^-1, x2*x1*x2^-1*x1^-1, + x1*x3*x0*x3^-1*x1^-1*x0^-1, + x1*x3*x0*x1^-1*x0^-1*x3^-1 >, + {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) """ if len(flist) > 0: f = prod(flist) @@ -1818,11 +1815,13 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis d1 = 0 else: bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical0) - vert_lines = list(dv.values()) + vert_lines = list(dv) vert_lines.sort() - if vertical0: - for j in dv: - dic[d1 + j] = dv[j] + for i, j in enumerate(vert_lines): + dic[d1 + i] = dv[j] + # if vertical0: + # for j in dv: + # dic[d1 + j] = dv[j] g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False, projective=projective, puiseux=puiseux, vertical=vert_lines) if simplified: hom = g.simplification_isomorphism() @@ -1833,7 +1832,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis return (g1, dict()) dic1 = {} for i in range(len(flist1)): - L = [j1 for j1 in dic.keys() if dic[j1] == i] + L = [j1 for j1 in dic if dic[j1] == i] dic1[i] = [hom(g.gen(j)) for j in L] if not projective and infinity: t = prod(hom(x) for x in g.gens()).inverse() @@ -1841,4 +1840,5 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis n = g1.ngens() rels = [_.Tietze() for _ in g1.relations()] g1 = FreeGroup(n) / rels + dic1 = {i: list(set([g1(el.Tietze()) for el in dic1[i]])) for i in dic1} return (g1, dic1) From 6bcdec45c9f0e5a1bb92493aa5c6066f4759b380 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 8 Oct 2023 10:27:56 -0600 Subject: [PATCH 34/95] forget a commit --- src/sage/schemes/curves/plane_curve_arrangement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 3e5162e6da1..9066812b72a 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -384,7 +384,7 @@ def reduce(self): L = [c.defining_polynomial().radical() for c in self] return P(*L) - def fundamental_group(self, vertical=False): + def fundamental_group(self, simplified=True, vertical=False): r""" It computes the fundamental group of the complement of the union of affine plane curves in `\mathbb{C}^2`. @@ -425,7 +425,7 @@ def fundamental_group(self, vertical=False): raise TypeError('the base field is not in QQbar') C = self.reduce() L = C.defining_polynomials() - G, dic = fundamental_group_arrangement(L, puiseux=True, vertical=vertical) + G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical) return (G, dic) def braid_monodromy(self, vertical=False): From b7e62707d1651d0e0e7e352c8dfef75e71af4398 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 17 Oct 2023 18:50:56 +0200 Subject: [PATCH 35/95] improve the class-1 --- src/sage/schemes/curves/plane_curve_arrangement.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 9066812b72a..34cb8e8e6cc 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -101,6 +101,13 @@ def __init__(self, parent, curves, check=True): raise ValueError("not all elements are curves") if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") + self._braid_monodromy = None + self._vertical_braid_monodromy = None + self._strands = dict() + self._vertical_strands = dict() + self._fundamental_group = None + self._meridians = dict() + self._infinity = None def __getitem__(self, i): """ @@ -465,6 +472,8 @@ def braid_monodromy(self, vertical=False): This functionality requires the sirocco package to be installed. """ + if self._braid_monodromy: + return self._braid_monodromy L = self.defining_polynomials() return braid_monodromy(prod(L), arrangement=L, vertical=vertical)[:-1] From d602f167b9c5a5c3406eeac325c2475bfdc5953f Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 17 Oct 2023 23:56:06 +0200 Subject: [PATCH 36/95] improving class (2)2 --- .../schemes/curves/plane_curve_arrangement.py | 37 +++++++++++++++++-- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 34cb8e8e6cc..e5657dba125 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -102,9 +102,10 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") self._braid_monodromy = None - self._vertical_braid_monodromy = None self._strands = dict() - self._vertical_strands = dict() + self._vertical_braid_monodromy = None + self._vertical_strands = None + self._vertical_asymptotes = dict() self._fundamental_group = None self._meridians = dict() self._infinity = None @@ -472,10 +473,38 @@ def braid_monodromy(self, vertical=False): This functionality requires the sirocco package to be installed. """ - if self._braid_monodromy: + if self._braid_monodromy and not vertical: return self._braid_monodromy + if self._vertical_braid_monodromy and self._vertical_asymptotes and vertical: + return self._vertical_braid_monodromy + K = self.base_ring() + if not K.is_subring(QQbar): + raise TypeError('the base field is not in QQbar') L = self.defining_polynomials() - return braid_monodromy(prod(L), arrangement=L, vertical=vertical)[:-1] + bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical)[:-1] + if vertical: + self._vertical_braid_monodromy = bm + self._vertical_strands = dic + self._vertical_asymptotes = dv + else: + self._braid_monodromy = bm + self._strands = dic + return bm + + def strands(self, vertical=False): + if not vertical: + if not self._strands: + print("Braid monodromy has not been computed") + return self._strands + if vertical: + if not self._vertical_strands: + print("Braid monodromy has not been computed") + return self._vertical_strands + + def vertical_asymptotes(self): + if not self._vertical_asymptotes: + print("Braid monodromy has not been computed") + return self._vertical_asymptotes class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): From 82e54b78d91d281da80716a7ed254c69b2dc6268 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 18 Oct 2023 17:46:51 +0200 Subject: [PATCH 37/95] class attributes - 3 --- src/sage/schemes/curves/affine_curve.py | 4 +- .../schemes/curves/plane_curve_arrangement.py | 88 ++++++++++++++----- src/sage/schemes/curves/zariski_vankampen.py | 10 ++- 3 files changed, 76 insertions(+), 26 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 9262a9b5e38..24710474077 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1769,12 +1769,12 @@ def is_vertical_line(self): EXAMPLES:: - sage: A2. = AffineSpace(2, QQ) + sage: A2. = AffineSpace(2, QQ) sage: Curve(x - 1).is_vertical_line() True sage: Curve(x - y).is_vertical_line() False - sage: Curve(y^2 * x + x + y).has_vertical_asymptote() + sage: Curve(y^2 * x + x + y).is_vertical_line() False """ A = self.ambient_space() diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index e5657dba125..856f1e2612e 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -108,7 +108,6 @@ def __init__(self, parent, curves, check=True): self._vertical_asymptotes = dict() self._fundamental_group = None self._meridians = dict() - self._infinity = None def __getitem__(self, i): """ @@ -332,6 +331,23 @@ def change_ring(self, base_ring): curves = tuple(c.change_ring(base_ring) for c in self) return parent(curves) + def coordinate_ring(self): + """ + Return the coordinate ring of ``self``. + + OUTPUT: + + The base ring of the curve arrangement. + + EXAMPLES:: + + sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: C = L(x, y) + sage: C.coordinate_ring() + Multivariate Polynomial Ring in x, y over Rational Field + """ + return self.curves()[0].defining_polynomial().parent() + def defining_polynomials(self): r""" Return the defining polynomials of the elements of``self``. @@ -413,28 +429,42 @@ def fundamental_group(self, simplified=True, vertical=False): sage: # needs sirocco sage: H. = OrderedAffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) - sage: A.fundamental_group() - (Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, - x1*x0*x1^-1*x0^-1, - (x0*x2)^2*(x0^-1*x2^-1)^2 >, - {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], - 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]}) - sage: A.fundamental_group(vertical=True) - (Finitely presented group < x0, x1, x2 | x1*x0^-1*x1^-1*x0, - x2*x1*x2^-1*x1^-1, x2*x0*x2^-1*x0^-1 >, - {0: [x1], 1: [x0], 2: [x2], 3: [x2^-1*x1^-2*x0^-1]}) + sage: G = A.fundamental_group(); G + Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, + x1*x0*x1^-1*x0^-1, + (x0*x2)^2*(x0^-1*x2^-1)^2 > + sage: A.fundamental_group(vertical=True) == G + True .. WARNING:: This functionality requires the sirocco package to be installed. """ + if self._fundamental_group and self._meridians: + return self._fundamental_group K = self.base_ring() + R = self.coordinate_ring() if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') C = self.reduce() L = C.defining_polynomials() - G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical) - return (G, dic) + if not vertical and self._braid_monodromy is not None and self._strands: + d1 = prod(L).degree() + bd = (self._braid_monodromy, self._strands, dict(), d1) + elif vertical and self._vertical_braid_monodromy is not None and self._vertical_asymptotes and self._vertical_strands: + d1 = prod(L).degree(R.gen(1)) + bd = (self._braid_monodromy, self._strands, self._vertical_asymptotes, d1) + else: + bd = None + G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical, braid_data=bd) + self._fundamental_group = G + self._meridians = dic + return G + + def meridians(self): + if not self._meridians: + print("Braid monodromy has not been computed") + return self._meridians def braid_monodromy(self, vertical=False): r""" @@ -459,15 +489,13 @@ def braid_monodromy(self, vertical=False): sage: H. = OrderedAffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) sage: A.braid_monodromy() - ([s1*s0*(s1*s2*s1)^2*s2*(s1^-1*s2^-1)^2*s1^-1*s0^-1*s1^-1, - s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - s1*s0*s1*s2*(s1*s2^-1)^2*s0*s1*s2*s1*s0*s2^-1*s1^-3*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - s1*s0*s1*s2*s1*s2^-1*s1^4*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - s1*s0*s1*s2*s1*s2^-1*s1^-1*s2*s0^-1*s1^-1], - {0: 2, 1: 1, 2: 0, 3: 0}, {}) + [s1*s0*(s1*s2*s1)^2*s2*(s1^-1*s2^-1)^2*s1^-1*s0^-1*s1^-1, + s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s1*s2*(s1*s2^-1)^2*s0*s1*s2*s1*s0*s2^-1*s1^-3*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s1*s2*s1*s2^-1*s1^4*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + s1*s0*s1*s2*s1*s2^-1*s1^-1*s2*s0^-1*s1^-1] sage: A.braid_monodromy(vertical=True) - ([s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0], - {0: 1, 1: 0, 2: 0}, {1: 2}) + [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] .. WARNING:: @@ -481,7 +509,7 @@ def braid_monodromy(self, vertical=False): if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') L = self.defining_polynomials() - bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical)[:-1] + bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical) if vertical: self._vertical_braid_monodromy = bm self._vertical_strands = dic @@ -568,6 +596,22 @@ def base_ring(self): """ return self._base_ring + def coordinate_ring(self): + """ + Return the base ring. + + OUTPUT: + + The base ring of the curve arrangement. + + EXAMPLES:: + + sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L.base_ring() + Rational Field + """ + return self._coordinate_ring + def change_ring(self, base_ring): """ Return curve arrangements over a different base ring. diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 45efea572bd..ebbfb66b81f 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1700,7 +1700,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, vertical=False): +def fundamental_group_arrangement(flist, simplified=True, projective=False, puiseux=False, vertical=False, braid_data=None): r""" Compute the fundamental group of the complement of a curve defined by a list of polynomials with the extra information @@ -1730,6 +1730,10 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis whenever no curve has vertical asymptotes the computation of braid monodromy is simpler if some lines are vertical. + - ``braid_data`` -- tuple (default: ``None``); if it is not the default + it is the output of ``fundamental_group_from_braid_mon`` previously + computed. + OUTPUT: - A list of braids. The braids correspond to paths based in the same point; @@ -1808,7 +1812,9 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis infinity = all([g.degree(y) == g.degree() or Curve(g).is_vertical_line() for g in flist1]) if vertical0: infinity = all([not Curve(g).has_vertical_asymptote() for g in flist1]) - if len(flist1) == 0: + if braid_data: + bm, dic, dv, d1 = braid_data + elif len(flist1) == 0: bm = [] dic = dict() dv = {j: j for j, f in flist} From 1fb3c768c5e7aa490dff7448775ed78736e0bcaa Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 20 Oct 2023 00:38:30 +0200 Subject: [PATCH 38/95] some more doct-test, vertical lines to be verified --- .../schemes/curves/plane_curve_arrangement.py | 121 +++++++++++++++--- 1 file changed, 103 insertions(+), 18 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 856f1e2612e..606f55701e3 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -105,7 +105,7 @@ def __init__(self, parent, curves, check=True): self._strands = dict() self._vertical_braid_monodromy = None self._vertical_strands = None - self._vertical_asymptotes = dict() + self._vertical_lines = None self._fundamental_group = None self._meridians = dict() @@ -451,9 +451,9 @@ def fundamental_group(self, simplified=True, vertical=False): if not vertical and self._braid_monodromy is not None and self._strands: d1 = prod(L).degree() bd = (self._braid_monodromy, self._strands, dict(), d1) - elif vertical and self._vertical_braid_monodromy is not None and self._vertical_asymptotes and self._vertical_strands: + elif vertical and self._vertical_braid_monodromy is not None and self._vertical_lines is not None and self._vertical_strands: d1 = prod(L).degree(R.gen(1)) - bd = (self._braid_monodromy, self._strands, self._vertical_asymptotes, d1) + bd = (self._braid_monodromy, self._strands, self._vertical_lines, d1) else: bd = None G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical, braid_data=bd) @@ -462,8 +462,33 @@ def fundamental_group(self, simplified=True, vertical=False): return G def meridians(self): - if not self._meridians: + r""" + Meridians of each irreducible component if the group has been computed + + OUTPUT: + + A dictionnary which associates the index of each curve with its meridians, + including the line at infinity if it can be easily computed + + EXAMPLES:: + + sage: # needs sirocco + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x, y + x - 1, x) + sage: G = A.fundamental_group(); G + Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, + x1*x0*x1^-1*x0^-1, + (x0*x2)^2*(x0^-1*x2^-1)^2 > + sage: A.meridians() + {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not self._meridians or not self._fundamental_group: print("Braid monodromy has not been computed") + return None return self._meridians def braid_monodromy(self, vertical=False): @@ -501,9 +526,9 @@ def braid_monodromy(self, vertical=False): This functionality requires the sirocco package to be installed. """ - if self._braid_monodromy and not vertical: + if not vertical and self._braid_monodromy is not None and self._strands: return self._braid_monodromy - if self._vertical_braid_monodromy and self._vertical_asymptotes and vertical: + if vertical and self._vertical_braid_monodromy is not None and self._vertical_strands and self._vertical_lines is not None: return self._vertical_braid_monodromy K = self.base_ring() if not K.is_subring(QQbar): @@ -513,26 +538,86 @@ def braid_monodromy(self, vertical=False): if vertical: self._vertical_braid_monodromy = bm self._vertical_strands = dic - self._vertical_asymptotes = dv + self._vertical_lines = dv else: self._braid_monodromy = bm self._strands = dic return bm def strands(self, vertical=False): - if not vertical: - if not self._strands: - print("Braid monodromy has not been computed") - return self._strands - if vertical: - if not self._vertical_strands: - print("Braid monodromy has not been computed") - return self._vertical_strands + r""" + Strands for each member of the arrangement. + + OUTPUT: + + A dictionnary which associates to the index of each strand its associated component. + + EXAMPLES:: + + sage: # needs sirocco + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x, y + x - 1, x) + sage: bm = A.braid_monodromy() + sage: A.strands() + {0: 2, 1: 1, 2: 0, 3: 0} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not self._strands or self._braid_monodromy is None: + print("Braid monodromy has not been computed") + return self._strands + + def vertical_strands(self): + r""" + Vertical strands for each member of the arrangement. + + OUTPUT: + + A dictionnary which associates to the index of each strand its associated component. + + EXAMPLES:: + + sage: # needs sirocco + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x, y + x - 1, x) + sage: bm = A.braid_monodromy(vertical=True) + sage: A.vertical_strands() + {0: 1, 1: 0, 2: 0} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not self._vertical_strands or self._vertical_braid_monodromy is None or self._vertical_lines is None: + print("Vertical braid monodromy has not been computed") + return self._vertical_strands + + def vertical_lines(self): + r""" + Vertical lines in the arrangement. + + OUTPUT: + + A dictionnary which associates an index to the index of a vertical lines. + + EXAMPLES:: - def vertical_asymptotes(self): - if not self._vertical_asymptotes: + sage: # needs sirocco + sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x, y + x - 1, x) + sage: bm = A.braid_monodromy(vertical=True) + sage: A.vertical_lines() + {1: 2} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not self._vertical_strands or self._vertical_braid_monodromy is None or self._vertical_lines is None: print("Braid monodromy has not been computed") - return self._vertical_asymptotes + return self._vertical_lines class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): From e7053e4939211c81386fe28500fc80ded80b7f32 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 20 Oct 2023 08:25:33 +0200 Subject: [PATCH 39/95] change documentation of fundamental_group_arrangement --- src/sage/schemes/curves/zariski_vankampen.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index ebbfb66b81f..b816d62beb0 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1170,16 +1170,15 @@ def braid_monodromy(f, arrangement=(), vertical=False): OUTPUT: - A list of braids, two dictionaries, and the number - of strands of the braids (specially relevant if the list - pf braids is empty). - The braids correspond to paths based in the same point; - each of these paths is the conjugated of a loop around one of the points - in the discriminant of the projection of ``f``. The first dictionary assigns each - strand to the index of the corresponding factor in ``arrangement``. - If ``vertical`` is set to ``True`` only the vertical lines are not used. - The second dictionnary assign to each vertical line the corresponding braid. - The fourth one is the number of strands of the braids. + - A list of braids, images by the braid monodromy of a geometric + basis of the complement of the discriminant of `f` in `\mathbb{C}'. + - A dictionnary: ``i``, index of a strand is sent to the index of + the corresponding factor in ``arrangement``. + - Another dictionnary, only relevant if ``vertical`` is ``True``. It attaches + the index of a vertical line in ``arrangement`` to the index of its + corresponding braid. + - A non-negative integer: the number of strands of the braids, only necessary + if the list of braids is empty. .. NOTE:: From 3fdbcea051d4f968abe7a73cad1730865b419007 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 6 Nov 2023 10:36:14 +0100 Subject: [PATCH 40/95] lint issue --- src/sage/schemes/curves/zariski_vankampen.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index b816d62beb0..aa2dd190968 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1171,12 +1171,15 @@ def braid_monodromy(f, arrangement=(), vertical=False): OUTPUT: - A list of braids, images by the braid monodromy of a geometric - basis of the complement of the discriminant of `f` in `\mathbb{C}'. + basis of the complement of the discriminant of `f` in `\mathbb{C}`. + - A dictionnary: ``i``, index of a strand is sent to the index of the corresponding factor in ``arrangement``. + - Another dictionnary, only relevant if ``vertical`` is ``True``. It attaches the index of a vertical line in ``arrangement`` to the index of its corresponding braid. + - A non-negative integer: the number of strands of the braids, only necessary if the list of braids is empty. From b87ef6f5876200c780c01ed89759ef61f2f4e300 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 11 Nov 2023 22:33:10 +0100 Subject: [PATCH 41/95] sort a list to avoid random failures --- src/sage/schemes/curves/zariski_vankampen.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index aa2dd190968..761eb6a19ff 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -188,13 +188,15 @@ 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) - (1, - -0.500000000000000? - 0.866025403784439?*I, - -0.500000000000000? + 0.866025403784439?*I, - -0.522757958574711?, - 0.2613789792873551? - 0.4527216721561923?*I, - 0.2613789792873551? + 0.4527216721561923?*I) + sage: L = list(discrim(flist)) + sage: L.sort() + sage: L + [-0.522757958574711?, + -0.500000000000000? - 0.866025403784439?*I, + -0.500000000000000? + 0.866025403784439?*I, + 0.2613789792873551? - 0.4527216721561923?*I, + 0.2613789792873551? + 0.4527216721561923?*I, + 1] """ flist = tuple(pols) x, y = flist[0].parent().gens() From 7caaf7648d922903fd6c110606a0f0061b2ebc1b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 13 Nov 2023 10:21:33 +0100 Subject: [PATCH 42/95] some doc typos --- .../geometry/hyperplane_arrangement/arrangement.py | 6 +++--- src/sage/schemes/curves/plane_curve_arrangement.py | 14 ++++---------- src/sage/schemes/curves/zariski_vankampen.py | 6 +++--- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 1910f1c2fcb..9972b646059 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3766,7 +3766,7 @@ def _fundamental_group_(self, proj=False): 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`` + coefficients in a subfield of `\overline{\mathbb{Q}}`` INPUT: @@ -3869,7 +3869,7 @@ def fundamental_group(self, projective=False): 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`` + coefficients in a subfield of `\overline{\mathbb{Q}` INPUT: @@ -4268,7 +4268,7 @@ def _coerce_map_from_(self, P): class OrderedHyperplaneArrangements(HyperplaneArrangements): """ - Hyperplane arrangements. + Ordered Hyperplane arrangements. For more information on hyperplane arrangements, see :mod:`sage.geometry.hyperplane_arrangement.arrangement`. diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 606f55701e3..2af5051fc37 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -3,7 +3,7 @@ Affine Plane Curve Arrangements We create an :class:`OrderedAffinePlaneCurveArrangements` -object following the properties of :class:`HyperplaneArrangements` +object following the properties of :class:`HyperplaneArrangements`:: sage: H. = OrderedAffinePlaneCurveArrangements(QQ) sage: C = H(3*x + 2*y - x^2 + y^3 - 7); C @@ -14,8 +14,8 @@ sage: C[0].parent() -The default base field is `\QQ`, the rational numbers. -Number fields are also possible (also with fixed embeddings in ``QQbar``):: +The default base field is `\mathbb{Q}`, the rational numbers. +Number fields are also possible (also with fixed embeddings in `\overline{\mathbb{Q}}`):: sage: # needs sage.rings.number_field sage: x = polygen(QQ, 'x') @@ -66,12 +66,6 @@ class OrderedAffinePlaneCurveArrangementsElement(Element): """ An ordered affine plane curve arrangement. - # - # .. WARNING:: - # - # You should never create - # :class:`HyperplaneArrangementElement` instances directly, - # always use the parent. """ def __init__(self, parent, curves, check=True): @@ -410,7 +404,7 @@ def reduce(self): def fundamental_group(self, simplified=True, vertical=False): r""" - It computes the fundamental group of the complement of the union + The fundamental group of the complement of the union of affine plane curves in `\mathbb{C}^2`. INPUT: diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 761eb6a19ff..b155aeb6a4f 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -287,7 +287,7 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False): - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given by a list of edges - - ``convex`` -- boolean (default: `False`), if set to ``True`` a simpler + - ``convex`` -- boolean (default: ``False``), if set to ``True`` a simpler computation is made - ``precision`` -- bits of precision (default: 53) @@ -383,7 +383,7 @@ def voronoi_cells(V, vertical_lines=[]): - ``V`` -- a corrected Voronoi diagram - - ``vertical_lines`` -- list (default: `[]`) indices of the vertical lines + - ``vertical_lines`` -- list (default: ``[]``) indices of the vertical lines OUTPUT: @@ -1348,7 +1348,7 @@ def conjugate_positive_form(braid): INPUT: - - ``braid`` -- a braid ``\sigma``. + - ``braid`` -- a braid `\sigma`. OUTPUT: From 54f926dcafd5f8227d5e70e28023f5e3b57e256b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 18 Nov 2023 10:25:54 +0100 Subject: [PATCH 43/95] change names in fieldI to avoid strange errors if b is a variable --- 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 b155aeb6a4f..1c3cfe5baf9 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -634,9 +634,9 @@ def fieldI(field): sage: a0 = p.roots(QQbar, multiplicities=False)[0] sage: F0. = NumberField(p, embedding=a0) sage: fieldI(F0) - Number Field in b with defining polynomial + Number Field in primitif_element 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 + with primitif_element = 0.4863890359345430? + 1.000000000000000?*I If ``I`` is already in the field, the result is the field itself:: @@ -652,7 +652,7 @@ def fieldI(field): if I0 in field: return field field_a = field[I0] - field_b = field_a.absolute_field('b0') + field_b = field_a.absolute_field('imaginary_unit') b0 = field_b.gen() q = b0.minpoly() qembd = field_b.embeddings(QQbar) @@ -660,7 +660,7 @@ def fieldI(field): b1 = h1(b0) b2 = h1(field_b(field_a.gen(0))) b3 = field.gen(0) - F1 = NumberField(q, 'b', embedding=b1) + F1 = NumberField(q, 'primitif_element', embedding=b1) if b3 in F1 and b2.imag() > 0: return F1 From 16f3608668c5a767627f10caf958b08b9a860acd Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 22 Nov 2023 11:46:57 +0100 Subject: [PATCH 44/95] comentarios para corregir en plane_curve_arrangement --- .../schemes/curves/plane_curve_arrangement.py | 42 ++++++------------- 1 file changed, 12 insertions(+), 30 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 2af5051fc37..01de7f51ec1 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -2,6 +2,7 @@ r""" Affine Plane Curve Arrangements +# Quitar Ordered We create an :class:`OrderedAffinePlaneCurveArrangements` object following the properties of :class:`HyperplaneArrangements`:: @@ -95,6 +96,7 @@ def __init__(self, parent, curves, check=True): raise ValueError("not all elements are curves") if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") + # Añadir más atributos con opciones self._braid_monodromy = None self._strands = dict() self._vertical_braid_monodromy = None @@ -180,6 +182,7 @@ def curves(self): return self._curves def _repr_(self): + # Añadir espacio ambiente y posiblemente que dé solo los polinomios r""" String representation for a curve arrangement. @@ -258,7 +261,7 @@ def union(self, other): result = P(*curves) return result - add_curve = union + add_curves = union __or__ = union @@ -434,6 +437,7 @@ def fundamental_group(self, simplified=True, vertical=False): This functionality requires the sirocco package to be installed. """ + # creo que no hace falta comprobar meridianos if self._fundamental_group and self._meridians: return self._fundamental_group K = self.base_ring() @@ -445,17 +449,19 @@ def fundamental_group(self, simplified=True, vertical=False): if not vertical and self._braid_monodromy is not None and self._strands: d1 = prod(L).degree() bd = (self._braid_monodromy, self._strands, dict(), d1) + # línea demasiada larga elif vertical and self._vertical_braid_monodromy is not None and self._vertical_lines is not None and self._vertical_strands: d1 = prod(L).degree(R.gen(1)) bd = (self._braid_monodromy, self._strands, self._vertical_lines, d1) else: bd = None - G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical, braid_data=bd) + G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical, braid_data=bd) self._fundamental_group = G self._meridians = dic return G def meridians(self): + # Añadir opciones con los nuevos atributos y si no está que lo calcule r""" Meridians of each irreducible component if the group has been computed @@ -589,6 +595,8 @@ def vertical_strands(self): return self._vertical_strands def vertical_lines(self): + # Mirar de hacerlo sin calcular la monodromía, solo chequear con vertical + # Hacer dos, vertical_lines y asíntotas, o quitarlo r""" Vertical lines in the arrangement. @@ -778,6 +786,7 @@ def _element_constructor_(self, *args, **kwds): if len(kwds) > 0: raise ValueError('unknown keyword argument') # process positional arguments + # cambiar el nombre AA = self.ambient_space() R = AA.coordinate_ring() curves = () @@ -798,7 +807,7 @@ def _element_constructor_(self, *args, **kwds): def _an_element_(self): """ - Dirty trick to avoid test run failure. + Return an element of ``self``. TESTS:: @@ -862,30 +871,3 @@ def gen(self, i): y """ return self.gens()[i] - - # def _coerce_map_from_(self, P): - # """ - # Return whether there is a coercion. - # - # TESTS:: - # - # sage: L. = OrderedAffinePlaneCurveArrangements(QQ); L - # Curve arrangements in Affine Space of dimension 2 over Rational Field - # sage: M. = HyperplaneArrangements(RR); M - # Hyperplane arrangements in 2-dimensional linear space over Real Field with 53 bits of precision with coordinates x, y - # - # sage: L.coerce_map_from(ZZ) - # Coercion map: - # From: Integer Ring - # To: Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x - # sage: M.coerce_map_from(L) - # Coercion map: - # From: Hyperplane arrangements in 1-dimensional linear space over Rational Field with coordinate x - # To: Hyperplane arrangements in 1-dimensional linear space over Real Field with 53 bits of precision with coordinate y - # sage: L.coerce_map_from(M) - # """ - # if self.ambient_space().has_coerce_map_from(P): - # return True - # if isinstance(P, OrderedAffinePlaneCurveArrangements): - # return self.base_ring().has_coerce_map_from(P.base_ring()) - # return super()._coerce_map_from_(P) From 98e08096ddb6ae28ee1bd9ea30629c08237ae0e1 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 22 Nov 2023 19:34:14 +0100 Subject: [PATCH 45/95] correcting class of affine arrangements, creating class of projective arrangements --- src/sage/schemes/curves/all.py | 4 +- .../schemes/curves/plane_curve_arrangement.py | 1084 +++++++++++++++-- src/sage/schemes/curves/zariski_vankampen.py | 23 +- 3 files changed, 982 insertions(+), 129 deletions(-) diff --git a/src/sage/schemes/curves/all.py b/src/sage/schemes/curves/all.py index 24adf1e2249..bdb29c72ee7 100644 --- a/src/sage/schemes/curves/all.py +++ b/src/sage/schemes/curves/all.py @@ -24,4 +24,6 @@ from .projective_curve import Hasse_bounds -from .plane_curve_arrangement import OrderedAffinePlaneCurveArrangements +from .plane_curve_arrangement import AffinePlaneCurveArrangements + +from .plane_curve_arrangement import ProjectivePlaneCurveArrangements diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 01de7f51ec1..944c8e7d416 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -1,14 +1,14 @@ # -*- coding: utf-8 -*- r""" -Affine Plane Curve Arrangements +Affine and Projective Plane Curve Arrangements -# Quitar Ordered -We create an :class:`OrderedAffinePlaneCurveArrangements` -object following the properties of :class:`HyperplaneArrangements`:: +We create classes :class:`AffinePlaneCurveArrangements` +and :class:`ProjectivePlaneCurveArrangements` +following the properties of :class:`HyperplaneArrangements`:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: C = H(3*x + 2*y - x^2 + y^3 - 7); C - Arrangement (Affine Plane Curve over Rational Field defined by y^3 - x^2 + 3*x + 2*y - 7) + Arrangement (y^3 - x^2 + 3*x + 2*y - 7) in Affine Space of dimension 2 over Rational Field The individual curves will be in :class:`AffinePlaneCurve`:: @@ -21,12 +21,11 @@ sage: # needs sage.rings.number_field sage: x = polygen(QQ, 'x') sage: NF. = NumberField(x^4 - 5 * x^2 + 5, embedding=1.90) - sage: H. = OrderedAffinePlaneCurveArrangements(NF) + sage: H. = AffinePlaneCurveArrangements(NF) sage: A = H(y^2 - a * z, y^2 + a * z); A - Arrangement (Affine Plane Curve over Number Field in a with defining polynomial - x^4 - 5*x^2 + 5 with a = 1.902113032590308? defined by y^2 + (-a)*z, - Affine Plane Curve over Number Field in a with defining polynomial - x^4 - 5*x^2 + 5 with a = 1.902113032590308? defined by y^2 + a*z) + Arrangement (y^2 + (-a)*z, y^2 + a*z) in Affine Space of dimension 2 + over Number Field in a with defining polynomial + x^4 - 5*x^2 + 5 with a = 1.902113032590308? sage: A.base_ring() Number Field in a with defining polynomial x^4 - 5*x^2 + 5 with a = 1.902113032590308? @@ -56,6 +55,8 @@ from sage.schemes.affine.affine_space import AffineSpace from sage.schemes.curves.affine_curve import AffinePlaneCurve from sage.schemes.curves.constructor import Curve +from sage.schemes.curves.projective_curve import ProjectiveSpace +from sage.schemes.curves.projective_curve import ProjectivePlaneCurve from sage.schemes.curves.zariski_vankampen import braid_monodromy from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement from sage.structure.parent import Parent @@ -64,7 +65,7 @@ from sage.structure.unique_representation import UniqueRepresentation -class OrderedAffinePlaneCurveArrangementsElement(Element): +class AffinePlaneCurveArrangementsElement(Element): """ An ordered affine plane curve arrangement. """ @@ -75,16 +76,15 @@ def __init__(self, parent, curves, check=True): INPUT: - - ``parent`` -- the parent :class:`HyperplaneArrangements` + - ``parent`` -- the parent :class:`AffinePlaneCurveArrangements` - ``curves`` -- a tuple of curves EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: elt = H(x, y); elt - Arrangement (Affine Plane Curve over Rational Field defined by x, - Affine Plane Curve over Rational Field defined by y) + Arrangement (x, y) in Affine Space of dimension 2 over Rational Field sage: TestSuite(elt).run() """ super().__init__(parent) @@ -98,10 +98,16 @@ def __init__(self, parent, curves, check=True): raise ValueError("not all curves are in the same ambient space") # Añadir más atributos con opciones self._braid_monodromy = None + self._braid_monodromy_with_vertical = None self._strands = dict() - self._vertical_braid_monodromy = None - self._vertical_strands = None - self._vertical_lines = None + self._strands_with_vertical = dict() + self._vertical_lines_in_braid_mon = None + self._fundamental_group_vertical_simplified = None + self._meridians_vertical_simplified = dict() + self._fundamental_group_vertical = None + self._meridians_vertical = dict() + self._fundamental_group_simplified = None + self._meridians_simplified = dict() self._fundamental_group = None self._meridians = dict() @@ -119,11 +125,10 @@ def __getitem__(self, i): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H(y^2 - x, y^3 + 2 * x^2, x^4 + y^4 + 1); h - Arrangement (Affine Plane Curve over Rational Field defined by y^2 - x, - Affine Plane Curve over Rational Field defined by y^3 + 2*x^2, - Affine Plane Curve over Rational Field defined by x^4 + y^4 + 1) + Arrangement (y^2 - x, y^3 + 2*x^2, x^4 + y^4 + 1) + in Affine Space of dimension 2 over Rational Field """ return self._curves[i] @@ -131,7 +136,7 @@ def __hash__(self): r""" TESTS:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H((x * y, x + y +1)) sage: len_dict = {h: len(h)} """ @@ -147,7 +152,7 @@ def n_curves(self): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H((x * y, x + y +1)) sage: h.n_curves() 2 @@ -168,7 +173,7 @@ def curves(self): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H((x * y, x + y + 1)) sage: h.curves() (Affine Plane Curve over Rational Field defined by x*y, @@ -182,7 +187,6 @@ def curves(self): return self._curves def _repr_(self): - # Añadir espacio ambiente y posiblemente que dé solo los polinomios r""" String representation for a curve arrangement. @@ -192,19 +196,19 @@ def _repr_(self): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: h - Arrangement of 5 curves + Arrangement of 5 curves in Affine Space of dimension 2 over Rational Field sage: H(()) - Empty curve arrangement + Empty curve arrangement in Affine Space of dimension 2 over Rational Field """ if len(self) == 0: - return 'Empty curve arrangement' + return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) elif len(self) < 5: - curves = ', '.join(h._repr_() for h in self._curves) - return 'Arrangement ({0})'.format(curves) - return 'Arrangement of {0} curves'.format(len(self)) + curves = ', '.join(h.defining_polynomial()._repr_() for h in self._curves) + return 'Arrangement ({0}) in {1}'.format(curves, self.parent().ambient_space()) + return 'Arrangement of {0} curves in {1}'.format(len(self), self.parent().ambient_space()) def _richcmp_(self, other, op): """ @@ -212,7 +216,7 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: H(x) == H(y) False sage: H(x) == H(2 * x) @@ -240,11 +244,11 @@ def union(self, other): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: C = Curve(x^8 - y^8 -x^4 * y^4) sage: h1 = h.union(C); h1 - Arrangement of 6 curves + Arrangement of 6 curves in Affine Space of dimension 2 over Rational Field sage: h1 == h1.union(C) Repeated curve True @@ -280,14 +284,12 @@ def deletion(self, curves): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: C = h[-1] sage: h.deletion(C) - Arrangement (Affine Plane Curve over Rational Field defined by x*y, - Affine Plane Curve over Rational Field defined by x + y + 1, - Affine Plane Curve over Rational Field defined by -y^5 + x^3, - Affine Plane Curve over Rational Field defined by x^5 + y^5 + x^2*y^2) + Arrangement (x*y, x + y + 1, -y^5 + x^3, x^5 + y^5 + x^2*y^2) + in Affine Space of dimension 2 over Rational Field """ parent = self.parent() curves = parent(curves) @@ -315,14 +317,12 @@ def change_ring(self, base_ring): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) sage: K. = CyclotomicField(3) # optional - sage.rings.number_field sage: A.change_ring(K) # optional - sage.rings.number_field - Arrangement (Affine Plane Curve over Cyclotomic Field of order 3 and degree 2 defined by -x^3 + y^2, - Affine Plane Curve over Cyclotomic Field of order 3 and degree 2 defined by x, - Affine Plane Curve over Cyclotomic Field of order 3 and degree 2 defined by y, - Affine Plane Curve over Cyclotomic Field of order 3 and degree 2 defined by x^2 + x*y + y^2) + Arrangement (-x^3 + y^2, x, y, x^2 + x*y + y^2) in Affine Space of + dimension 2 over Cyclotomic Field of order 3 and degree 2 """ parent = self.parent().change_ring(base_ring) curves = tuple(c.change_ring(base_ring) for c in self) @@ -338,7 +338,7 @@ def coordinate_ring(self): EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) sage: C = L(x, y) sage: C.coordinate_ring() Multivariate Polynomial Ring in x, y over Rational Field @@ -351,7 +351,7 @@ def defining_polynomials(self): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) sage: A.defining_polynomials() (-x^3 + y^2, x, y, x^2 + x*y + y^2) @@ -364,7 +364,7 @@ def defining_polynomial(self, simplified=True): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y ** 2 + x ** 2, x, y) sage: prod(A.defining_polynomials()) == A.defining_polynomial() True @@ -377,7 +377,7 @@ def have_common_factors(self): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(x * y, x^2 + x* y^3) sage: A.have_common_factors() True @@ -395,17 +395,18 @@ def reduce(self): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2)) sage: A.reduce() - Arrangement (Affine Plane Curve over Rational Field defined by y, - Affine Plane Curve over Rational Field defined by x^3 + 2*x^2*y + 2*x*y^2 + y^3) + Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Affine Space + of dimension 2 over Rational Field """ P = self.parent() L = [c.defining_polynomial().radical() for c in self] return P(*L) def fundamental_group(self, simplified=True, vertical=False): + # chequear qué pasa con vertical r""" The fundamental group of the complement of the union of affine plane curves in `\mathbb{C}^2`. @@ -424,43 +425,61 @@ def fundamental_group(self, simplified=True, vertical=False): EXAMPLES:: sage: # needs sirocco - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) - sage: G = A.fundamental_group(); G + sage: A.fundamental_group() Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x1*x0*x1^-1*x0^-1, (x0*x2)^2*(x0^-1*x2^-1)^2 > - sage: A.fundamental_group(vertical=True) == G - True + sage: A.fundamental_group(vertical=True) + Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, + x1*x0*x1^-1*x0^-1, + (x2*x1)^2*(x2^-1*x1^-1)^2 > .. WARNING:: This functionality requires the sirocco package to be installed. """ # creo que no hace falta comprobar meridianos - if self._fundamental_group and self._meridians: + if self._fundamental_group and not vertical and not simplified: return self._fundamental_group + if self._fundamental_group_simplified and simplified and not vertical: + return self._fundamental_group_simplified + if self._fundamental_group_vertical and not simplified and vertical: + return self._fundamental_group_vertical + if self._fundamental_group_vertical_simplified and simplified and vertical: + return self._fundamental_group_vertical_simplified K = self.base_ring() R = self.coordinate_ring() if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') C = self.reduce() L = C.defining_polynomials() - if not vertical and self._braid_monodromy is not None and self._strands: + if not vertical and self._braid_monodromy is not None: d1 = prod(L).degree() bd = (self._braid_monodromy, self._strands, dict(), d1) # línea demasiada larga - elif vertical and self._vertical_braid_monodromy is not None and self._vertical_lines is not None and self._vertical_strands: + elif vertical and self._braid_monodromy_with_vertical is not None: d1 = prod(L).degree(R.gen(1)) - bd = (self._braid_monodromy, self._strands, self._vertical_lines, d1) + bd = (self._braid_monodromy_with_vertical, self._strands, self._vertical_lines_in_braid_mon, d1) else: bd = None G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical, braid_data=bd) - self._fundamental_group = G - self._meridians = dic + if not vertical and not simplified: + self._fundamental_group = G + self._meridians = dic + elif not vertical: + self._fundamental_group_simplified = G + self._meridians_simplified = dic + elif not simplified: + self._fundamental_group_vertical = G + self._meridians_vertical = dic + else: + self._fundamental_group_vertical = G + self._meridians_vertical_simplified = dic return G - def meridians(self): + def meridians(self, simplified=True, vertical=False): # Añadir opciones con los nuevos atributos y si no está que lo calcule r""" Meridians of each irreducible component if the group has been computed @@ -473,7 +492,7 @@ def meridians(self): EXAMPLES:: sage: # needs sirocco - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) sage: G = A.fundamental_group(); G Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, @@ -486,10 +505,14 @@ def meridians(self): This functionality requires the sirocco package to be installed. """ - if not self._meridians or not self._fundamental_group: - print("Braid monodromy has not been computed") - return None - return self._meridians + if self._meridians and not vertical and not simplified: + return self._meridians + if self._meridians_simplified and simplified and not vertical: + return self._meridians_simplified + if self._meridians_vertical and not simplified and vertical: + return self._meridians_vertical + if self._meridians_vertical_simplified and simplified and vertical: + return self._meridians_vertical_simplified def braid_monodromy(self, vertical=False): r""" @@ -511,7 +534,7 @@ def braid_monodromy(self, vertical=False): EXAMPLES:: sage: # needs sirocco - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) sage: A.braid_monodromy() [s1*s0*(s1*s2*s1)^2*s2*(s1^-1*s2^-1)^2*s1^-1*s0^-1*s1^-1, @@ -526,19 +549,19 @@ def braid_monodromy(self, vertical=False): This functionality requires the sirocco package to be installed. """ - if not vertical and self._braid_monodromy is not None and self._strands: + if not vertical and self._braid_monodromy is not None: return self._braid_monodromy - if vertical and self._vertical_braid_monodromy is not None and self._vertical_strands and self._vertical_lines is not None: - return self._vertical_braid_monodromy + if vertical and self._braid_monodromy_with_vertical is not None: + return self._braid_monodromy_with_vertical K = self.base_ring() if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') L = self.defining_polynomials() bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical) if vertical: - self._vertical_braid_monodromy = bm - self._vertical_strands = dic - self._vertical_lines = dv + self._braid_monodromy_with_vertical = bm + self._strands_with_vertical = dic + self._vertical_lines_in_braid_mon = dv else: self._braid_monodromy = bm self._strands = dic @@ -555,7 +578,7 @@ def strands(self, vertical=False): EXAMPLES:: sage: # needs sirocco - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) sage: bm = A.braid_monodromy() sage: A.strands() @@ -580,7 +603,7 @@ def vertical_strands(self): EXAMPLES:: sage: # needs sirocco - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) sage: bm = A.braid_monodromy(vertical=True) sage: A.vertical_strands() @@ -590,11 +613,11 @@ def vertical_strands(self): This functionality requires the sirocco package to be installed. """ - if not self._vertical_strands or self._vertical_braid_monodromy is None or self._vertical_lines is None: - print("Vertical braid monodromy has not been computed") - return self._vertical_strands + if not self._strands_with_vertical: + self._braid_monodromy_with_vertical = self._braid_monodromy_with_vertical + return self._strands_with_vertical - def vertical_lines(self): + def vertical_lines_in_braid_mon(self): # Mirar de hacerlo sin calcular la monodromía, solo chequear con vertical # Hacer dos, vertical_lines y asíntotas, o quitarlo r""" @@ -607,22 +630,22 @@ def vertical_lines(self): EXAMPLES:: sage: # needs sirocco - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) sage: bm = A.braid_monodromy(vertical=True) - sage: A.vertical_lines() + sage: A.vertical_lines_in_braid_mon() {1: 2} .. WARNING:: This functionality requires the sirocco package to be installed. """ - if not self._vertical_strands or self._vertical_braid_monodromy is None or self._vertical_lines is None: - print("Braid monodromy has not been computed") - return self._vertical_lines + if not self._vertical_lines_in_braid_mon: + self._braid_monodromy_with_vertical = self._braid_monodromy_with_vertical + return self._vertical_lines_in_braid_mon -class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): +class AffinePlaneCurveArrangements(Parent, UniqueRepresentation): """ Curve arrangements. @@ -634,14 +657,12 @@ class OrderedAffinePlaneCurveArrangements(Parent, UniqueRepresentation): EXAMPLES:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: H(x, y^2, x-1, y-1) - Arrangement (Affine Plane Curve over Rational Field defined by x, - Affine Plane Curve over Rational Field defined by y^2, - Affine Plane Curve over Rational Field defined by x - 1, - Affine Plane Curve over Rational Field defined by y - 1) + Arrangement (x, y^2, x - 1, y - 1) in Affine Space + of dimension 2 over Rational Field """ - Element = OrderedAffinePlaneCurveArrangementsElement + Element = AffinePlaneCurveArrangementsElement def __init__(self, base_ring, names=tuple()): """ @@ -649,16 +670,16 @@ def __init__(self, base_ring, names=tuple()): TESTS:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) - sage: K = OrderedAffinePlaneCurveArrangements(QQ, names=('x', 'y')) + sage: H. = AffinePlaneCurveArrangements(QQ) + sage: K = AffinePlaneCurveArrangements(QQ, names=('x', 'y')) sage: H is K True sage: type(K) - + TESTS:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: TestSuite(H).run() """ if base_ring not in _Fields: @@ -677,7 +698,7 @@ def base_ring(self): EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.base_ring() Rational Field """ @@ -693,7 +714,7 @@ def coordinate_ring(self): EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.base_ring() Rational Field """ @@ -709,12 +730,12 @@ def change_ring(self, base_ring): OUTPUT: - A new :class:`OrderedAffinePlaneCurveArrangements` instance over the new + A new :class:`AffinePlaneCurveArrangements` instance over the new base ring. EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.gen(0) x sage: L.change_ring(RR).base_ring() @@ -725,7 +746,7 @@ def change_ring(self, base_ring): sage: L.change_ring(QQ) is L True """ - return OrderedAffinePlaneCurveArrangements(base_ring, names=self._names) + return AffinePlaneCurveArrangements(base_ring, names=self._names) @cached_method def ambient_space(self): @@ -734,7 +755,7 @@ def ambient_space(self): EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.ambient_space() Affine Space of dimension 2 over Rational Field """ @@ -750,7 +771,7 @@ def _repr_(self): EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ); L + sage: L. = AffinePlaneCurveArrangements(QQ); L Curve arrangements in Affine Space of dimension 2 over Rational Field """ return 'Curve arrangements in {0}'.format(self.ambient_space()) @@ -765,10 +786,9 @@ def _element_constructor_(self, *args, **kwds): EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) sage: A = L._element_constructor_(x, y); A - Arrangement (Affine Plane Curve over Rational Field defined by x, - Affine Plane Curve over Rational Field defined by y) + Arrangement (x, y) in Affine Space of dimension 2 over Rational Field sage: L._element_constructor_([x, y]) == A True sage: L._element_constructor_(Curve(x), Curve(y)) == A @@ -786,14 +806,13 @@ def _element_constructor_(self, *args, **kwds): if len(kwds) > 0: raise ValueError('unknown keyword argument') # process positional arguments - # cambiar el nombre - AA = self.ambient_space() - R = AA.coordinate_ring() + ambient_space = self.ambient_space() + R = ambient_space.coordinate_ring() curves = () for h in arg: try: ambient = h.ambient_space() - if ambient == AA: + if ambient == ambient_space: curves += (h, ) else: return None @@ -811,9 +830,9 @@ def _an_element_(self): TESTS:: - sage: H. = OrderedAffinePlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: H._an_element_() - Arrangement (Affine Plane Curve over Rational Field defined by t) + Arrangement (t) in Affine Space of dimension 2 over Rational Field """ x = self.gen(0) return self(x) @@ -829,7 +848,7 @@ def ngens(self): EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.ngens() 2 """ @@ -846,7 +865,7 @@ def gens(self): EXAMPLES:: - sage: L = OrderedAffinePlaneCurveArrangements(QQ, ('x', 'y')) + sage: L = AffinePlaneCurveArrangements(QQ, ('x', 'y')) sage: L.gens() (x, y) """ @@ -866,7 +885,836 @@ def gen(self, i): EXAMPLES:: - sage: L. = OrderedAffinePlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) + sage: L.gen(1) + y + """ + return self.gens()[i] + + +# class ProjectivePlaneCurveArrangementsElement(AffinePlaneCurveArrangementsElement): +class ProjectivePlaneCurveArrangementsElement(Element): + """ + An ordered projective plane curve arrangement. + """ + + def __init__(self, parent, curves, check=True): + """ + Construct an ordered projective plane curve arrangement. + + INPUT: + + - ``parent`` -- the parent :class:`ProjectivePlaneCurveArrangements` + + - ``curves`` -- a tuple of curves + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: elt = H(x, y, z); elt + Arrangement (x, y, z) in Projective Space of dimension 2 over Rational Field + sage: TestSuite(elt).run() + """ + super().__init__(parent) + self._curves = curves + if check: + if not isinstance(curves, tuple): + raise ValueError("the curves must be given as a tuple") + if not all(isinstance(h, ProjectivePlaneCurve) for h in curves): + raise ValueError("not all elements are curves") + if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): + raise ValueError("not all curves are in the same ambient space") + # Añadir más atributos con opciones + self._braid_monodromy = None + self._braid_monodromy_with_vertical = None + self._strands = dict() + self._strands_with_vertical = dict() + self._vertical_lines_in_braid_mon = None + self._fundamental_group_vertical_simplified = None + self._meridians_vertical_simplified = dict() + self._fundamental_group_vertical = None + self._meridians_vertical = dict() + self._fundamental_group_simplified = None + self._meridians_simplified = dict() + self._fundamental_group = None + self._meridians = dict() + + def __getitem__(self, i): + """ + Return the `i`-th curve. + + INPUT: + + - ``i`` -- integer + + OUTPUT: + + The `i`-th curve. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: h = H(y^2 - x * z, y^3 + 2 * x^2 * z, x^4 + y^4 + z^4); h + Arrangement (y^2 - x*z, y^3 + 2*x^2*z, x^4 + y^4 + z^4) + in Projective Space of dimension 2 over Rational Field + """ + return self._curves[i] + + def __hash__(self): + r""" + TESTS:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: h = H((x * y, x + y + z)) + sage: len_dict = {h: len(h)} + """ + return hash(self.curves()) + + def n_curves(self): + r""" + Return the number of curves in the arrangement. + + OUTPUT: + + An integer. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: h = H((x * y, x + y + z)) + sage: h.n_curves() + 2 + sage: len(h) # equivalent + 2 + """ + return len(self._curves) + + __len__ = n_curves + + def curves(self): + r""" + Return the curves in the arrangement as a tuple. + + OUTPUT: + + A tuple. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: h = H((x * y, x + y + z)) + sage: h.curves() + (Projective Conic Curve over Rational Field defined by x*y, + Projective Plane Curve over Rational Field defined by x + y + z) + + Note that the hyperplanes can be indexed as if they were a list:: + + sage: h[1] + Projective Plane Curve over Rational Field defined by x + y + z + """ + return self._curves + + def _repr_(self): + r""" + String representation for a curve arrangement. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) + sage: h + Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field + sage: H(()) + Empty curve arrangement in Projective Space of dimension 2 over Rational Field + """ + if len(self) == 0: + return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) + elif len(self) < 5: + curves = ', '.join(h.defining_polynomial()._repr_() for h in self._curves) + return 'Arrangement ({0}) in {1}'.format(curves, self.parent().ambient_space()) + return 'Arrangement of {0} curves in {1}'.format(len(self), self.parent().ambient_space()) + + def _richcmp_(self, other, op): + """ + Compare two curve arrangements. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: H(x) == H(y) + False + sage: H(x) == H(2 * x) + True + + TESTS:: + + sage: H(x) == 0 + False + """ + return richcmp(self._curves, other._curves, op) + + def union(self, other): + r""" + The union of ``self`` with ``other``. + + INPUT: + + - ``other`` -- a curve arrangement or something that can + be converted into a curve arrangement + + OUTPUT: + + A new curve arrangement. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) + sage: C = Curve(x^8 - y^8 -x^4 * y^4) + sage: h1 = h.union(C); h1 + Arrangement of 6 curves in Projective Space of dimension 2 over Rational Field + sage: h1 == h1.union(C) + Repeated curve + True + """ + P = self.parent() + other_h = P(other) + curves0 = self._curves + other_h._curves + curves = () + for h in curves0: + if h not in curves: + curves += (h, ) + else: + print("Repeated curve") + result = P(*curves) + return result + + add_curves = union + + __or__ = union + + def deletion(self, curves): + r""" + Return the curve arrangement obtained by removing ``h``. + + INPUT: + + - ``h`` -- a curve or curve arrangement + + OUTPUT: + + A new curve arrangement with the given curve(s) + ``h`` removed. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) + sage: C = h[-1] + sage: h.deletion(C) + Arrangement (x*y, x + y + z, -y^5 + x^3*z^2, x^5 + y^5 + x^2*y^2*z) + in Projective Space of dimension 2 over Rational Field + """ + parent = self.parent() + curves = parent(curves) + planes = list(self) + for curve in curves: + try: + planes.remove(curve) + except ValueError: + raise ValueError('curve is not in the arrangement') + return parent(planes) + + def change_ring(self, base_ring): + """ + Return curve arrangement over the new base ring. + + INPUT: + + - ``base_ring`` -- the new base ring; must be a field for + curve arrangements + + OUTPUT: + + The curve arrangement obtained by changing the base + field, as a new curve arrangement. + + EXAMPLES:: + + SAGE: # needs sage.rings.number_field + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(y^2*z - x^3, x, y, y^2 + x * y + x^2) + sage: K. = CyclotomicField(3) + sage: A.change_ring(K) + Arrangement (-x^3 + y^2*z, x, y, x^2 + x*y + y^2) in Projective Space of + dimension 2 over Cyclotomic Field of order 3 and degree 2 + """ + parent = self.parent().change_ring(base_ring) + curves = tuple(c.change_ring(base_ring) for c in self) + return parent(curves) + + def coordinate_ring(self): + """ + Return the coordinate ring of ``self``. + + OUTPUT: + + The base ring of the curve arrangement. + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: C = L(x, y) + sage: C.coordinate_ring() + Multivariate Polynomial Ring in x, y, z over Rational Field + """ + return self.curves()[0].defining_polynomial().parent() + + def defining_polynomials(self): + r""" + Return the defining polynomials of the elements of``self``. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(y^2*z - x^3, x, y, y^2 + x * y + x^2) + sage: A.defining_polynomials() + (-x^3 + y^2*z, x, y, x^2 + x*y + y^2) + """ + return tuple(h.defining_polynomial() for h in self) + + def defining_polynomial(self, simplified=True): + r""" + Return the defining polynomial of the union of the curves in ``self``. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(y ** 2 + x ** 2, x, y) + sage: prod(A.defining_polynomials()) == A.defining_polynomial() + True + """ + return prod(self.defining_polynomials()) + + def have_common_factors(self): + r""" + Check if th curves have common factors. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(x * y, x^2 * z^2 + x * y^3) + sage: A.have_common_factors() + True + """ + L = [c.defining_polynomial() for c in self] + C = Combinations(L, 2) + for f1, f2 in C: + if f1.gcd(f2).degree() > 0: + return True + return False + + def reduce(self): + r""" + Replace the curves by their reduction. + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2)) + sage: A.reduce() + Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Projective Space + of dimension 2 over Rational Field + """ + P = self.parent() + L = [c.defining_polynomial().radical() for c in self] + return P(*L) + + # def fundamental_group(self, simplified=True, vertical=False): + # r""" + # The fundamental group of the complement of the union + # of affine plane curves in `\mathbb{C}^2`. + # + # INPUT: + # + # - ``vertical`` -- boolean (default: False). If it is ``True``, there + # are no vertical asymptotes, and there are vertical lines, then a + # simplified braid braid_monodromy is used. + # + # OUTPUT: + # + # A group finitely presented with the assignation of each curve to + # a set of meridians, including the line at infinity. + # + # EXAMPLES:: + # + # sage: # needs sirocco + # sage: H. = AffinePlaneCurveArrangements(QQ) + # sage: A = H(y^2 + x, y + x - 1, x) + # sage: A.fundamental_group() + # Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, + # x1*x0*x1^-1*x0^-1, + # (x0*x2)^2*(x0^-1*x2^-1)^2 > + # sage: A.fundamental_group(vertical=True) + # Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, + # x1*x0*x1^-1*x0^-1, + # (x2*x1)^2*(x2^-1*x1^-1)^2 > + # + # .. WARNING:: + # + # This functionality requires the sirocco package to be installed. + # """ + # # creo que no hace falta comprobar meridianos + # if self._fundamental_group and not vertical and not simplified: + # return self._fundamental_group + # if self._fundamental_group_simplified and simplified and not vertical: + # return self._fundamental_group_simplified + # if self._fundamental_group_vertical and not simplified and vertical: + # return self._fundamental_group_vertical + # if self._fundamental_group_vertical_simplified and simplified and vertical: + # return self._fundamental_group_vertical_simplified + # K = self.base_ring() + # R = self.coordinate_ring() + # if not K.is_subring(QQbar): + # raise TypeError('the base field is not in QQbar') + # C = self.reduce() + # L = C.defining_polynomials() + # if not vertical and self._braid_monodromy is not None: + # d1 = prod(L).degree() + # bd = (self._braid_monodromy, self._strands, dict(), d1) + # # línea demasiada larga + # elif vertical and self._braid_monodromy_with_vertical is not None: + # d1 = prod(L).degree(R.gen(1)) + # bd = (self._braid_monodromy_with_vertical, self._strands, self._vertical_lines_in_braid_mon, d1) + # else: + # bd = None + # G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical, braid_data=bd) + # if not vertical and not simplified: + # self._fundamental_group = G + # self._meridians = dic + # elif not vertical: + # self._fundamental_group_simplified = G + # self._meridians_simplified = dic + # elif not simplified: + # self._fundamental_group_vertical = G + # self._meridians_vertical = dic + # else: + # self._fundamental_group_vertical = G + # self._meridians_vertical_simplified = dic + # return G + # + # def meridians(self, simplified=True, vertical=False): + # # Añadir opciones con los nuevos atributos y si no está que lo calcule + # r""" + # Meridians of each irreducible component if the group has been computed + # + # OUTPUT: + # + # A dictionnary which associates the index of each curve with its meridians, + # including the line at infinity if it can be easily computed + # + # EXAMPLES:: + # + # sage: # needs sirocco + # sage: H. = AffinePlaneCurveArrangements(QQ) + # sage: A = H(y^2 + x, y + x - 1, x) + # sage: G = A.fundamental_group(); G + # Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, + # x1*x0*x1^-1*x0^-1, + # (x0*x2)^2*(x0^-1*x2^-1)^2 > + # sage: A.meridians() + # {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]} + # + # .. WARNING:: + # + # This functionality requires the sirocco package to be installed. + # """ + # if self._meridians and not vertical and not simplified: + # return self._meridians + # if self._meridians_simplified and simplified and not vertical: + # return self._meridians_simplified + # if self._meridians_vertical and not simplified and vertical: + # return self._meridians_vertical + # if self._meridians_vertical_simplified and simplified and vertical: + # return self._meridians_vertical_simplified + # + # def braid_monodromy(self, vertical=False): + # r""" + # It computes the braid monodromy of the complement of the union + # of affine plane curves in `\mathbb{C}^2`. If there are vertical + # asymptotes a change of variable is done. + # + # INPUT: + # + # - ``vertical`` -- boolean (default: False). If it is ``True``, there + # are no vertical asymptotes, and there are vertical lines, then a + # simplified braid braid_monodromy is computed. + # + # OUTPUT: + # + # A braid monodromy with dictionnaries identifying strans with components + # and braids with vertical lines.. + # + # EXAMPLES:: + # + # sage: # needs sirocco + # sage: H. = AffinePlaneCurveArrangements(QQ) + # sage: A = H(y^2 + x, y + x - 1, x) + # sage: A.braid_monodromy() + # [s1*s0*(s1*s2*s1)^2*s2*(s1^-1*s2^-1)^2*s1^-1*s0^-1*s1^-1, + # s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + # s1*s0*s1*s2*(s1*s2^-1)^2*s0*s1*s2*s1*s0*s2^-1*s1^-3*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + # s1*s0*s1*s2*s1*s2^-1*s1^4*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, + # s1*s0*s1*s2*s1*s2^-1*s1^-1*s2*s0^-1*s1^-1] + # sage: A.braid_monodromy(vertical=True) + # [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] + # + # .. WARNING:: + # + # This functionality requires the sirocco package to be installed. + # """ + # if not vertical and self._braid_monodromy is not None: + # return self._braid_monodromy + # if vertical and self._braid_monodromy_with_vertical is not None: + # return self._braid_monodromy_with_vertical + # K = self.base_ring() + # if not K.is_subring(QQbar): + # raise TypeError('the base field is not in QQbar') + # L = self.defining_polynomials() + # bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical) + # if vertical: + # self._braid_monodromy_with_vertical = bm + # self._strands_with_vertical = dic + # self._vertical_lines_in_braid_mon = dv + # else: + # self._braid_monodromy = bm + # self._strands = dic + # return bm + # + # def strands(self, vertical=False): + # r""" + # Strands for each member of the arrangement. + # + # OUTPUT: + # + # A dictionnary which associates to the index of each strand its associated component. + # + # EXAMPLES:: + # + # sage: # needs sirocco + # sage: H. = AffinePlaneCurveArrangements(QQ) + # sage: A = H(y^2 + x, y + x - 1, x) + # sage: bm = A.braid_monodromy() + # sage: A.strands() + # {0: 2, 1: 1, 2: 0, 3: 0} + # + # .. WARNING:: + # + # This functionality requires the sirocco package to be installed. + # """ + # if not self._strands or self._braid_monodromy is None: + # print("Braid monodromy has not been computed") + # return self._strands + # + # def vertical_strands(self): + # r""" + # Vertical strands for each member of the arrangement. + # + # OUTPUT: + # + # A dictionnary which associates to the index of each strand its associated component. + # + # EXAMPLES:: + # + # sage: # needs sirocco + # sage: H. = AffinePlaneCurveArrangements(QQ) + # sage: A = H(y^2 + x, y + x - 1, x) + # sage: bm = A.braid_monodromy(vertical=True) + # sage: A.vertical_strands() + # {0: 1, 1: 0, 2: 0} + # + # .. WARNING:: + # + # This functionality requires the sirocco package to be installed. + # """ + # if not self._strands_with_vertical: + # self._braid_monodromy_with_vertical = self._braid_monodromy_with_vertical + # return self._strands_with_vertical + # + # def vertical_lines_in_braid_mon(self): + # # Mirar de hacerlo sin calcular la monodromía, solo chequear con vertical + # # Hacer dos, vertical_lines y asíntotas, o quitarlo + # r""" + # Vertical lines in the arrangement. + # + # OUTPUT: + # + # A dictionnary which associates an index to the index of a vertical lines. + # + # EXAMPLES:: + # + # sage: # needs sirocco + # sage: H. = AffinePlaneCurveArrangements(QQ) + # sage: A = H(y^2 + x, y + x - 1, x) + # sage: bm = A.braid_monodromy(vertical=True) + # sage: A.vertical_lines_in_braid_mon() + # {1: 2} + # + # .. WARNING:: + # + # This functionality requires the sirocco package to be installed. + # """ + # if not self._vertical_lines_in_braid_mon: + # self._braid_monodromy_with_vertical = self._braid_monodromy_with_vertical + # return self._vertical_lines_in_braid_mon + + +class ProjectivePlaneCurveArrangements(Parent, UniqueRepresentation): +# class ProjectivePlaneCurveArrangements(AffinePlaneCurveArrangements): + """ + Curve arrangements. + + INPUT: + + - ``base_ring`` -- ring; the base ring + + - ``names`` -- tuple of strings; the variable names + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: H(x, y^2, x-z, y-z) + Arrangement (x, y^2, x - z, y - z) in Projective Space + of dimension 2 over Rational Field + """ + Element = ProjectivePlaneCurveArrangementsElement + + def __init__(self, base_ring, names=tuple()): + """ + Initialize ``self``. + + TESTS:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: K = ProjectivePlaneCurveArrangements(QQ, names=('x', 'y', 'z')) + sage: H is K + True + sage: type(K) + + + TESTS:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: TestSuite(H).run() + """ + if base_ring not in _Fields: + raise ValueError('base ring must be a field') + super().__init__(category=Sets()) + self._base_ring = base_ring + self._names = names + + def base_ring(self): + """ + Return the base ring. + + OUTPUT: + + The base ring of the curve arrangement. + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.base_ring() + Rational Field + """ + return self._base_ring + + def coordinate_ring(self): + """ + Return the base ring. + + OUTPUT: + + The base ring of the curve arrangement. + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.base_ring() + Rational Field + """ + return self._coordinate_ring + + def change_ring(self, base_ring): + """ + Return curve arrangements over a different base ring. + + INPUT: + + - ``base_ring`` -- a ring; the new base ring. + + OUTPUT: + + A new :class:`ProjectivePlaneCurveArrangements` instance over the new + base ring. + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.gen(0) + x + sage: L.change_ring(RR).base_ring() + Real Field with 53 bits of precision + + TESTS:: + + sage: L.change_ring(QQ) is L + True + """ + return ProjectivePlaneCurveArrangements(base_ring, names=self._names) + + @cached_method + def ambient_space(self): + """ + Return the ambient space. + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Projective Space of dimension 2 over Rational Field + """ + return ProjectiveSpace(self.base_ring(), 2, self._names) + + def _repr_(self): + """ + Return a string representation. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: L. = AffinePlaneCurveArrangements(QQ); L + Curve arrangements in Affine Space of dimension 2 over Rational Field + """ + return 'Curve arrangements in {0}'.format(self.ambient_space()) + + def _element_constructor_(self, *args, **kwds): + """ + Construct an element of ``self``. + + INPUT: + + - ``*args`` -- positional arguments, each defining a curve + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: A = L._element_constructor_(x, y); A + Arrangement (x, y) in Projective Space of dimension 2 over Rational Field + sage: L._element_constructor_([x, y]) == A + True + sage: L._element_constructor_(Curve(x), Curve(y)) == A + True + sage: L._element_constructor_(y, x) == A + False + """ + if len(args) == 1 and not (isinstance(args[0], (tuple, list))): + arg = (args[0], ) + elif len(args) == 1: + arg = tuple(args[0]) + else: + arg = tuple(args) + # process keyword arguments + if len(kwds) > 0: + raise ValueError('unknown keyword argument') + # process positional arguments + ambient_space = self.ambient_space() + R = ambient_space.coordinate_ring() + curves = () + for h in arg: + try: + ambient = h.ambient_space() + if ambient == ambient_space: + curves += (h, ) + else: + return None + except AttributeError: + try: + h = R(h) + curves += (Curve(h), ) + except TypeError: + return None + return self.element_class(self, curves) + + def _an_element_(self): + """ + Return an element of ``self``. + + TESTS:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: H._an_element_() + Arrangement (t) in Projective Space of dimension 2 over Rational Field + """ + x = self.gen(0) + return self(x) + + @cached_method + def ngens(self): + """ + Return the number of variables, i.e. 3, kept for completness. + + OUTPUT: + + An integer (3). + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.ngens() + 3 + """ + return len(self._names) + + @cached_method + def gens(self): + """ + Return the coordinates. + + OUTPUT: + + A tuple of linear expressions, one for each linear variable. + + EXAMPLES:: + + sage: L = ProjectivePlaneCurveArrangements(QQ, ('x', 'y', 'z')) + sage: L.gens() + (x, y, z) + """ + return self.ambient_space().gens() + + def gen(self, i): + """ + Return the `i`-th coordinate. + + INPUT: + + - ``i`` -- integer + + OUTPUT: + + A variable. + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) sage: L.gen(1) y """ diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 1c3cfe5baf9..c6cc65c9c46 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1269,7 +1269,8 @@ def braid_monodromy(f, arrangement=(), vertical=False): if d > 1: result = [BraidGroup(d).one() for p in transversal] else: - result = [() for p in transversal] + G = FreeGroup(0) / [] + result = [G.one() for p in transversal] p1 = F(0) if d > 0: roots_base, strands = strand_components(g, arrangement_h, p1) @@ -1491,8 +1492,7 @@ def relation(x, b): return x * b / x -def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projective=False, puiseux=False, - vertical=[]): +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. @@ -1519,7 +1519,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv one relation if ``projective`` is set to ``True``. - ``vertical`` -- list of integers (default: ``[]``); the indices in - ``[1..r]`` of the braids that surround a vertical line + ``[0 .. r - 1]`` of the braids that surround a vertical line If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. @@ -1543,7 +1543,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv 12 (4,) sage: B2 = BraidGroup(2) sage: bm = [B2(3 * [1])] - sage: g = fundamental_group_from_braid_mon(bm, vertical=[1]); g + sage: g = fundamental_group_from_braid_mon(bm, vertical=[0]); g Finitely presented group < x0, x1, x2 | x2*x0*x1*x2^-1*x1^-1*x0^-1, x2*x0*x1*x0*x1^-1*x0^-1*x2^-1*x1^-1 > sage: fundamental_group_from_braid_mon([]) is None @@ -1555,6 +1555,8 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv v = len(vertical0) if bm == []: d = degree + elif bm[0].parent().order() == 1: + d = 1 else: d = bm[0].parent().strands() if d is None: @@ -1565,7 +1567,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv return Fv / [] if d == 1: return Fv / [(1, j, -1, -j) for j in range(2, d + v + 1)] - bmh = [br for j, br in enumerate(bm) if j + 1 not in vertical0] + bmh = [br for j, br in enumerate(bm) if j not in vertical0] if not puiseux: relations_h = (relation([(x, b) for x in F.gens() for b in bmh])) rel_h = [r[1] for r in relations_h] @@ -1581,7 +1583,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projectiv rel_v = [] for j, k in enumerate(vertical0): l1 = d + j + 1 - br = bm[k - 1] + br = bm[k] for gen in F.gens(): j0 = gen.Tietze()[0] rl = (l1,) + (gen * br).Tietze() + (-l1, -j0) @@ -1792,9 +1794,10 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis {0: [x1], 1: [x3], 2: [x2], 3: [x0], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) sage: fundamental_group_arrangement(L, vertical=True) - (Finitely presented group < x0, x1, x2, x3 | x2*x0*x2^-1*x0^-1, x2*x1*x2^-1*x1^-1, - x1*x3*x0*x3^-1*x1^-1*x0^-1, - x1*x3*x0*x1^-1*x0^-1*x3^-1 >, + (Finitely presented group < x0, x1, x2, x3 | x3*x0*x3^-1*x0^-1, + x3*x1*x3^-1*x1^-1, + x1*x2*x0*x2^-1*x1^-1*x0^-1, + x1*x2*x0*x1^-1*x0^-1*x2^-1 >, {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) """ if len(flist) > 0: From 397224eac9ec9ad938eadef3a6ffbeb59aa22479 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 23 Nov 2023 15:20:30 +0100 Subject: [PATCH 46/95] working on projective arrangements --- src/sage/schemes/curves/affine_curve.py | 4 +- .../schemes/curves/plane_curve_arrangement.py | 31 +++++----- src/sage/schemes/curves/zariski_vankampen.py | 60 ++++++++++++++----- 3 files changed, 64 insertions(+), 31 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 3b316e0ba50..e61fd7b1940 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1767,7 +1767,9 @@ def has_vertical_asymptote(self): R = A.coordinate_ring() x, y = R.gens() f = self.defining_polynomial().radical() - return f.degree(y) < f.degree() > 1 + dy = f.degree(y) + dxy = f.coefficient({y: dy}).degree() + return dxy > 0 and f.degree() > 1 def is_vertical_line(self): """ diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 944c8e7d416..860cd32a466 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -10,7 +10,7 @@ sage: C = H(3*x + 2*y - x^2 + y^3 - 7); C Arrangement (y^3 - x^2 + 3*x + 2*y - 7) in Affine Space of dimension 2 over Rational Field -The individual curves will be in :class:`AffinePlaneCurve`:: +The individual curves will be in :class:`AffinePlaneCurve` or in :class:`ProjectivePlaneCurve`:: sage: C[0].parent() @@ -419,8 +419,7 @@ def fundamental_group(self, simplified=True, vertical=False): OUTPUT: - A group finitely presented with the assignation of each curve to - a set of meridians, including the line at infinity. + A group finitely presented. EXAMPLES:: @@ -440,7 +439,6 @@ def fundamental_group(self, simplified=True, vertical=False): This functionality requires the sirocco package to be installed. """ - # creo que no hace falta comprobar meridianos if self._fundamental_group and not vertical and not simplified: return self._fundamental_group if self._fundamental_group_simplified and simplified and not vertical: @@ -458,7 +456,6 @@ def fundamental_group(self, simplified=True, vertical=False): if not vertical and self._braid_monodromy is not None: d1 = prod(L).degree() bd = (self._braid_monodromy, self._strands, dict(), d1) - # línea demasiada larga elif vertical and self._braid_monodromy_with_vertical is not None: d1 = prod(L).degree(R.gen(1)) bd = (self._braid_monodromy_with_vertical, self._strands, self._vertical_lines_in_braid_mon, d1) @@ -480,7 +477,6 @@ def fundamental_group(self, simplified=True, vertical=False): return G def meridians(self, simplified=True, vertical=False): - # Añadir opciones con los nuevos atributos y si no está que lo calcule r""" Meridians of each irreducible component if the group has been computed @@ -618,8 +614,6 @@ def vertical_strands(self): return self._strands_with_vertical def vertical_lines_in_braid_mon(self): - # Mirar de hacerlo sin calcular la monodromía, solo chequear con vertical - # Hacer dos, vertical_lines y asíntotas, o quitarlo r""" Vertical lines in the arrangement. @@ -892,8 +886,8 @@ def gen(self, i): return self.gens()[i] -# class ProjectivePlaneCurveArrangementsElement(AffinePlaneCurveArrangementsElement): class ProjectivePlaneCurveArrangementsElement(Element): +# class ProjectivePlaneCurveArrangementsElement(AffinePlaneCurveArrangementsElement): """ An ordered projective plane curve arrangement. """ @@ -925,11 +919,6 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") # Añadir más atributos con opciones - self._braid_monodromy = None - self._braid_monodromy_with_vertical = None - self._strands = dict() - self._strands_with_vertical = dict() - self._vertical_lines_in_braid_mon = None self._fundamental_group_vertical_simplified = None self._meridians_vertical_simplified = dict() self._fundamental_group_vertical = None @@ -1237,7 +1226,7 @@ def reduce(self): # def fundamental_group(self, simplified=True, vertical=False): # r""" # The fundamental group of the complement of the union - # of affine plane curves in `\mathbb{C}^2`. + # of projective plane curves in `\mathbb{P}^2`. # # INPUT: # @@ -1268,7 +1257,6 @@ def reduce(self): # # This functionality requires the sirocco package to be installed. # """ - # # creo que no hace falta comprobar meridianos # if self._fundamental_group and not vertical and not simplified: # return self._fundamental_group # if self._fundamental_group_simplified and simplified and not vertical: @@ -1277,11 +1265,22 @@ def reduce(self): # return self._fundamental_group_vertical # if self._fundamental_group_vertical_simplified and simplified and vertical: # return self._fundamental_group_vertical_simplified + # H = self.parent() # K = self.base_ring() # R = self.coordinate_ring() + # x, y, z = R.gens() # if not K.is_subring(QQbar): # raise TypeError('the base field is not in QQbar') # C = self.reduce() + # infinity = Curve(z) + # if infinity in C: + # j = C.curves().index(infinity) + # for j, c in enumerate(C): + # g = c.defining_polynomial() + # if z.divides(g): + # h = R(g / z) + # break + # C1 = H(C.curves()[:j] + (h, ) + C.curves()[j + 1: ]) # L = C.defining_polynomials() # if not vertical and self._braid_monodromy is not None: # d1 = prod(L).degree() diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index c6cc65c9c46..55e73ff0e1b 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1105,6 +1105,45 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): return (resul, vd) +def vertical_lines_in_braidmon(flist): + r""" + Returns the vertical lines in ``flist``, unless + one of the other components has a vertical asymptote. + + INPUT: + + - ``flist`` -- a list of polynomials with two variables whose + product equals ``f`` + + OUTPUT: + + A list with the indices of the vertical lines in ``flist`` if there is + no other componnet with vertical asymptote; otherwise it returns an empty + list. + + EXAMPLES:: + sage: from sage.schemes.curves.zariski_vankampen import vertical_lines_in_braidmon + sage: R. = QQ[] + sage: flist = [x^2 - y^3, x, x + 3 * y - 5, 1 - x] + sage: vertical_lines_in_braidmon(flist) + [1, 3] + sage: flist += [x * y - 1] + sage: vertical_lines_in_braidmon(flist) + [] + """ + if not flist: + return [] + res = [] + for j, f in enumerate(flist): + C = Curve(f) + vertical_asymptote = C.has_vertical_asymptote() + if vertical_asymptote: + return [] + if C.is_vertical_line(): + res.append(j) + return res + + def strand_components(f, flist, p1): r""" Compute only the assignment from strands to elements of ``flist``. @@ -1226,23 +1265,16 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: arrangement1 = tuple(_.change_ring(F) for _ in arrangement) x, y = f.parent().gens() - if not vertical or any([Curve(g).has_vertical_asymptote() for g in arrangement]): - dic_vertical = {j: False for j, f0 in enumerate(arrangement1)} - elif vertical: - dic_vertical = {j: Curve(f0).is_vertical_line() for j, f0 in enumerate(arrangement1)} - arrangement_h = () - indices_h = () - arrangement_v = () - for j, f0 in enumerate(arrangement1): - if dic_vertical[j]: - arrangement_v += (f0, ) - else: - arrangement_h += (f0, ) - indices_h += (j, ) + if vertical: + indices_v = vertical_lines_in_braidmon(arrangement1) + else: + indices_v = [] + arrangement_h = tuple(f0 for j, f0 in enumerate(arrangement1) if j not in indices_v) + arrangement_v = tuple(f0 for j, f0 in enumerate(arrangement1) if j in indices_v) glist = tuple(_[0] for f0 in arrangement_h for _ in f0.factor()) g = f.parent()(prod(glist)) d = g.degree(y) - if not arrangement_v: + if not arrangement_v: # change of coordinates only if indices_v is empty while not g.coefficient(y**d) in F: g = g.subs({x: x + y}) d = g.degree(y) From 8785db1a299e180af394058ff56fc277da5fc789 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 23 Nov 2023 19:15:42 +0100 Subject: [PATCH 47/95] method fundamental group for projective curves --- .../schemes/curves/plane_curve_arrangement.py | 439 +++++++----------- src/sage/schemes/curves/zariski_vankampen.py | 16 +- 2 files changed, 170 insertions(+), 285 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 860cd32a466..ceaeb407752 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -405,18 +405,23 @@ def reduce(self): L = [c.defining_polynomial().radical() for c in self] return P(*L) - def fundamental_group(self, simplified=True, vertical=False): - # chequear qué pasa con vertical + def fundamental_group(self, simplified=True, vertical=True, projective=False): r""" The fundamental group of the complement of the union of affine plane curves in `\mathbb{C}^2`. INPUT: - - ``vertical`` -- boolean (default: False). If it is ``True``, there + - ``vertical`` -- boolean (default: False); if it is ``True``, there are no vertical asymptotes, and there are vertical lines, then a simplified braid braid_monodromy is used. + - ``simplified`` -- boolean (default: True); if it is ``True``, the + group is simplified. + + - ``projective`` -- boolean (default: False); to be used in the + method for projective curves. + OUTPUT: A group finitely presented. @@ -426,7 +431,7 @@ def fundamental_group(self, simplified=True, vertical=False): sage: # needs sirocco sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) - sage: A.fundamental_group() + sage: A.fundamental_group(vertical=False) Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x1*x0*x1^-1*x0^-1, (x0*x2)^2*(x0^-1*x2^-1)^2 > @@ -461,7 +466,7 @@ def fundamental_group(self, simplified=True, vertical=False): bd = (self._braid_monodromy_with_vertical, self._strands, self._vertical_lines_in_braid_mon, d1) else: bd = None - G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical, braid_data=bd) + G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, projective=projective, vertical=vertical, braid_data=bd) if not vertical and not simplified: self._fundamental_group = G self._meridians = dic @@ -476,9 +481,9 @@ def fundamental_group(self, simplified=True, vertical=False): self._meridians_vertical_simplified = dic return G - def meridians(self, simplified=True, vertical=False): + def meridians(self, simplified=True, vertical=True): r""" - Meridians of each irreducible component if the group has been computed + Meridians of each irreducible component. OUTPUT: @@ -491,34 +496,44 @@ def meridians(self, simplified=True, vertical=False): sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) sage: G = A.fundamental_group(); G - Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, + Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, x1*x0*x1^-1*x0^-1, - (x0*x2)^2*(x0^-1*x2^-1)^2 > + (x2*x1)^2*(x2^-1*x1^-1)^2 > sage: A.meridians() - {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]} + {0: [x1, x2*x1*x2^-1], 1: [x0], 2: [x2], 3: [x1^-1*x2^-1*x1^-1*x0^-1]} .. WARNING:: This functionality requires the sirocco package to be installed. """ - if self._meridians and not vertical and not simplified: + if not vertical and not simplified: + computed = bool(self._meridians) + elif not vertical: + computed = bool(self._meridians_simplified) + elif not simplified: + computed = bool(self._meridians_vertical) + else: + computed = bool(self._meridians_vertical_simplified) + if not computed: + _ = self._fundamental_group(simplified=simplified, vertical=vertical) + if not vertical and not simplified: return self._meridians - if self._meridians_simplified and simplified and not vertical: + if simplified and not vertical: return self._meridians_simplified - if self._meridians_vertical and not simplified and vertical: + if not simplified and vertical: return self._meridians_vertical - if self._meridians_vertical_simplified and simplified and vertical: + if simplified and vertical: return self._meridians_vertical_simplified - def braid_monodromy(self, vertical=False): + def braid_monodromy(self, vertical=True): r""" - It computes the braid monodromy of the complement of the union + It computes the braid monodromy of the compleme+nt of the union of affine plane curves in `\mathbb{C}^2`. If there are vertical asymptotes a change of variable is done. INPUT: - - ``vertical`` -- boolean (default: False). If it is ``True``, there + - ``vertical`` -- boolean (default: True). If it is ``True``, there are no vertical asymptotes, and there are vertical lines, then a simplified braid braid_monodromy is computed. @@ -532,7 +547,7 @@ def braid_monodromy(self, vertical=False): sage: # needs sirocco sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) - sage: A.braid_monodromy() + sage: A.braid_monodromy(vertical=False) [s1*s0*(s1*s2*s1)^2*s2*(s1^-1*s2^-1)^2*s1^-1*s0^-1*s1^-1, s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, s1*s0*s1*s2*(s1*s2^-1)^2*s0*s1*s2*s1*s0*s2^-1*s1^-3*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, @@ -563,7 +578,7 @@ def braid_monodromy(self, vertical=False): self._strands = dic return bm - def strands(self, vertical=False): + def strands(self): r""" Strands for each member of the arrangement. @@ -584,8 +599,8 @@ def strands(self, vertical=False): This functionality requires the sirocco package to be installed. """ - if not self._strands or self._braid_monodromy is None: - print("Braid monodromy has not been computed") + if not self._strands: + self._braid_monodromy = self.braid_monodromy(vertical=False) return self._strands def vertical_strands(self): @@ -610,7 +625,7 @@ def vertical_strands(self): This functionality requires the sirocco package to be installed. """ if not self._strands_with_vertical: - self._braid_monodromy_with_vertical = self._braid_monodromy_with_vertical + self._braid_monodromy_with_vertical = self.braid_monodromy(vertical=True) return self._strands_with_vertical def vertical_lines_in_braid_mon(self): @@ -635,7 +650,7 @@ def vertical_lines_in_braid_mon(self): This functionality requires the sirocco package to be installed. """ if not self._vertical_lines_in_braid_mon: - self._braid_monodromy_with_vertical = self._braid_monodromy_with_vertical + self._braid_monodromy_with_vertical = self.braid_monodromy(vertical=True) return self._vertical_lines_in_braid_mon @@ -887,7 +902,7 @@ def gen(self, i): class ProjectivePlaneCurveArrangementsElement(Element): -# class ProjectivePlaneCurveArrangementsElement(AffinePlaneCurveArrangementsElement): + # class ProjectivePlaneCurveArrangementsElement(AffinePlaneCurveArrangementsElement): """ An ordered projective plane curve arrangement. """ @@ -919,10 +934,6 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") # Añadir más atributos con opciones - self._fundamental_group_vertical_simplified = None - self._meridians_vertical_simplified = dict() - self._fundamental_group_vertical = None - self._meridians_vertical = dict() self._fundamental_group_simplified = None self._meridians_simplified = dict() self._fundamental_group = None @@ -1223,257 +1234,133 @@ def reduce(self): L = [c.defining_polynomial().radical() for c in self] return P(*L) - # def fundamental_group(self, simplified=True, vertical=False): - # r""" - # The fundamental group of the complement of the union - # of projective plane curves in `\mathbb{P}^2`. - # - # INPUT: - # - # - ``vertical`` -- boolean (default: False). If it is ``True``, there - # are no vertical asymptotes, and there are vertical lines, then a - # simplified braid braid_monodromy is used. - # - # OUTPUT: - # - # A group finitely presented with the assignation of each curve to - # a set of meridians, including the line at infinity. - # - # EXAMPLES:: - # - # sage: # needs sirocco - # sage: H. = AffinePlaneCurveArrangements(QQ) - # sage: A = H(y^2 + x, y + x - 1, x) - # sage: A.fundamental_group() - # Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, - # x1*x0*x1^-1*x0^-1, - # (x0*x2)^2*(x0^-1*x2^-1)^2 > - # sage: A.fundamental_group(vertical=True) - # Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, - # x1*x0*x1^-1*x0^-1, - # (x2*x1)^2*(x2^-1*x1^-1)^2 > - # - # .. WARNING:: - # - # This functionality requires the sirocco package to be installed. - # """ - # if self._fundamental_group and not vertical and not simplified: - # return self._fundamental_group - # if self._fundamental_group_simplified and simplified and not vertical: - # return self._fundamental_group_simplified - # if self._fundamental_group_vertical and not simplified and vertical: - # return self._fundamental_group_vertical - # if self._fundamental_group_vertical_simplified and simplified and vertical: - # return self._fundamental_group_vertical_simplified - # H = self.parent() - # K = self.base_ring() - # R = self.coordinate_ring() - # x, y, z = R.gens() - # if not K.is_subring(QQbar): - # raise TypeError('the base field is not in QQbar') - # C = self.reduce() - # infinity = Curve(z) - # if infinity in C: - # j = C.curves().index(infinity) - # for j, c in enumerate(C): - # g = c.defining_polynomial() - # if z.divides(g): - # h = R(g / z) - # break - # C1 = H(C.curves()[:j] + (h, ) + C.curves()[j + 1: ]) - # L = C.defining_polynomials() - # if not vertical and self._braid_monodromy is not None: - # d1 = prod(L).degree() - # bd = (self._braid_monodromy, self._strands, dict(), d1) - # # línea demasiada larga - # elif vertical and self._braid_monodromy_with_vertical is not None: - # d1 = prod(L).degree(R.gen(1)) - # bd = (self._braid_monodromy_with_vertical, self._strands, self._vertical_lines_in_braid_mon, d1) - # else: - # bd = None - # G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, vertical=vertical, braid_data=bd) - # if not vertical and not simplified: - # self._fundamental_group = G - # self._meridians = dic - # elif not vertical: - # self._fundamental_group_simplified = G - # self._meridians_simplified = dic - # elif not simplified: - # self._fundamental_group_vertical = G - # self._meridians_vertical = dic - # else: - # self._fundamental_group_vertical = G - # self._meridians_vertical_simplified = dic - # return G - # - # def meridians(self, simplified=True, vertical=False): - # # Añadir opciones con los nuevos atributos y si no está que lo calcule - # r""" - # Meridians of each irreducible component if the group has been computed - # - # OUTPUT: - # - # A dictionnary which associates the index of each curve with its meridians, - # including the line at infinity if it can be easily computed - # - # EXAMPLES:: - # - # sage: # needs sirocco - # sage: H. = AffinePlaneCurveArrangements(QQ) - # sage: A = H(y^2 + x, y + x - 1, x) - # sage: G = A.fundamental_group(); G - # Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, - # x1*x0*x1^-1*x0^-1, - # (x0*x2)^2*(x0^-1*x2^-1)^2 > - # sage: A.meridians() - # {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]} - # - # .. WARNING:: - # - # This functionality requires the sirocco package to be installed. - # """ - # if self._meridians and not vertical and not simplified: - # return self._meridians - # if self._meridians_simplified and simplified and not vertical: - # return self._meridians_simplified - # if self._meridians_vertical and not simplified and vertical: - # return self._meridians_vertical - # if self._meridians_vertical_simplified and simplified and vertical: - # return self._meridians_vertical_simplified - # - # def braid_monodromy(self, vertical=False): - # r""" - # It computes the braid monodromy of the complement of the union - # of affine plane curves in `\mathbb{C}^2`. If there are vertical - # asymptotes a change of variable is done. - # - # INPUT: - # - # - ``vertical`` -- boolean (default: False). If it is ``True``, there - # are no vertical asymptotes, and there are vertical lines, then a - # simplified braid braid_monodromy is computed. - # - # OUTPUT: - # - # A braid monodromy with dictionnaries identifying strans with components - # and braids with vertical lines.. - # - # EXAMPLES:: - # - # sage: # needs sirocco - # sage: H. = AffinePlaneCurveArrangements(QQ) - # sage: A = H(y^2 + x, y + x - 1, x) - # sage: A.braid_monodromy() - # [s1*s0*(s1*s2*s1)^2*s2*(s1^-1*s2^-1)^2*s1^-1*s0^-1*s1^-1, - # s1*s0*(s1*s2)^2*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - # s1*s0*s1*s2*(s1*s2^-1)^2*s0*s1*s2*s1*s0*s2^-1*s1^-3*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - # s1*s0*s1*s2*s1*s2^-1*s1^4*s2*s1^-1*s2^-1*s1^-1*s0^-1*s1^-1, - # s1*s0*s1*s2*s1*s2^-1*s1^-1*s2*s0^-1*s1^-1] - # sage: A.braid_monodromy(vertical=True) - # [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] - # - # .. WARNING:: - # - # This functionality requires the sirocco package to be installed. - # """ - # if not vertical and self._braid_monodromy is not None: - # return self._braid_monodromy - # if vertical and self._braid_monodromy_with_vertical is not None: - # return self._braid_monodromy_with_vertical - # K = self.base_ring() - # if not K.is_subring(QQbar): - # raise TypeError('the base field is not in QQbar') - # L = self.defining_polynomials() - # bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical) - # if vertical: - # self._braid_monodromy_with_vertical = bm - # self._strands_with_vertical = dic - # self._vertical_lines_in_braid_mon = dv - # else: - # self._braid_monodromy = bm - # self._strands = dic - # return bm - # - # def strands(self, vertical=False): - # r""" - # Strands for each member of the arrangement. - # - # OUTPUT: - # - # A dictionnary which associates to the index of each strand its associated component. - # - # EXAMPLES:: - # - # sage: # needs sirocco - # sage: H. = AffinePlaneCurveArrangements(QQ) - # sage: A = H(y^2 + x, y + x - 1, x) - # sage: bm = A.braid_monodromy() - # sage: A.strands() - # {0: 2, 1: 1, 2: 0, 3: 0} - # - # .. WARNING:: - # - # This functionality requires the sirocco package to be installed. - # """ - # if not self._strands or self._braid_monodromy is None: - # print("Braid monodromy has not been computed") - # return self._strands - # - # def vertical_strands(self): - # r""" - # Vertical strands for each member of the arrangement. - # - # OUTPUT: - # - # A dictionnary which associates to the index of each strand its associated component. - # - # EXAMPLES:: - # - # sage: # needs sirocco - # sage: H. = AffinePlaneCurveArrangements(QQ) - # sage: A = H(y^2 + x, y + x - 1, x) - # sage: bm = A.braid_monodromy(vertical=True) - # sage: A.vertical_strands() - # {0: 1, 1: 0, 2: 0} - # - # .. WARNING:: - # - # This functionality requires the sirocco package to be installed. - # """ - # if not self._strands_with_vertical: - # self._braid_monodromy_with_vertical = self._braid_monodromy_with_vertical - # return self._strands_with_vertical - # - # def vertical_lines_in_braid_mon(self): - # # Mirar de hacerlo sin calcular la monodromía, solo chequear con vertical - # # Hacer dos, vertical_lines y asíntotas, o quitarlo - # r""" - # Vertical lines in the arrangement. - # - # OUTPUT: - # - # A dictionnary which associates an index to the index of a vertical lines. - # - # EXAMPLES:: - # - # sage: # needs sirocco - # sage: H. = AffinePlaneCurveArrangements(QQ) - # sage: A = H(y^2 + x, y + x - 1, x) - # sage: bm = A.braid_monodromy(vertical=True) - # sage: A.vertical_lines_in_braid_mon() - # {1: 2} - # - # .. WARNING:: - # - # This functionality requires the sirocco package to be installed. - # """ - # if not self._vertical_lines_in_braid_mon: - # self._braid_monodromy_with_vertical = self._braid_monodromy_with_vertical - # return self._vertical_lines_in_braid_mon + def fundamental_group(self, simplified=True): + r""" + The fundamental group of the complement of the union + of projective plane curves in `\mathbb{P}^2`. + + INPUT: + + - ``simplified`` -- boolean (default: True); set if the group + is simplified.. + + OUTPUT: + + A finitely presented group. + + EXAMPLES:: + + sage: # needs sirocco + sage: H. = AffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x, y + x - 1, x) + sage: A.fundamental_group() + Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, + x1*x0*x1^-1*x0^-1, + (x2*x1)^2*(x2^-1*x1^-1)^2 > + sage: A.fundamental_group(vertical=True) + Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, + x1*x0*x1^-1*x0^-1, + (x2*x1)^2*(x2^-1*x1^-1)^2 > + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if self._fundamental_group and not simplified: + return self._fundamental_group + if self._fundamental_group_simplified and simplified: + return self._fundamental_group_simplified + H = self.parent() + K = self.base_ring() + R = self.coordinate_ring() + x, y, z = R.gens() + if not K.is_subring(QQbar): + raise TypeError('the base field is not in QQbar') + C = self.reduce() + n = len(C) + infinity = Curve(z) + infinity_in_C = infinity in C + if infinity_in_C: + j = C.curves().index(infinity) + C = H(C.curves()[:j] + C.curves()[j + 1:]) + infinity_divides = False + for j, c in enumerate(C): + g = c.defining_polynomial() + infinity_divides = z.divides(g) + if infinity_divides: + h = R(g / z) + C = H(C.curves()[:j] + (h, ) + C.curves()[j + 1:]) + break + affine = AffinePlaneCurveArrangements(K, names=('u', 'v')) + u, v = affine.gens() + affines = [f.defining_polynomial()(x=u, y=v, z=1) for f in C] + changes = any([g.degree(v) < g.degree() > 1 for g in affines]) + while changes: + affines = [f(u=u + v) for f in affines] + changes = any([g.degree(v) < g.degree() > 1 for g in affines]) + C_affine = affine(affines) + proj = not (infinity_divides or infinity_in_C) + G = C_affine.fundamental_group(simplified=simplified, vertical=True, + projective=proj) + dic = C_affine.meridians(simplified=simplified, vertical=True) + if infinity_in_C: + dic1 = dict() + for k in range(j): + dic1[k] = dic[k] + dic1[j] = dic[n - 1] + for k in range(j + 1, n): + dic1[k] = dic[k - 1] + elif infinity_divides: + dic1 = {k: dic[k] for k in range(n)} + dic1[j] += dic[n] + else: + dic1 = dic + if not simplified: + self._fundamental_group = G + self._meridians = dic1 + else: + self._fundamental_group_simplified = G + self._meridians_simplified = dic1 + return G + + def meridians(self, simplified=True): + r""" + Meridians of each irreducible component. + + OUTPUT: + + A dictionnary which associates the index of each curve with its meridians, + including the line at infinity if it can be easily computed + + EXAMPLES:: + + sage: # needs sirocco + sage: H. = AffinePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x, y + x - 1, x) + sage: G = A.fundamental_group(); G + Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, + x1*x0*x1^-1*x0^-1, + (x2*x1)^2*(x2^-1*x1^-1)^2 > + sage: A.meridians() + {0: [x1, x2*x1*x2^-1], 1: [x0], 2: [x2], 3: [x1^-1*x2^-1*x1^-1*x0^-1]} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not simplified: + computed = bool(self._meridians) + else: + computed = bool(self._meridians_simplified) + if not computed: + _ = self._fundamental_group(simplified=simplified) + if not simplified: + return self._meridians + return self._meridians_simplified class ProjectivePlaneCurveArrangements(Parent, UniqueRepresentation): -# class ProjectivePlaneCurveArrangements(AffinePlaneCurveArrangements): + # class ProjectivePlaneCurveArrangements(AffinePlaneCurveArrangements): """ Curve arrangements. diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 55e73ff0e1b..b15a2ac0367 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1818,13 +1818,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis (Finitely presented group < x | x^2 >, {0: [x]}) sage: L = [x, y, x - 1, x -y] sage: fundamental_group_arrangement(L) - (Finitely presented group < x0, x1, x2, x3 | - x2*x3^-1*x2^-1*x3, - x0*x1*x3*x0^-1*x3^-1*x1^-1, - x3*x0*x1*x3^-1*x1^-1*x0^-1, - x1*x2*x1^-1*x0*x1*x2^-1*x1^-1*x0^-1 >, - {0: [x1], 1: [x3], 2: [x2], 3: [x0], - 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) + (Finitely presented group < x0, x1, x2, x3 | x2*x3^-1*x2^-1*x3, + x0*x1*x3*x0^-1*x3^-1*x1^-1, + x3*x0*x1*x3^-1*x1^-1*x0^-1, + x1*x2*x1^-1*x0*x1*x2^-1*x1^-1*x0^-1 >, + {0: [x1], 1: [x3], 2: [x2], 3: [x0]}) sage: fundamental_group_arrangement(L, vertical=True) (Finitely presented group < x0, x1, x2, x3 | x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1, @@ -1879,8 +1877,8 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis for i in range(len(flist1)): L = [j1 for j1 in dic if dic[j1] == i] dic1[i] = [hom(g.gen(j)) for j in L] - if not projective and infinity: - t = prod(hom(x) for x in g.gens()).inverse() + if not projective and infinity and d1 == f.degree(y): + t = prod(hom(a) for a in g.gens()).inverse() dic1[len(flist1)] = [t] n = g1.ngens() rels = [_.Tietze() for _ in g1.relations()] From 2df222f209da17f707c9e21399799438a9baa66f Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 24 Nov 2023 00:03:01 +0100 Subject: [PATCH 48/95] doctests and corrections --- .../schemes/curves/plane_curve_arrangement.py | 116 ++++++---- src/sage/schemes/curves/zariski_vankampen.py | 206 ++++++++++-------- 2 files changed, 197 insertions(+), 125 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index ceaeb407752..242ee1c0667 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -16,7 +16,8 @@ The default base field is `\mathbb{Q}`, the rational numbers. -Number fields are also possible (also with fixed embeddings in `\overline{\mathbb{Q}}`):: +Number fields are also possible (also with fixed embeddings in +`\overline{\mathbb{Q}}`):: sage: # needs sage.rings.number_field sage: x = polygen(QQ, 'x') @@ -94,7 +95,8 @@ def __init__(self, parent, curves, check=True): raise ValueError("the curves must be given as a tuple") if not all(isinstance(h, AffinePlaneCurve) for h in curves): raise ValueError("not all elements are curves") - if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): + if not all(h.ambient_space() is self.parent().ambient_space() + for h in curves): raise ValueError("not all curves are in the same ambient space") # Añadir más atributos con opciones self._braid_monodromy = None @@ -206,9 +208,12 @@ def _repr_(self): if len(self) == 0: return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) elif len(self) < 5: - curves = ', '.join(h.defining_polynomial()._repr_() for h in self._curves) - return 'Arrangement ({0}) in {1}'.format(curves, self.parent().ambient_space()) - return 'Arrangement of {0} curves in {1}'.format(len(self), self.parent().ambient_space()) + curves = ', '.join(h.defining_polynomial()._repr_() + for h in self._curves) + return 'Arrangement ({0}) in {1}'.format(curves, + self.parent().ambient_space()) + return 'Arrangement of {0} curves in {1}'.format(len(self), + self.parent().ambient_space()) def _richcmp_(self, other, op): """ @@ -403,9 +408,14 @@ def reduce(self): """ P = self.parent() L = [c.defining_polynomial().radical() for c in self] + for f1, f2 in Combinations(L, 2): + if f1.gcd(f2) != 1: + print("Some curves have common components") + return None return P(*L) - def fundamental_group(self, simplified=True, vertical=True, projective=False): + def fundamental_group(self, simplified=True, vertical=True, + projective=False): r""" The fundamental group of the complement of the union of affine plane curves in `\mathbb{C}^2`. @@ -450,7 +460,8 @@ def fundamental_group(self, simplified=True, vertical=True, projective=False): return self._fundamental_group_simplified if self._fundamental_group_vertical and not simplified and vertical: return self._fundamental_group_vertical - if self._fundamental_group_vertical_simplified and simplified and vertical: + if self._fundamental_group_vertical_simplified and simplified \ + and vertical: return self._fundamental_group_vertical_simplified K = self.base_ring() R = self.coordinate_ring() @@ -463,10 +474,15 @@ def fundamental_group(self, simplified=True, vertical=True, projective=False): bd = (self._braid_monodromy, self._strands, dict(), d1) elif vertical and self._braid_monodromy_with_vertical is not None: d1 = prod(L).degree(R.gen(1)) - bd = (self._braid_monodromy_with_vertical, self._strands, self._vertical_lines_in_braid_mon, d1) + bd = (self._braid_monodromy_with_vertical, self._strands, + self._vertical_lines_in_braid_mon, d1) else: bd = None - G, dic = fundamental_group_arrangement(L, simplified=simplified, puiseux=True, projective=projective, vertical=vertical, braid_data=bd) + G, dic = fundamental_group_arrangement(L, simplified=simplified, + puiseux=True, + projective=projective, + vertical=vertical, + braid_data=bd) if not vertical and not simplified: self._fundamental_group = G self._meridians = dic @@ -515,7 +531,8 @@ def meridians(self, simplified=True, vertical=True): else: computed = bool(self._meridians_vertical_simplified) if not computed: - _ = self._fundamental_group(simplified=simplified, vertical=vertical) + _ = self._fundamental_group(simplified=simplified, + vertical=vertical) if not vertical and not simplified: return self._meridians if simplified and not vertical: @@ -568,7 +585,8 @@ def braid_monodromy(self, vertical=True): if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') L = self.defining_polynomials() - bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical) + bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, + vertical=vertical) if vertical: self._braid_monodromy_with_vertical = bm self._strands_with_vertical = dic @@ -584,7 +602,8 @@ def strands(self): OUTPUT: - A dictionnary which associates to the index of each strand its associated component. + A dictionnary which associates to the index of each strand + its associated component. EXAMPLES:: @@ -609,7 +628,8 @@ def vertical_strands(self): OUTPUT: - A dictionnary which associates to the index of each strand its associated component. + A dictionnary which associates to the index of each strand + its associated component. EXAMPLES:: @@ -625,7 +645,7 @@ def vertical_strands(self): This functionality requires the sirocco package to be installed. """ if not self._strands_with_vertical: - self._braid_monodromy_with_vertical = self.braid_monodromy(vertical=True) + self.braid_monodromy(vertical=True) return self._strands_with_vertical def vertical_lines_in_braid_mon(self): @@ -650,7 +670,7 @@ def vertical_lines_in_braid_mon(self): This functionality requires the sirocco package to be installed. """ if not self._vertical_lines_in_braid_mon: - self._braid_monodromy_with_vertical = self.braid_monodromy(vertical=True) + self.braid_monodromy(vertical=True) return self._vertical_lines_in_braid_mon @@ -902,7 +922,6 @@ def gen(self, i): class ProjectivePlaneCurveArrangementsElement(Element): - # class ProjectivePlaneCurveArrangementsElement(AffinePlaneCurveArrangementsElement): """ An ordered projective plane curve arrangement. """ @@ -931,7 +950,8 @@ def __init__(self, parent, curves, check=True): raise ValueError("the curves must be given as a tuple") if not all(isinstance(h, ProjectivePlaneCurve) for h in curves): raise ValueError("not all elements are curves") - if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): + if not all(h.ambient_space() is self.parent().ambient_space() + for h in curves): raise ValueError("not all curves are in the same ambient space") # Añadir más atributos con opciones self._fundamental_group_simplified = None @@ -1034,9 +1054,12 @@ def _repr_(self): if len(self) == 0: return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) elif len(self) < 5: - curves = ', '.join(h.defining_polynomial()._repr_() for h in self._curves) - return 'Arrangement ({0}) in {1}'.format(curves, self.parent().ambient_space()) - return 'Arrangement of {0} curves in {1}'.format(len(self), self.parent().ambient_space()) + curves = ', '.join(h.defining_polynomial()._repr_() + for h in self._curves) + return 'Arrangement ({0}) in {1}'.format(curves, + self.parent().ambient_space()) + return 'Arrangement of {0} curves in {1}'.format(len(self), + self.parent().ambient_space()) def _richcmp_(self, other, op): """ @@ -1232,6 +1255,10 @@ def reduce(self): """ P = self.parent() L = [c.defining_polynomial().radical() for c in self] + for f1, f2 in Combinations(L, 2): + if f1.gcd(f2) != 1: + print("Some curves have common components") + return None return P(*L) def fundamental_group(self, simplified=True): @@ -1251,16 +1278,18 @@ def fundamental_group(self, simplified=True): EXAMPLES:: sage: # needs sirocco - sage: H. = AffinePlaneCurveArrangements(QQ) - sage: A = H(y^2 + x, y + x - 1, x) + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x*z, y + x - z, x) sage: A.fundamental_group() - Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, - x1*x0*x1^-1*x0^-1, - (x2*x1)^2*(x2^-1*x1^-1)^2 > - sage: A.fundamental_group(vertical=True) - Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, - x1*x0*x1^-1*x0^-1, - (x2*x1)^2*(x2^-1*x1^-1)^2 > + Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > + sage: A = H(y^2 + x*z, z, x) + sage: A.fundamental_group() + Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > + sage: A = H(y^2 + x*z, z*x, y) + sage: A.fundamental_group() + Finitely presented group + < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, + x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > .. WARNING:: @@ -1329,20 +1358,30 @@ def meridians(self, simplified=True): OUTPUT: - A dictionnary which associates the index of each curve with its meridians, - including the line at infinity if it can be easily computed + A dictionnary which associates the index of each curve with + its meridians, including the line at infinity if it can be + easily computed EXAMPLES:: sage: # needs sirocco - sage: H. = AffinePlaneCurveArrangements(QQ) - sage: A = H(y^2 + x, y + x - 1, x) - sage: G = A.fundamental_group(); G - Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, - x1*x0*x1^-1*x0^-1, - (x2*x1)^2*(x2^-1*x1^-1)^2 > + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x*z, y + x - z, x) + sage: A.fundamental_group() + Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > sage: A.meridians() - {0: [x1, x2*x1*x2^-1], 1: [x0], 2: [x2], 3: [x1^-1*x2^-1*x1^-1*x0^-1]} + {0: [x1], 1: [x1^-1*x0^-1*x1^-1], 2: [x0]} + sage: A = H(y^2 + x*z, z, x) + sage: A.fundamental_group() + Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > + sage: A.meridians() + {0: [x0, x1*x0*x1^-1], 1: [x0^-1*x1^-1*x0^-1], 2: [x1]} + sage: A = H(y^2 + x*z, z*x, y) + sage: A.fundamental_group() + Finitely presented group < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, + x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > + sage: A.meridians() + {0: [x0, x2*x0*x2^-1], 1: [x2, x0^-1*x2^-1*x1^-1*x0^-1], 2: [x1]} .. WARNING:: @@ -1360,7 +1399,6 @@ def meridians(self, simplified=True): class ProjectivePlaneCurveArrangements(Parent, UniqueRepresentation): - # class ProjectivePlaneCurveArrangements(AffinePlaneCurveArrangements): """ Curve arrangements. diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index b15a2ac0367..b3b8c2e4c57 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -8,7 +8,7 @@ The current implementation allows to compute a presentation of the fundamental group of curves over the rationals or number fields with -a fixed embedding on `\QQbar`. +a fixed embedding on `\overline{\mathbb{Q}}`. Instead of computing a representation of the braid monodromy, we choose several base points and a system of paths joining them that @@ -66,7 +66,6 @@ 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 .constructor import Curve roots_interval_cache = dict() @@ -109,8 +108,8 @@ def braid_from_piecewise(strands): yauxi = val[indices[j]][2] aaux = val[indices[j] - 1][0] baux = val[indices[j]][0] - interpolar = xauxr + (yauxr - xauxr) * (i - aaux) / (baux - aaux) - interpolai = xauxi + (yauxi - xauxi) * (i - aaux) / (baux - aaux) + interpolar = xauxr + (yauxr - xauxr)*(i - aaux) / (baux - aaux) + interpolai = xauxi + (yauxi - xauxi)*(i - aaux) / (baux - aaux) totalpoints[j].append([interpolar, interpolai]) else: totalpoints[j].append([val[indices[j]][1], @@ -141,7 +140,7 @@ def sgn(x, y): l1j = l1[j] for k in range(j): if l2j < l2[k]: - t = (l1j[0] - l1[k][0]) / ((l2[k][0] - l2j[0]) + (l1j[0] - l1[k][0])) + t = (l1j[0]-l1[k][0])/((l2[k][0] - l2j[0]) + (l1j[0]-l1[k][0])) s = sgn(l1[k][1] * (1 - t) + t * l2[k][1], l1j[1] * (1 - t) + t * l2j[1]) cruces.append([t, k, j, s]) @@ -177,11 +176,11 @@ def discrim(pols): INPUT: - ``pols`` -- a list or tuple of polynomials in two variables with - coefficients in a number field with a fixed embedding in `\QQbar` + coefficients in a number field with a fixed embedding in `\overline{\mathbb{Q}}`. OUTPUT: - A tuple with the roots of the discriminant in `\QQbar`. + A tuple with the roots of the discriminant in `\overline{\mathbb{Q}}`. EXAMPLES:: @@ -209,7 +208,8 @@ def discrim_pairs(f, g): return pol_ring(f.discriminant(y)) return pol_ring(f.resultant(g, y)) - pairs = [(f, None) for f in flist] + [(f, g) for f, g in Combinations(flist, 2)] + pairs = [(f, None) for f in flist] + [(f, g) for f, g + in Combinations(flist, 2)] fdiscrim = discrim_pairs(pairs) rts = () poly = 1 @@ -269,7 +269,8 @@ def corrected_voronoi_diagram(points): V = VoronoiDiagram(configuration) valid = True for r in V.regions().items(): - if not r[1].rays() and not r[1].interior_contains(apprpoints[r[0].affine()]): + if not r[1].rays() and \ + not r[1].interior_contains(apprpoints[r[0].affine()]): prec += 53 valid = False break @@ -287,12 +288,12 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False): - ``circuit`` -- a circuit in the graph of a Voronoi Diagram, given by a list of edges - - ``convex`` -- boolean (default: ``False``), if set to ``True`` a simpler + - ``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 + - ``verbose`` -- boolean (default: ``False``); for testing purposes OUTPUT: @@ -356,7 +357,6 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False): # return circuit return circuit_vertex elif pr < 0: - # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) return tuple(reversed(circuit_vertex)) prec = precision while True: @@ -364,10 +364,8 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False): totalangle = sum((CIF(*vectors[i]) / CIF(*vectors[i - 1])).argument() for i in range(len(vectors))) if totalangle < 0: - # return list(reversed([(c[1], c[0]) + c[2:] for c in circuit])) return tuple(reversed(circuit_vertex)) if totalangle > 0: - # return circuit return circuit_vertex prec *= 2 if verbose: @@ -383,7 +381,8 @@ def voronoi_cells(V, vertical_lines=[]): - ``V`` -- a corrected Voronoi diagram - - ``vertical_lines`` -- list (default: ``[]``) indices of the vertical lines + - ``vertical_lines`` -- list (default: ``[]``); indices of the + vertical lines OUTPUT: @@ -394,7 +393,8 @@ def voronoi_cells(V, vertical_lines=[]): 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. - - ``vertical_regions`` -- dictionnary for the regions associated with vertical lines. + - ``vertical_regions`` -- dictionnary for the regions associated + with vertical lines. EXAMPLES:: @@ -460,19 +460,20 @@ def voronoi_cells(V, vertical_lines=[]): regions = V.regions() points = [p for p in V.regions().keys() if V.regions()[p].is_compact()] compact_regions = [regions[p] for p in points] - vertical_regions = dict() # {j: regions[points[j]] for j in vertical_lines} - 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') + vertical_regions = dict() + non_compact_regions = [_ for _ in V.regions().values() + if not _.is_compact()] + G = Graph([u.vertices() for v in compact_regions for u in v.faces(1)], + format='list_of_edges') + E = Graph([u.vertices() for v in non_compact_regions for u in v.faces(1) + if u.is_compact()], format='list_of_edges') p = next(E.vertex_iterator()) EC = orient_circuit(E.eulerian_circuit()) - # EC = [EC0[0][0]] + [e[1] for e in EC0] DG = Graph() for i, reg in enumerate(compact_regions): Greg0 = orient_circuit(reg.graph().eulerian_circuit(), convex=True) if i in vertical_lines: vertical_regions[i] = Greg0 - # 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] @@ -566,16 +567,19 @@ def followstrand(f, factors, x0, x1, y0a, prec=53): ci = c.imag() coefsfactors += list(cr.endpoints()) coefsfactors += list(ci.endpoints()) - from sage.libs.sirocco import contpath, contpath_mp, contpath_comps, contpath_mp_comps + from sage.libs.sirocco import contpath, contpath_mp, contpath_comps + from sage.libs.sirocco import contpath_mp_comps try: if prec == 53: if factors: - points = contpath_comps(deg, coefs, yr, yi, degsfactors, coefsfactors) + points = contpath_comps(deg, coefs, yr, yi, + degsfactors, coefsfactors) else: points = contpath(deg, coefs, yr, yi) else: if factors: - points = contpath_mp_comps(deg, coefs, yr, yi, prec, degsfactors, coefsfactors) + points = contpath_mp_comps(deg, coefs, yr, yi, prec, + degsfactors, coefsfactors) else: points = contpath_mp(deg, coefs, yr, yi, prec) return points @@ -870,8 +874,8 @@ def braid_in_segment(glist, x0, x1, precision=dict()): F1 = g.base_ring() x, y = g.parent().gens() intervals = {} - if not precision1: # new - precision1 = {f: 53 for f in glist} # new + if not precision1: + precision1 = {f: 53 for f in glist} y0s = [] for f in glist: if f.variables() == (y,): @@ -883,15 +887,18 @@ def braid_in_segment(glist, x0, x1, precision=dict()): while True: 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)): + if not any(a.overlaps(b) for a, b in + itertools.combinations(intervals[f], 2)): break precision1[f] *= 2 strands = [] for f in glist: for i in intervals[f]: - aux = followstrand(f, [p for p in glist if p != f], x0, x1, i.center(), precision1[f]) + 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] + 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 = [] @@ -904,20 +911,22 @@ def braid_in_segment(glist, x0, x1, precision=dict()): 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])]) + initialstrands.append([(0, center.real(), center.imag()), + (1, cs[0][1], cs[0][2])]) matched += 1 if matched != 1: - precision1 = {f: precision1[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision=precision1) # new + precision1 = {f: precision1[f] * 2 for f in glist} + return braid_in_segment(glist, x0, x1, precision=precision1) 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())]) + finalstrands.append([(0, cs[-1][1], cs[-1][2]), + (1, center.real(), center.imag())]) matched += 1 if matched != 1: - precision1 = {f: precision1[f] * 2 for f in glist} # new - return braid_in_segment(glist, x0, x1, precision=precision1) # new + precision1 = {f: precision1[f] * 2 for f in glist} + return braid_in_segment(glist, x0, x1, precision=precision1) initialbraid = braid_from_piecewise(initialstrands) finalbraid = braid_from_piecewise(finalstrands) @@ -945,8 +954,8 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): by a tuple whose first element is the an integer for the position and the second one is the cyclic ordered list of vertices in the region. - - ``vertical_regions`` -- dictionary with keys the vertices of ``dual_graph`` - to fix regions associated with vertical lines + - ``vertical_regions`` -- dictionary with keys the vertices of + ``dual_graph`` to fix regions associated with vertical lines OUTPUT: A geometric basis and a dictionnary. @@ -984,7 +993,9 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): {0: 0, 1: 3, 2: 1, 3: 2, 4: 4} """ i = EC0.index(p) - EC = EC0[i:-1] + EC0[:i + 1] # A counterclockwise eulerian circuit on the boundary, starting and ending at 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(): j = list(dual_graph.vertices())[0][0] @@ -993,29 +1004,34 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): else: vd = dict() return [EC], vd - 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) + Intern = 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, sort=True) and v != ECi] + if ECi in Intern: + EI = [v for v in E if v in + Intern.connected_component_containing_vertex(ECi, sort=True) + and v != ECi] if len(EI) > 0: q = ECi connecting_path = list(EC[:i]) break - if EC[-i] in Internal: - EI = [v for v in E if v in Internal.connected_component_containing_vertex(EC[-i], sort=True) and v != EC[-i]] + if EC[-i] in Intern: + EI = [v for v in E if v in + Intern.connected_component_containing_vertex(EC[-i], + sort=True) + and v != EC[-i]] if len(EI) > 0: q = EC[-i] connecting_path = list(reversed(EC[-i:])) break # Precompute distances from q in E and I E_dist_q = E.shortest_path_lengths(q) - I_dist_q = Internal.shortest_path_lengths(q) + I_dist_q = Intern.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) + cutpath = Intern.shortest_path(q, r) for i, v in enumerate(cutpath): if i > 0 and v in EC: r = v @@ -1047,7 +1063,8 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): E1.add_edge(cutpath[i], cutpath[i + 1], None) E2.add_edge(cutpath[i], cutpath[i + 1], None) Gd = copy(dual_graph) - to_delete = [e for e in Gd.edges(sort=True) if e[2][0] in cutpath and e[2][1] in cutpath] + 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 = [] @@ -1055,16 +1072,20 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): for reg in Gd2.vertices(sort=True): vertices_2 += reg[1][:-1] reg_circuit = reg[1] - edges_2 += [(v1, reg_circuit[i + 1]) for i, v1 in enumerate(reg_circuit[:-1])] - edges_2 += [(v1, reg_circuit[i - 1]) for i, v1 in enumerate(reg_circuit[1:])] + 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:])] + 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 @@ -1077,11 +1098,6 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): EC1 = list(EC[qi:] + EC[1:ri]) + list(reversed(cutpath)) EC2 = cutpath + list(EC[ri + 1:qi + 1]) - # regs1 = [v[1] for v in Gd1.vertices()] - # VR1 = {p: vertical_regions[p] for p in vertical_regions.keys() if p in regs1} - # regs2 = [v[1] for v in Gd2.vertices()] - # VR2 = {p: vertical_regions[p] for p in vertical_regions.keys() if p in regs2} - gb1, vd1 = geometric_basis(G1, E1, EC1, q, Gd1, vertical_regions) gb2, vd2 = geometric_basis(G2, E2, EC2, q, Gd2, vertical_regions) @@ -1161,9 +1177,10 @@ def strand_components(f, flist, p1): OUTPUT: - A list and a dictionary. The first one is an ordered list of pairs - consisting of ``(z,i)`` where ``z`` is a root of ``f(p_1,y)`` and `i` is the position - of the polynomial in the list whose root is ``z``. The second one attaches - a number `i` (strand) to a number `j` (a polynomial in the list). + 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:: @@ -1200,14 +1217,15 @@ def braid_monodromy(f, arrangement=(), vertical=False): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers - - ``arrangement`` -- tuple (default: ``[]``). An optional tuple of polynomials whose product - equals ``f``. + - ``arrangement`` -- tuple (default: ``[]``); an optional tuple + of polynomials whose product equals ``f``. - - `vertical` .. boolean (default: ``False`). If set to ``True``, ``arrangements`` - contains more than one polynomial, some of them is of degree `1` in `x` - and degree `0` in `y`, and none of the other components have vertical asymptotes, - then these components are marked and not used for - the computation of the braid monodromy. + - `vertical` .. boolean (default: ``False`). If set to ``True``, + ``arrangements`` contains more than one polynomial, some of them + are of degree `1` in `x` and degree `0` in `y`, and none of + the other components have vertical asymptotes, then these + components are marked and not used for the computation + of the braid monodromy. OUTPUT: @@ -1217,12 +1235,12 @@ def braid_monodromy(f, arrangement=(), vertical=False): - A dictionnary: ``i``, index of a strand is sent to the index of the corresponding factor in ``arrangement``. - - Another dictionnary, only relevant if ``vertical`` is ``True``. It attaches - the index of a vertical line in ``arrangement`` to the index of its - corresponding braid. + - Another dictionnary, only relevant if ``vertical`` is ``True``. + It attaches the index of a vertical line in ``arrangement`` + to the index of its corresponding braid. - - A non-negative integer: the number of strands of the braids, only necessary - if the list of braids is empty. + - A non-negative integer: the number of strands of the braids, + only necessary if the list of braids is empty. .. NOTE:: @@ -1269,8 +1287,10 @@ def braid_monodromy(f, arrangement=(), vertical=False): indices_v = vertical_lines_in_braidmon(arrangement1) else: indices_v = [] - arrangement_h = tuple(f0 for j, f0 in enumerate(arrangement1) if j not in indices_v) - arrangement_v = tuple(f0 for j, f0 in enumerate(arrangement1) if j in indices_v) + arrangement_h = tuple(f0 for j, f0 in enumerate(arrangement1) + if j not in indices_v) + arrangement_v = tuple(f0 for j, f0 in enumerate(arrangement1) + if j in indices_v) glist = tuple(_[0] for f0 in arrangement_h for _ in f0.factor()) g = f.parent()(prod(glist)) d = g.degree(y) @@ -1289,7 +1309,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): transversal = dict() vl = [] for f0 in arrangement_v: - pt = [j for j, t in enumerate(disc) if f0(x=t) == 0] + pt = [j for j, t in enumerate(disc) if f0.subs({x: t}) == 0] if pt: vertical_braid[f0] = (pt[0], arrangement1.index(f0)) vl.append(pt[0]) @@ -1297,7 +1317,8 @@ def braid_monodromy(f, arrangement=(), vertical=False): transversal[f0] = arrangement1.index(f0) vl.sort() if not disc: - vertical_braids = {i + d: transversal[f0] for i, f0 in enumerate(transversal)} + vertical_braids = {i + d: transversal[f0] + for i, f0 in enumerate(transversal)} if d > 1: result = [BraidGroup(d).one() for p in transversal] else: @@ -1349,7 +1370,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): segsbraids[(beginseg, endseg)] = b segsbraids[(endseg, beginseg)] = b.inverse() end_braid_computation = True - except ChildProcessError: # ChildProcessError: # hack to deal with random fails first time + except ChildProcessError: # hack to deal with random fails first time pass B = BraidGroup(d) result = [] @@ -1524,7 +1545,9 @@ def relation(x, b): return x * b / x -def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projective=False, puiseux=False, vertical=[]): +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. @@ -1637,7 +1660,8 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): INPUT: - ``f`` -- a polynomial in two variables, with coefficients in either - the rationals or a number field with a fixed embedding in `\QQbar` + the rationals or a number field with a fixed embedding in + `\overline{\mathbb{Q}}` - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the presentation will be simplified (see below) @@ -1687,7 +1711,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): 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`:: + fixed embedding in `\overline{\mathbb{Q}}`:: sage: # optional - sirocco sage: from sage.schemes.curves.zariski_vankampen import fundamental_group @@ -1702,7 +1726,8 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): sage: fundamental_group(f) Finitely presented group < x0 | > - We compute the fundamental group of the complement of a quartic using the ``puiseux`` option:: + We compute the fundamental group of the complement of a + quartic using the ``puiseux`` option:: sage: # optional - sirocco sage: from sage.schemes.curves.zariski_vankampen import fundamental_group @@ -1735,10 +1760,15 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): d = g.degree(y) else: d = bm[0].parent().strands() - return fundamental_group_from_braid_mon(bm, degree=d, simplified=simplified, projective=projective, puiseux=puiseux) + 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, vertical=False, braid_data=None): +def fundamental_group_arrangement(flist, simplified=True, projective=False, + puiseux=False, vertical=False, + braid_data=None): r""" Compute the fundamental group of the complement of a curve defined by a list of polynomials with the extra information @@ -1846,7 +1876,8 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis flist1 = [g.subs({x: x + y}) for g in flist1] f = prod(flist1) if not vertical0: - infinity = all([g.degree(y) == g.degree() or Curve(g).is_vertical_line() for g in flist1]) + infinity = all([g.degree(y) == g.degree() or + Curve(g).is_vertical_line() for g in flist1]) if vertical0: infinity = all([not Curve(g).has_vertical_asymptote() for g in flist1]) if braid_data: @@ -1865,7 +1896,10 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, puis # if vertical0: # for j in dv: # dic[d1 + j] = dv[j] - g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False, projective=projective, puiseux=puiseux, vertical=vert_lines) + g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False, + projective=projective, + puiseux=puiseux, + vertical=vert_lines) if simplified: hom = g.simplification_isomorphism() else: From 11a3fcd8e36098f6a874dad5f39222ffa82f93e6 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 24 Nov 2023 14:56:10 +0100 Subject: [PATCH 49/95] cleaning classes and preparing arrangements --- .../hyperplane_arrangement/arrangement.py | 62 +- .../schemes/curves/plane_curve_arrangement.py | 1393 +++++++++-------- 2 files changed, 764 insertions(+), 691 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 9972b646059..bc6ad7e7bba 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3669,6 +3669,54 @@ class OrderedHyperplaneArrangementElement(HyperplaneArrangementElement): always use the parent. """ + def __init__(self, parent, hyperplanes, check=True, backend=None): + """ + Construct an ordered hyperplane arrangement. + + INPUT: + + - ``parent`` -- the parent :class:`OrderedHyperplaneArrangements` + + - ``hyperplanes`` -- a tuple of hyperplanes + + - ``check`` -- boolean (optional; default ``True``); whether + to check input + + - ``backend`` -- string (optional; default: ``None``); the backend to + use for the related polyhedral objects + + EXAMPLES:: + + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: elt = H(x, y); elt + Arrangement + sage: TestSuite(elt).run() + + It is possible to specify a backend for polyhedral computations:: + + sage: # needs sage.rings.number_field + sage: R. = QuadraticField(5) + sage: H = OrderedHyperplaneArrangements(R, names='xyz') + sage: x, y, z = H.gens() + sage: A = H(sqrt5*x + 2*y + 3*z, backend='normaliz') + sage: A.backend() + 'normaliz' + sage: A.regions()[0].backend() # optional - pynormaliz + 'normaliz' + """ + super().__init__(parent, hyperplanes, check=check, backend=backend) + # self._hyperplanes = hyperplanes + # self._backend = backend + # if check: + # if not isinstance(hyperplanes, tuple): + # raise ValueError("the hyperplanes must be given as a tuple") + # if not all(isinstance(h, Hyperplane) for h in hyperplanes): + # raise ValueError("not all elements are hyperplanes") + # if not all(h.parent() is self.parent().ambient_space() for h in hyperplanes): + # raise ValueError("not all hyperplanes are in the ambient space") + self._fundamental_group_ = None + self._meridians_ = None + def hyperplane_section(self, proj=True): r""" It computes a generic hyperplane section of ``self``, an arrangement @@ -3761,7 +3809,7 @@ def hyperplane_section(self, proj=True): H1 = self.add_hyperplane(h0) return H1.restriction(h0) - def _fundamental_group_(self, proj=False): + def _plane_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 @@ -3783,7 +3831,7 @@ def _fundamental_group_(self, proj=False): sage: A. = OrderedHyperplaneArrangements(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._plane_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) @@ -3792,7 +3840,7 @@ def _fundamental_group_(self, proj=False): Hyperplane x + 0*y + 1, Hyperplane 0*x + y + 1, Hyperplane x - y + 0] - sage: G, dic = H._fundamental_group_() # optional - sirocco + sage: G, dic = H._plane_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, @@ -3803,14 +3851,14 @@ def _fundamental_group_(self, proj=False): sage: dic # optional - sirocco {0: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} sage: H=A(x,y,x+y) - sage: H._fundamental_group_() # optional - sirocco + sage: H._plane_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: [x0], 1: [x2], 2: [x1], 3: [x2^-1*x1^-1*x0^-1]}) - sage: H._fundamental_group_(proj=True) # optional - sirocco + sage: H._plane_fundamental_group_(proj=True) # optional - sirocco (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) sage: A3. = OrderedHyperplaneArrangements(QQ) sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) # optional - sage.graphs - sage: G, dic = H._fundamental_group_(proj=True) # optional - sage.graphs, sirocco + sage: G, dic = H._plane_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, @@ -3937,7 +3985,7 @@ def fundamental_group(self, projective=False): """ n = self.dimension() if n <= 2 or (n == 3 and projective): - return self._fundamental_group_(proj=projective) + return self._plane_fundamental_group_(proj=projective) H1 = self.hyperplane_section(proj=projective) H2, dic = H1.fundamental_group(projective=projective) return (H2, dic) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 242ee1c0667..a0d141ff088 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -378,7 +378,7 @@ def defining_polynomial(self, simplified=True): def have_common_factors(self): r""" - Check if th curves have common factors. + Check if the curves have common factors. EXAMPLES:: @@ -394,10 +394,17 @@ def have_common_factors(self): return True return False - def reduce(self): + def reduce(self, clean=False): r""" Replace the curves by their reduction. + INPUT: + + - ``clean`` -- boolean (default: False); if ``False`` + and there are common factors it returns ``None`` and + a warning message. If ``True``, the common factors are kept + only in the first occurance. + EXAMPLES:: sage: H. = AffinePlaneCurveArrangements(QQ) @@ -407,11 +414,17 @@ def reduce(self): of dimension 2 over Rational Field """ P = self.parent() - L = [c.defining_polynomial().radical() for c in self] - for f1, f2 in Combinations(L, 2): - if f1.gcd(f2) != 1: - print("Some curves have common components") - return None + R = self.coordinate_ring() + L = [self[0].defining_polynomial().radical()] + for c in self[1:]: + g = c.defining_polynomial().radical() + for f in L: + d = g.gcd(f) + if d.degree() > 0 and not clean: + print("Some curves have common components") + return None + g = R(g / d) + L.append(g) return P(*L) def fundamental_group(self, simplified=True, vertical=True, @@ -674,6 +687,483 @@ def vertical_lines_in_braid_mon(self): return self._vertical_lines_in_braid_mon +class ProjectivePlaneCurveArrangementsElement(AffinePlaneCurveArrangementsElement): + """ + An ordered projective plane curve arrangement. + """ + + def __init__(self, parent, curves, check=True): + """ + Construct an ordered projective plane curve arrangement. + + INPUT: + + - ``parent`` -- the parent :class:`ProjectivePlaneCurveArrangements` + + - ``curves`` -- a tuple of curves + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: elt = H(x, y, z); elt + Arrangement (x, y, z) in Projective Space of dimension 2 over Rational Field + sage: TestSuite(elt).run() + """ + Element.__init__(self, parent) + self._curves = curves + if check: + if not isinstance(curves, tuple): + raise ValueError("the curves must be given as a tuple") + if not all(isinstance(h, ProjectivePlaneCurve) for h in curves): + raise ValueError("not all elements are curves") + if not all(h.ambient_space() is self.parent().ambient_space() + for h in curves): + raise ValueError("not all curves are in the same ambient space") + # Añadir más atributos con opciones + self._fundamental_group_simplified = None + self._meridians_simplified = dict() + self._fundamental_group = None + self._meridians = dict() + + # def __getitem__(self, i): + # """ + # Return the `i`-th curve. + # + # INPUT: + # + # - ``i`` -- integer + # + # OUTPUT: + # + # The `i`-th curve. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: h = H(y^2 - x * z, y^3 + 2 * x^2 * z, x^4 + y^4 + z^4); h + # Arrangement (y^2 - x*z, y^3 + 2*x^2*z, x^4 + y^4 + z^4) + # in Projective Space of dimension 2 over Rational Field + # """ + # return self._curves[i] + # + # def __hash__(self): + # r""" + # TESTS:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: h = H((x * y, x + y + z)) + # sage: len_dict = {h: len(h)} + # """ + # return hash(self.curves()) + # + # def n_curves(self): + # r""" + # Return the number of curves in the arrangement. + # + # OUTPUT: + # + # An integer. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: h = H((x * y, x + y + z)) + # sage: h.n_curves() + # 2 + # sage: len(h) # equivalent + # 2 + # """ + # return len(self._curves) + # + # __len__ = n_curves + # + # def curves(self): + # r""" + # Return the curves in the arrangement as a tuple. + # + # OUTPUT: + # + # A tuple. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: h = H((x * y, x + y + z)) + # sage: h.curves() + # (Projective Conic Curve over Rational Field defined by x*y, + # Projective Plane Curve over Rational Field defined by x + y + z) + # + # Note that the hyperplanes can be indexed as if they were a list:: + # + # sage: h[1] + # Projective Plane Curve over Rational Field defined by x + y + z + # """ + # return self._curves + # + # def _repr_(self): + # r""" + # String representation for a curve arrangement. + # + # OUTPUT: + # + # A string. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) + # sage: h + # Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field + # sage: H(()) + # Empty curve arrangement in Projective Space of dimension 2 over Rational Field + # """ + # if len(self) == 0: + # return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) + # elif len(self) < 5: + # curves = ', '.join(h.defining_polynomial()._repr_() + # for h in self._curves) + # return 'Arrangement ({0}) in {1}'.format(curves, + # self.parent().ambient_space()) + # return 'Arrangement of {0} curves in {1}'.format(len(self), + # self.parent().ambient_space()) + # + # def _richcmp_(self, other, op): + # """ + # Compare two curve arrangements. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: H(x) == H(y) + # False + # sage: H(x) == H(2 * x) + # True + # + # TESTS:: + # + # sage: H(x) == 0 + # False + # """ + # return richcmp(self._curves, other._curves, op) + # + # def union(self, other): + # r""" + # The union of ``self`` with ``other``. + # + # INPUT: + # + # - ``other`` -- a curve arrangement or something that can + # be converted into a curve arrangement + # + # OUTPUT: + # + # A new curve arrangement. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) + # sage: C = Curve(x^8 - y^8 -x^4 * y^4) + # sage: h1 = h.union(C); h1 + # Arrangement of 6 curves in Projective Space of dimension 2 over Rational Field + # sage: h1 == h1.union(C) + # Repeated curve + # True + # """ + # P = self.parent() + # other_h = P(other) + # curves0 = self._curves + other_h._curves + # curves = () + # for h in curves0: + # if h not in curves: + # curves += (h, ) + # else: + # print("Repeated curve") + # result = P(*curves) + # return result + # + # add_curves = union + # + # __or__ = union + # + # def deletion(self, curves): + # r""" + # Return the curve arrangement obtained by removing ``h``. + # + # INPUT: + # + # - ``h`` -- a curve or curve arrangement + # + # OUTPUT: + # + # A new curve arrangement with the given curve(s) + # ``h`` removed. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) + # sage: C = h[-1] + # sage: h.deletion(C) + # Arrangement (x*y, x + y + z, -y^5 + x^3*z^2, x^5 + y^5 + x^2*y^2*z) + # in Projective Space of dimension 2 over Rational Field + # """ + # parent = self.parent() + # curves = parent(curves) + # planes = list(self) + # for curve in curves: + # try: + # planes.remove(curve) + # except ValueError: + # raise ValueError('curve is not in the arrangement') + # return parent(planes) + # + # def change_ring(self, base_ring): + # """ + # Return curve arrangement over the new base ring. + # + # INPUT: + # + # - ``base_ring`` -- the new base ring; must be a field for + # curve arrangements + # + # OUTPUT: + # + # The curve arrangement obtained by changing the base + # field, as a new curve arrangement. + # + # EXAMPLES:: + # + # SAGE: # needs sage.rings.number_field + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: A = H(y^2*z - x^3, x, y, y^2 + x * y + x^2) + # sage: K. = CyclotomicField(3) + # sage: A.change_ring(K) + # Arrangement (-x^3 + y^2*z, x, y, x^2 + x*y + y^2) in Projective Space of + # dimension 2 over Cyclotomic Field of order 3 and degree 2 + # """ + # parent = self.parent().change_ring(base_ring) + # curves = tuple(c.change_ring(base_ring) for c in self) + # return parent(curves) + # + # def coordinate_ring(self): + # """ + # Return the coordinate ring of ``self``. + # + # OUTPUT: + # + # The base ring of the curve arrangement. + # + # EXAMPLES:: + # + # sage: L. = ProjectivePlaneCurveArrangements(QQ) + # sage: C = L(x, y) + # sage: C.coordinate_ring() + # Multivariate Polynomial Ring in x, y, z over Rational Field + # """ + # return self.curves()[0].defining_polynomial().parent() + # + # def defining_polynomials(self): + # r""" + # Return the defining polynomials of the elements of``self``. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: A = H(y^2*z - x^3, x, y, y^2 + x * y + x^2) + # sage: A.defining_polynomials() + # (-x^3 + y^2*z, x, y, x^2 + x*y + y^2) + # """ + # return tuple(h.defining_polynomial() for h in self) + # + # def defining_polynomial(self, simplified=True): + # r""" + # Return the defining polynomial of the union of the curves in ``self``. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: A = H(y ** 2 + x ** 2, x, y) + # sage: prod(A.defining_polynomials()) == A.defining_polynomial() + # True + # """ + # return prod(self.defining_polynomials()) + # + # def have_common_factors(self): + # r""" + # Check if the curves have common factors. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: A = H(x * y, x^2 * z^2 + x * y^3) + # sage: A.have_common_factors() + # True + # """ + # L = [c.defining_polynomial() for c in self] + # C = Combinations(L, 2) + # for f1, f2 in C: + # if f1.gcd(f2).degree() > 0: + # return True + # return False + # + # def reduce(self): + # r""" + # Replace the curves by their reduction. + # + # EXAMPLES:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2)) + # sage: A.reduce() + # Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Projective Space + # of dimension 2 over Rational Field + # """ + # P = self.parent() + # L = [c.defining_polynomial().radical() for c in self] + # for f1, f2 in Combinations(L, 2): + # if f1.gcd(f2) != 1: + # print("Some curves have common components") + # return None + # return P(*L) + # + def fundamental_group(self, simplified=True): + r""" + The fundamental group of the complement of the union + of projective plane curves in `\mathbb{P}^2`. + + INPUT: + + - ``simplified`` -- boolean (default: True); set if the group + is simplified.. + + OUTPUT: + + A finitely presented group. + + EXAMPLES:: + + sage: # needs sirocco + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x*z, y + x - z, x) + sage: A.fundamental_group() + Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > + sage: A = H(y^2 + x*z, z, x) + sage: A.fundamental_group() + Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > + sage: A = H(y^2 + x*z, z*x, y) + sage: A.fundamental_group() + Finitely presented group + < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, + x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if self._fundamental_group and not simplified: + return self._fundamental_group + if self._fundamental_group_simplified and simplified: + return self._fundamental_group_simplified + H = self.parent() + K = self.base_ring() + R = self.coordinate_ring() + x, y, z = R.gens() + if not K.is_subring(QQbar): + raise TypeError('the base field is not in QQbar') + C = self.reduce() + n = len(C) + infinity = Curve(z) + infinity_in_C = infinity in C + if infinity_in_C: + j = C.curves().index(infinity) + C = H(C.curves()[:j] + C.curves()[j + 1:]) + infinity_divides = False + for j, c in enumerate(C): + g = c.defining_polynomial() + infinity_divides = z.divides(g) + if infinity_divides: + h = R(g / z) + C = H(C.curves()[:j] + (h, ) + C.curves()[j + 1:]) + break + affine = AffinePlaneCurveArrangements(K, names=('u', 'v')) + u, v = affine.gens() + affines = [f.defining_polynomial()(x=u, y=v, z=1) for f in C] + changes = any([g.degree(v) < g.degree() > 1 for g in affines]) + while changes: + affines = [f(u=u + v) for f in affines] + changes = any([g.degree(v) < g.degree() > 1 for g in affines]) + C_affine = affine(affines) + proj = not (infinity_divides or infinity_in_C) + G = C_affine.fundamental_group(simplified=simplified, vertical=True, + projective=proj) + dic = C_affine.meridians(simplified=simplified, vertical=True) + if infinity_in_C: + dic1 = dict() + for k in range(j): + dic1[k] = dic[k] + dic1[j] = dic[n - 1] + for k in range(j + 1, n): + dic1[k] = dic[k - 1] + elif infinity_divides: + dic1 = {k: dic[k] for k in range(n)} + dic1[j] += dic[n] + else: + dic1 = dic + if not simplified: + self._fundamental_group = G + self._meridians = dic1 + else: + self._fundamental_group_simplified = G + self._meridians_simplified = dic1 + return G + + def meridians(self, simplified=True): + r""" + Meridians of each irreducible component. + + OUTPUT: + + A dictionnary which associates the index of each curve with + its meridians, including the line at infinity if it can be + easily computed + + EXAMPLES:: + + sage: # needs sirocco + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: A = H(y^2 + x*z, y + x - z, x) + sage: A.fundamental_group() + Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > + sage: A.meridians() + {0: [x1], 1: [x1^-1*x0^-1*x1^-1], 2: [x0]} + sage: A = H(y^2 + x*z, z, x) + sage: A.fundamental_group() + Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > + sage: A.meridians() + {0: [x0, x1*x0*x1^-1], 1: [x0^-1*x1^-1*x0^-1], 2: [x1]} + sage: A = H(y^2 + x*z, z*x, y) + sage: A.fundamental_group() + Finitely presented group < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, + x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > + sage: A.meridians() + {0: [x0, x2*x0*x2^-1], 1: [x2, x0^-1*x2^-1*x1^-1*x0^-1], 2: [x1]} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not simplified: + computed = bool(self._meridians) + else: + computed = bool(self._meridians_simplified) + if not computed: + _ = self._fundamental_group(simplified=simplified) + if not simplified: + return self._meridians + return self._meridians_simplified + + class AffinePlaneCurveArrangements(Parent, UniqueRepresentation): """ Curve arrangements. @@ -862,6 +1352,9 @@ def _an_element_(self): sage: H. = AffinePlaneCurveArrangements(QQ) sage: H._an_element_() Arrangement (t) in Affine Space of dimension 2 over Rational Field + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: H._an_element_() + Arrangement (t) in Projective Space of dimension 2 over Rational Field """ x = self.gen(0) return self(x) @@ -869,17 +1362,20 @@ def _an_element_(self): @cached_method def ngens(self): """ - Return the number of variables, i.e. 2, kept for completness. + Return the number of variables, i.e. 2 or 3, kept for completness. OUTPUT: - An integer (2). + An integer, 2 or 3, depending if the arrangement is projective or affine. EXAMPLES:: sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.ngens() 2 + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.ngens() + 3 """ return len(self._names) @@ -897,6 +1393,9 @@ def gens(self): sage: L = AffinePlaneCurveArrangements(QQ, ('x', 'y')) sage: L.gens() (x, y) + sage: L = ProjectivePlaneCurveArrangements(QQ, ('x', 'y', 'z')) + sage: L.gens() + (x, y, z) """ return self.ambient_space().gens() @@ -917,574 +1416,100 @@ def gen(self, i): sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.gen(1) y + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.gen(2) + z """ return self.gens()[i] -class ProjectivePlaneCurveArrangementsElement(Element): +class ProjectivePlaneCurveArrangements(AffinePlaneCurveArrangements): """ - An ordered projective plane curve arrangement. + Curve arrangements. + + INPUT: + + - ``base_ring`` -- ring; the base ring + + - ``names`` -- tuple of strings; the variable names + + EXAMPLES:: + + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: H(x, y^2, x-z, y-z) + Arrangement (x, y^2, x - z, y - z) in Projective Space + of dimension 2 over Rational Field """ + Element = ProjectivePlaneCurveArrangementsElement - def __init__(self, parent, curves, check=True): + def __init__(self, base_ring, names=tuple()): """ - Construct an ordered projective plane curve arrangement. - - INPUT: + Initialize ``self``. - - ``parent`` -- the parent :class:`ProjectivePlaneCurveArrangements` + TESTS:: - - ``curves`` -- a tuple of curves + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: K = ProjectivePlaneCurveArrangements(QQ, names=('x', 'y', 'z')) + sage: H is K + True + sage: type(K) + - EXAMPLES:: + TESTS:: sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: elt = H(x, y, z); elt - Arrangement (x, y, z) in Projective Space of dimension 2 over Rational Field - sage: TestSuite(elt).run() + sage: TestSuite(H).run() """ - super().__init__(parent) - self._curves = curves - if check: - if not isinstance(curves, tuple): - raise ValueError("the curves must be given as a tuple") - if not all(isinstance(h, ProjectivePlaneCurve) for h in curves): - raise ValueError("not all elements are curves") - if not all(h.ambient_space() is self.parent().ambient_space() - for h in curves): - raise ValueError("not all curves are in the same ambient space") - # Añadir más atributos con opciones - self._fundamental_group_simplified = None - self._meridians_simplified = dict() - self._fundamental_group = None - self._meridians = dict() + if base_ring not in _Fields: + raise ValueError('base ring must be a field') + super().__init__(base_ring, names=names) + self._base_ring = base_ring + self._names = names - def __getitem__(self, i): + # def base_ring(self): + # """ + # Return the base ring. + # + # OUTPUT: + # + # The base ring of the curve arrangement. + # + # EXAMPLES:: + # + # sage: L. = ProjectivePlaneCurveArrangements(QQ) + # sage: L.base_ring() + # Rational Field + # """ + # return self._base_ring + + # def coordinate_ring(self): + # """ + # Return the base ring. + # + # OUTPUT: + # + # The base ring of the curve arrangement. + # + # EXAMPLES:: + # + # sage: L. = ProjectivePlaneCurveArrangements(QQ) + # sage: L.base_ring() + # Rational Field + # """ + # return self._coordinate_ring + + def change_ring(self, base_ring): """ - Return the `i`-th curve. + Return curve arrangements over a different base ring. INPUT: - - ``i`` -- integer + - ``base_ring`` -- a ring; the new base ring. OUTPUT: - The `i`-th curve. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: h = H(y^2 - x * z, y^3 + 2 * x^2 * z, x^4 + y^4 + z^4); h - Arrangement (y^2 - x*z, y^3 + 2*x^2*z, x^4 + y^4 + z^4) - in Projective Space of dimension 2 over Rational Field - """ - return self._curves[i] - - def __hash__(self): - r""" - TESTS:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: h = H((x * y, x + y + z)) - sage: len_dict = {h: len(h)} - """ - return hash(self.curves()) - - def n_curves(self): - r""" - Return the number of curves in the arrangement. - - OUTPUT: - - An integer. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: h = H((x * y, x + y + z)) - sage: h.n_curves() - 2 - sage: len(h) # equivalent - 2 - """ - return len(self._curves) - - __len__ = n_curves - - def curves(self): - r""" - Return the curves in the arrangement as a tuple. - - OUTPUT: - - A tuple. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: h = H((x * y, x + y + z)) - sage: h.curves() - (Projective Conic Curve over Rational Field defined by x*y, - Projective Plane Curve over Rational Field defined by x + y + z) - - Note that the hyperplanes can be indexed as if they were a list:: - - sage: h[1] - Projective Plane Curve over Rational Field defined by x + y + z - """ - return self._curves - - def _repr_(self): - r""" - String representation for a curve arrangement. - - OUTPUT: - - A string. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) - sage: h - Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field - sage: H(()) - Empty curve arrangement in Projective Space of dimension 2 over Rational Field - """ - if len(self) == 0: - return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) - elif len(self) < 5: - curves = ', '.join(h.defining_polynomial()._repr_() - for h in self._curves) - return 'Arrangement ({0}) in {1}'.format(curves, - self.parent().ambient_space()) - return 'Arrangement of {0} curves in {1}'.format(len(self), - self.parent().ambient_space()) - - def _richcmp_(self, other, op): - """ - Compare two curve arrangements. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: H(x) == H(y) - False - sage: H(x) == H(2 * x) - True - - TESTS:: - - sage: H(x) == 0 - False - """ - return richcmp(self._curves, other._curves, op) - - def union(self, other): - r""" - The union of ``self`` with ``other``. - - INPUT: - - - ``other`` -- a curve arrangement or something that can - be converted into a curve arrangement - - OUTPUT: - - A new curve arrangement. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) - sage: C = Curve(x^8 - y^8 -x^4 * y^4) - sage: h1 = h.union(C); h1 - Arrangement of 6 curves in Projective Space of dimension 2 over Rational Field - sage: h1 == h1.union(C) - Repeated curve - True - """ - P = self.parent() - other_h = P(other) - curves0 = self._curves + other_h._curves - curves = () - for h in curves0: - if h not in curves: - curves += (h, ) - else: - print("Repeated curve") - result = P(*curves) - return result - - add_curves = union - - __or__ = union - - def deletion(self, curves): - r""" - Return the curve arrangement obtained by removing ``h``. - - INPUT: - - - ``h`` -- a curve or curve arrangement - - OUTPUT: - - A new curve arrangement with the given curve(s) - ``h`` removed. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) - sage: C = h[-1] - sage: h.deletion(C) - Arrangement (x*y, x + y + z, -y^5 + x^3*z^2, x^5 + y^5 + x^2*y^2*z) - in Projective Space of dimension 2 over Rational Field - """ - parent = self.parent() - curves = parent(curves) - planes = list(self) - for curve in curves: - try: - planes.remove(curve) - except ValueError: - raise ValueError('curve is not in the arrangement') - return parent(planes) - - def change_ring(self, base_ring): - """ - Return curve arrangement over the new base ring. - - INPUT: - - - ``base_ring`` -- the new base ring; must be a field for - curve arrangements - - OUTPUT: - - The curve arrangement obtained by changing the base - field, as a new curve arrangement. - - EXAMPLES:: - - SAGE: # needs sage.rings.number_field - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: A = H(y^2*z - x^3, x, y, y^2 + x * y + x^2) - sage: K. = CyclotomicField(3) - sage: A.change_ring(K) - Arrangement (-x^3 + y^2*z, x, y, x^2 + x*y + y^2) in Projective Space of - dimension 2 over Cyclotomic Field of order 3 and degree 2 - """ - parent = self.parent().change_ring(base_ring) - curves = tuple(c.change_ring(base_ring) for c in self) - return parent(curves) - - def coordinate_ring(self): - """ - Return the coordinate ring of ``self``. - - OUTPUT: - - The base ring of the curve arrangement. - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: C = L(x, y) - sage: C.coordinate_ring() - Multivariate Polynomial Ring in x, y, z over Rational Field - """ - return self.curves()[0].defining_polynomial().parent() - - def defining_polynomials(self): - r""" - Return the defining polynomials of the elements of``self``. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: A = H(y^2*z - x^3, x, y, y^2 + x * y + x^2) - sage: A.defining_polynomials() - (-x^3 + y^2*z, x, y, x^2 + x*y + y^2) - """ - return tuple(h.defining_polynomial() for h in self) - - def defining_polynomial(self, simplified=True): - r""" - Return the defining polynomial of the union of the curves in ``self``. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: A = H(y ** 2 + x ** 2, x, y) - sage: prod(A.defining_polynomials()) == A.defining_polynomial() - True - """ - return prod(self.defining_polynomials()) - - def have_common_factors(self): - r""" - Check if th curves have common factors. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: A = H(x * y, x^2 * z^2 + x * y^3) - sage: A.have_common_factors() - True - """ - L = [c.defining_polynomial() for c in self] - C = Combinations(L, 2) - for f1, f2 in C: - if f1.gcd(f2).degree() > 0: - return True - return False - - def reduce(self): - r""" - Replace the curves by their reduction. - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2)) - sage: A.reduce() - Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Projective Space - of dimension 2 over Rational Field - """ - P = self.parent() - L = [c.defining_polynomial().radical() for c in self] - for f1, f2 in Combinations(L, 2): - if f1.gcd(f2) != 1: - print("Some curves have common components") - return None - return P(*L) - - def fundamental_group(self, simplified=True): - r""" - The fundamental group of the complement of the union - of projective plane curves in `\mathbb{P}^2`. - - INPUT: - - - ``simplified`` -- boolean (default: True); set if the group - is simplified.. - - OUTPUT: - - A finitely presented group. - - EXAMPLES:: - - sage: # needs sirocco - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: A = H(y^2 + x*z, y + x - z, x) - sage: A.fundamental_group() - Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > - sage: A = H(y^2 + x*z, z, x) - sage: A.fundamental_group() - Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > - sage: A = H(y^2 + x*z, z*x, y) - sage: A.fundamental_group() - Finitely presented group - < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, - x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > - - .. WARNING:: - - This functionality requires the sirocco package to be installed. - """ - if self._fundamental_group and not simplified: - return self._fundamental_group - if self._fundamental_group_simplified and simplified: - return self._fundamental_group_simplified - H = self.parent() - K = self.base_ring() - R = self.coordinate_ring() - x, y, z = R.gens() - if not K.is_subring(QQbar): - raise TypeError('the base field is not in QQbar') - C = self.reduce() - n = len(C) - infinity = Curve(z) - infinity_in_C = infinity in C - if infinity_in_C: - j = C.curves().index(infinity) - C = H(C.curves()[:j] + C.curves()[j + 1:]) - infinity_divides = False - for j, c in enumerate(C): - g = c.defining_polynomial() - infinity_divides = z.divides(g) - if infinity_divides: - h = R(g / z) - C = H(C.curves()[:j] + (h, ) + C.curves()[j + 1:]) - break - affine = AffinePlaneCurveArrangements(K, names=('u', 'v')) - u, v = affine.gens() - affines = [f.defining_polynomial()(x=u, y=v, z=1) for f in C] - changes = any([g.degree(v) < g.degree() > 1 for g in affines]) - while changes: - affines = [f(u=u + v) for f in affines] - changes = any([g.degree(v) < g.degree() > 1 for g in affines]) - C_affine = affine(affines) - proj = not (infinity_divides or infinity_in_C) - G = C_affine.fundamental_group(simplified=simplified, vertical=True, - projective=proj) - dic = C_affine.meridians(simplified=simplified, vertical=True) - if infinity_in_C: - dic1 = dict() - for k in range(j): - dic1[k] = dic[k] - dic1[j] = dic[n - 1] - for k in range(j + 1, n): - dic1[k] = dic[k - 1] - elif infinity_divides: - dic1 = {k: dic[k] for k in range(n)} - dic1[j] += dic[n] - else: - dic1 = dic - if not simplified: - self._fundamental_group = G - self._meridians = dic1 - else: - self._fundamental_group_simplified = G - self._meridians_simplified = dic1 - return G - - def meridians(self, simplified=True): - r""" - Meridians of each irreducible component. - - OUTPUT: - - A dictionnary which associates the index of each curve with - its meridians, including the line at infinity if it can be - easily computed - - EXAMPLES:: - - sage: # needs sirocco - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: A = H(y^2 + x*z, y + x - z, x) - sage: A.fundamental_group() - Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > - sage: A.meridians() - {0: [x1], 1: [x1^-1*x0^-1*x1^-1], 2: [x0]} - sage: A = H(y^2 + x*z, z, x) - sage: A.fundamental_group() - Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > - sage: A.meridians() - {0: [x0, x1*x0*x1^-1], 1: [x0^-1*x1^-1*x0^-1], 2: [x1]} - sage: A = H(y^2 + x*z, z*x, y) - sage: A.fundamental_group() - Finitely presented group < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, - x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > - sage: A.meridians() - {0: [x0, x2*x0*x2^-1], 1: [x2, x0^-1*x2^-1*x1^-1*x0^-1], 2: [x1]} - - .. WARNING:: - - This functionality requires the sirocco package to be installed. - """ - if not simplified: - computed = bool(self._meridians) - else: - computed = bool(self._meridians_simplified) - if not computed: - _ = self._fundamental_group(simplified=simplified) - if not simplified: - return self._meridians - return self._meridians_simplified - - -class ProjectivePlaneCurveArrangements(Parent, UniqueRepresentation): - """ - Curve arrangements. - - INPUT: - - - ``base_ring`` -- ring; the base ring - - - ``names`` -- tuple of strings; the variable names - - EXAMPLES:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: H(x, y^2, x-z, y-z) - Arrangement (x, y^2, x - z, y - z) in Projective Space - of dimension 2 over Rational Field - """ - Element = ProjectivePlaneCurveArrangementsElement - - def __init__(self, base_ring, names=tuple()): - """ - Initialize ``self``. - - TESTS:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: K = ProjectivePlaneCurveArrangements(QQ, names=('x', 'y', 'z')) - sage: H is K - True - sage: type(K) - - - TESTS:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: TestSuite(H).run() - """ - if base_ring not in _Fields: - raise ValueError('base ring must be a field') - super().__init__(category=Sets()) - self._base_ring = base_ring - self._names = names - - def base_ring(self): - """ - Return the base ring. - - OUTPUT: - - The base ring of the curve arrangement. - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.base_ring() - Rational Field - """ - return self._base_ring - - def coordinate_ring(self): - """ - Return the base ring. - - OUTPUT: - - The base ring of the curve arrangement. - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.base_ring() - Rational Field - """ - return self._coordinate_ring - - def change_ring(self, base_ring): - """ - Return curve arrangements over a different base ring. - - INPUT: - - - ``base_ring`` -- a ring; the new base ring. - - OUTPUT: - - A new :class:`ProjectivePlaneCurveArrangements` instance over the new - base ring. + A new :class:`ProjectivePlaneCurveArrangements` instance over the new + base ring. EXAMPLES:: @@ -1514,132 +1539,132 @@ def ambient_space(self): """ return ProjectiveSpace(self.base_ring(), 2, self._names) - def _repr_(self): - """ - Return a string representation. - - OUTPUT: - - A string. - - EXAMPLES:: - - sage: L. = AffinePlaneCurveArrangements(QQ); L - Curve arrangements in Affine Space of dimension 2 over Rational Field - """ - return 'Curve arrangements in {0}'.format(self.ambient_space()) - - def _element_constructor_(self, *args, **kwds): - """ - Construct an element of ``self``. - - INPUT: - - - ``*args`` -- positional arguments, each defining a curve - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: A = L._element_constructor_(x, y); A - Arrangement (x, y) in Projective Space of dimension 2 over Rational Field - sage: L._element_constructor_([x, y]) == A - True - sage: L._element_constructor_(Curve(x), Curve(y)) == A - True - sage: L._element_constructor_(y, x) == A - False - """ - if len(args) == 1 and not (isinstance(args[0], (tuple, list))): - arg = (args[0], ) - elif len(args) == 1: - arg = tuple(args[0]) - else: - arg = tuple(args) - # process keyword arguments - if len(kwds) > 0: - raise ValueError('unknown keyword argument') - # process positional arguments - ambient_space = self.ambient_space() - R = ambient_space.coordinate_ring() - curves = () - for h in arg: - try: - ambient = h.ambient_space() - if ambient == ambient_space: - curves += (h, ) - else: - return None - except AttributeError: - try: - h = R(h) - curves += (Curve(h), ) - except TypeError: - return None - return self.element_class(self, curves) - - def _an_element_(self): - """ - Return an element of ``self``. - - TESTS:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: H._an_element_() - Arrangement (t) in Projective Space of dimension 2 over Rational Field - """ - x = self.gen(0) - return self(x) - - @cached_method - def ngens(self): - """ - Return the number of variables, i.e. 3, kept for completness. - - OUTPUT: - - An integer (3). - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.ngens() - 3 - """ - return len(self._names) - - @cached_method - def gens(self): - """ - Return the coordinates. - - OUTPUT: - - A tuple of linear expressions, one for each linear variable. - - EXAMPLES:: - - sage: L = ProjectivePlaneCurveArrangements(QQ, ('x', 'y', 'z')) - sage: L.gens() - (x, y, z) - """ - return self.ambient_space().gens() - - def gen(self, i): - """ - Return the `i`-th coordinate. - - INPUT: - - - ``i`` -- integer - - OUTPUT: - - A variable. - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.gen(1) - y - """ - return self.gens()[i] + # def _repr_(self): + # """ + # Return a string representation. + # + # OUTPUT: + # + # A string. + # + # EXAMPLES:: + # + # sage: L. = ProjectivePlaneCurveArrangements(QQ); L + # Curve arrangements in Projective Space of dimension 2 over Rational Field + # """ + # return 'Curve arrangements in {0}'.format(self.ambient_space()) + # + # def _element_constructor_(self, *args, **kwds): + # """ + # Construct an element of ``self``. + # + # INPUT: + # + # - ``*args`` -- positional arguments, each defining a curve + # + # EXAMPLES:: + # + # sage: L. = ProjectivePlaneCurveArrangements(QQ) + # sage: A = L._element_constructor_(x, y); A + # Arrangement (x, y) in Projective Space of dimension 2 over Rational Field + # sage: L._element_constructor_([x, y]) == A + # True + # sage: L._element_constructor_(Curve(x), Curve(y)) == A + # True + # sage: L._element_constructor_(y, x) == A + # False + # """ + # if len(args) == 1 and not (isinstance(args[0], (tuple, list))): + # arg = (args[0], ) + # elif len(args) == 1: + # arg = tuple(args[0]) + # else: + # arg = tuple(args) + # # process keyword arguments + # if len(kwds) > 0: + # raise ValueError('unknown keyword argument') + # # process positional arguments + # ambient_space = self.ambient_space() + # R = ambient_space.coordinate_ring() + # curves = () + # for h in arg: + # try: + # ambient = h.ambient_space() + # if ambient == ambient_space: + # curves += (h, ) + # else: + # return None + # except AttributeError: + # try: + # h = R(h) + # curves += (Curve(h), ) + # except TypeError: + # return None + # return self.element_class(self, curves) + # + # def _an_element_(self): + # """ + # Return an element of ``self``. + # + # TESTS:: + # + # sage: H. = ProjectivePlaneCurveArrangements(QQ) + # sage: H._an_element_() + # Arrangement (t) in Projective Space of dimension 2 over Rational Field + # """ + # x = self.gen(0) + # return self(x) + + # @cached_method + # def ngens(self): + # """ + # Return the number of variables, i.e. 3, kept for completness. + # + # OUTPUT: + # + # An integer (3). + # + # EXAMPLES:: + # + # sage: L. = ProjectivePlaneCurveArrangements(QQ) + # sage: L.ngens() + # 3 + # """ + # return len(self._names) + + # @cached_method + # def gens(self): + # """ + # Return the coordinates. + # + # OUTPUT: + # + # A tuple of linear expressions, one for each linear variable. + # + # EXAMPLES:: + # + # sage: L = ProjectivePlaneCurveArrangements(QQ, ('x', 'y', 'z')) + # sage: L.gens() + # (x, y, z) + # """ + # return self.ambient_space().gens() + + # def gen(self, i): + # """ + # Return the `i`-th coordinate. + # + # INPUT: + # + # - ``i`` -- integer + # + # OUTPUT: + # + # A variable. + # + # EXAMPLES:: + # + # sage: L. = ProjectivePlaneCurveArrangements(QQ) + # sage: L.gen(1) + # y + # """ + # return self.gens()[i] From e6c3757e783cf0136bff411897ae03fa31bcd00d Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 24 Nov 2023 17:41:46 +0100 Subject: [PATCH 50/95] alpha version for doctests --- .../hyperplane_arrangement/arrangement.py | 339 ++++++++++-------- src/sage/schemes/curves/affine_curve.py | 9 +- .../schemes/curves/plane_curve_arrangement.py | 6 +- src/sage/schemes/curves/zariski_vankampen.py | 43 ++- 4 files changed, 232 insertions(+), 165 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index bc6ad7e7bba..faab16d5d6c 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -367,12 +367,17 @@ from sage.combinat.permutation import Permutation from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane +from sage.misc.misc_c import prod +from sage.groups.free_group import FreeGroup 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.qqbar import QQbar from sage.rings.rational_field import QQ +from sage.schemes.curves.plane_curve_arrangement import AffinePlaneCurveArrangements +from sage.schemes.curves.plane_curve_arrangement import ProjectivePlaneCurveArrangements from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import richcmp @@ -3714,8 +3719,10 @@ def __init__(self, parent, hyperplanes, check=True, backend=None): # raise ValueError("not all elements are hyperplanes") # if not all(h.parent() is self.parent().ambient_space() for h in hyperplanes): # raise ValueError("not all hyperplanes are in the ambient space") - self._fundamental_group_ = None - self._meridians_ = None + self._affine_fundamental_group_ = None + self._affine_meridians_ = None + self._projective_fundamental_group_ = None + self._projective_meridians_ = None def hyperplane_section(self, proj=True): r""" @@ -3809,186 +3816,238 @@ def hyperplane_section(self, proj=True): H1 = self.add_hyperplane(h0) return H1.restriction(h0) - def _plane_fundamental_group_(self, proj=False): + def affine_fundamental_group(self): 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 `\overline{\mathbb{Q}}`` - - INPUT: - - - ``proj`` -- (optional, default ``False``). It decides if it computes the - fundamental group of the complement in the affine or projective space + hyperplane arrangement in `\mathbb{C}^n` whose equations have + coefficients in a subfield of `\overline{\mathbb{Q}` OUTPUT: - A group finitely presented with the assignation of each hyperplane to - a member of a group (meridian). + A finitely presented group. EXAMPLES:: + sage: # needs sirocco sage: A. = OrderedHyperplaneArrangements(QQ) sage: L = [y + x, y + x - 1] sage: H = A(L) - sage: G, dic = H._plane_fundamental_group_(); G # optional - sirocco + sage: H.affine_fundamental_group() Finitely presented group < x0, x1 | > sage: L = [x, y, x + 1, y + 1, x - y] - sage: H = A(L); list(H) - [Hyperplane x + 0*y + 0, - Hyperplane 0*x + y + 0, - Hyperplane x + 0*y + 1, - Hyperplane 0*x + y + 1, - Hyperplane x - y + 0] - sage: G, dic = H._plane_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: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} - sage: H=A(x,y,x+y) - sage: H._plane_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: [x0], 1: [x2], 2: [x1], 3: [x2^-1*x1^-1*x0^-1]}) - sage: H._plane_fundamental_group_(proj=True) # optional - sirocco - (Finitely presented group < x0, x1 | >, {1: (1,), 2: (2,), 3: (-2, -1)}) - sage: A3. = OrderedHyperplaneArrangements(QQ) - sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) # optional - sage.graphs - sage: G, dic = H._plane_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} + sage: A(L).affine_fundamental_group() + Finitely presented group + < x0, x1, x2, x3, x4 | x4*x0*x4^-1*x0^-1, + x0*x2*x3*x2^-1*x0^-1*x3^-1, + x1*x2*x4*x2^-1*x1^-1*x4^-1, + x2*x3*x0*x2^-1*x0^-1*x3^-1, + x2*x4*x1*x2^-1*x1^-1*x4^-1, + x4*x1*x4^-1*x3^-1*x2^-1*x1^-1*x2*x3 > + sage: H = A(x, y, x + y) + sage: H.affine_fundamental_group() + Finitely presented group + < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-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): + if self._affine_fundamental_group_: + return self._affine_fundamental_group_ + n = self.dimension() + r = len(self) + if n == 0: + print("Only empty arrangements for dimension ", n) + return None + if n == 1: + G = FreeGroup(r) / [] + dic = {j: G.gen(j) for j in range(r)} + dic[r] = [prod(G.gens()) ** -1] + self._affine_fundamental_group_ = G + self._affine_meridians_ = dic + return G + if n == 2: + S = self.parent().ambient_space().symmetric_space() + coord = vector((1,) + S.gens()) + Af = AffinePlaneCurveArrangements(K, + names=self.parent().variable_names()) + L = Af([vector(line.coefficients()) * coord for line in self]) + G = L.fundamental_group() + self._affine_fundamental_group_ = G + self._affine_meridians_ = L._meridians_vertical_simplified + return G + H1 = self.hyperplane_section(proj=False) + G = H1.affine_fundamental_group() + self._affine_fundamental_group_ = G + self._affine_meridians_ = H1._affine_meridians_ + return G + + def affine_meridians(self): 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 + It assigns to each hyperplane (including the one at infinity) + a meridian in the fundamental group, + of the complement, for a hyperplane arrangement in + `\mathbb{C}^n` whose equations have coefficients in a subfield of `\overline{\mathbb{Q}` - 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). + A dictionary EXAMPLES:: + sage: # needs sirocco sage: A. = OrderedHyperplaneArrangements(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: H.affine_meridians() + {0: [x0], 1: [x1], 2: [x1^-1*x0^-1]} sage: L = [x, y, x + 1, y + 1, x - y] - sage: H = A(L); list(H) - [Hyperplane x + 0*y + 0, - Hyperplane 0*x + y + 0, - Hyperplane x + 0*y + 1, - Hyperplane 0*x + y + 1, - Hyperplane x - y + 0] - 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: [x2], 1: [x4], 2: [x1], 3: [x3], 4: [x0], 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: [x0], 1: [x2], 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: A. = OrderedHyperplaneArrangements(QQ) - sage: H = A(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: H = A(L) + sage: H.affine_meridians() + {0: [x4], 1: [x1], 2: [x3], 3: [x0], 4: [x2], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} + sage: H = A(x, y, x + y) + sage: H.affine_meridians() + {0: [x2], 1: [x1], 2: [x0], 3: [x2^-1*x1^-1*x0^-1]} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not self._affine_meridians_: + self.affine_fundamental_group() + return self._affine_meridians_ + + def projective_fundamental_group(self): + r""" + It computes the fundamental group of the complement of a projective + hyperplane arrangement in `\mathbb{P}^n` whose equations have + coefficients in a subfield of `\overline{\mathbb{Q}`. + + OUTPUT: + + A finitely presented group. + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(x, y, x + y) + sage: H.projective_fundamental_group() + Finitely presented group < x0, x1 | > + sage: A3. = OrderedHyperplaneArrangements(QQ) + sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) # optional - sage.graphs + sage: G3 = H.projective_fundamental_group(); G3 # optional - sage.graphs + 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 > + sage: G3.abelian_invariants() # optional - sage.graphs + (0, 0, 0, 0, 0) + sage: A4. = OrderedHyperplaneArrangements(QQ) + sage: H = A4(hyperplane_arrangements.braid(4)) # optional - sage.graphs + sage: G4 = H.projective_fundamental_group(); G4 # optional - sage.graphs + 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 > + sage: G4.abelian_invariants() # optional - sage.graphs + (0, 0, 0, 0, 0) sage: L. = OrderedHyperplaneArrangements(QQ) + sage: H = hyperplane_arrangements.coordinate(5) sage: H = L(H) - sage: g = H.fundamental_group(projective=True)[0] # optional - sirocco - sage: g.is_abelian(), g.abelian_invariants() # optional - sirocco + sage: g = H.projective_fundamental_group() + sage: g.is_abelian(), g.abelian_invariants() (True, (0, 0, 0, 0)) .. WARNING:: This functionality requires the sirocco package to be installed. """ + K = self.base_ring() + if not K.is_subring(QQbar): + raise TypeError('the base field is not in QQbar') + if not self.is_central(): + raise TypeError('the arrangement is not projective') + if self._projective_fundamental_group_: + return self._projective_fundamental_group_ n = self.dimension() - if n <= 2 or (n == 3 and projective): - return self._plane_fundamental_group_(proj=projective) - H1 = self.hyperplane_section(proj=projective) - H2, dic = H1.fundamental_group(projective=projective) - return (H2, dic) + r = len(self) + if n == 1: + print("Only empty arrangements for dimension ", n - 1) + return None + if n == 2: + G = FreeGroup(r - 1) / [] + dic = {j: G.gen(j) for j in range(r - 1)} + dic[r - 1] = [prod(G.gens()) ** -1] + self._projective_fundamental_group_ = G + self._projective_meridians_ = dic + return G + if n == 3: + S = self.parent().ambient_space().symmetric_space() + coord = vector(S.gens()) + Proj = ProjectivePlaneCurveArrangements(K, + names=self.parent().variable_names()) + L = Proj([vector(line.coefficients()[1:]) * coord for line in self]) + G = L.fundamental_group() + self._projective_fundamental_group_ = G + self._projective_meridians_ = L._meridians_simplified + return G + H1 = self.hyperplane_section() + G = H1.projective_fundamental_group() + self._projective_fundamental_group_ = G + self._projective_meridians_ = H1._projective_meridians_ + return G + + def projective_meridians(self): + r""" + It assigns to each hyperplane + a meridian in the fundamental group, + of the complement, for a hyperplane arrangement in + `\mathbb{P}^n` whose equations have + coefficients in a subfield of `\overline{\mathbb{Q}` + + OUTPUT: + + A dictionary + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(x, y, x + y) + sage: H.projective_meridians() + {0: x0, 1: x1, 2: [x1^-1*x0^-1]} + sage: A3. = OrderedHyperplaneArrangements(QQ) + sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) # optional - sage.graphs + sage: H.projective_meridians() # optional - sage.graphs + {0: [x4], 1: [x0], 2: [x1], 3: [x2], 4: [x3], + 5: [x0^-1*x4^-1*x3^-1*x1^-1*x2^-1]} + sage: A4. = OrderedHyperplaneArrangements(QQ) + sage: H = A4(hyperplane_arrangements.braid(4)) # optional - sage.graphs + sage: H.projective_meridians() # optional - sage.graphs + {0: [x4], 1: [x0], 2: [x1], 3: [x0^-1*x4^-1*x3^-1*x1^-1*x2^-1], + 4: [x3], 5: [x2]} + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: H = hyperplane_arrangements.coordinate(5) + sage: H = L(H) + sage: H.projective_meridians() + {0: [x2], 1: [x3], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1], 4: [x1]} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not self._projective_meridians_: + self.projective_fundamental_group() + return self._projective_meridians_ class HyperplaneArrangements(Parent, UniqueRepresentation): diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index e61fd7b1940..e182c53388a 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1839,17 +1839,18 @@ def fundamental_group(self, simplified=True, puiseux=False): In the case of number fields, they need to have an embedding to the algebraic field:: - sage: # needs sage.rings.number_field - sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] + sage: # needs sage.rings.number_field, sirocco + sage: T. = QQ[] + sage: a = (t^2 + 5).roots(QQbar)[0][0] sage: F = NumberField(a.minpoly(), 'a', embedding=a) sage: F.inject_variables() Defining a sage: A. = AffineSpace(F, 2) sage: C = A.curve(y^2 - a*x^3 - x^2) - sage: C.fundamental_group() # optional - sirocco + sage: C.fundamental_group() Finitely presented group < x0 | > sage: C = A.curve(x * (x - 1)) - sage: C.fundamental_group() # optional - sirocco + sage: C.fundamental_group() Finitely presented group < x0, x1 | > .. WARNING:: diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index a0d141ff088..546bc849893 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -447,7 +447,7 @@ def fundamental_group(self, simplified=True, vertical=True, OUTPUT: - A group finitely presented. + A finitely presented group. EXAMPLES:: @@ -1088,10 +1088,10 @@ def fundamental_group(self, simplified=True): break affine = AffinePlaneCurveArrangements(K, names=('u', 'v')) u, v = affine.gens() - affines = [f.defining_polynomial()(x=u, y=v, z=1) for f in C] + affines = [f.defining_polynomial().subs({x: u, y: v, z: 1}) for f in C] changes = any([g.degree(v) < g.degree() > 1 for g in affines]) while changes: - affines = [f(u=u + v) for f in affines] + affines = [f.subs({u: u + v}) for f in affines] changes = any([g.degree(v) < g.degree() > 1 for g in affines]) C_affine = affine(affines) proj = not (infinity_divides or infinity_in_C) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index b3b8c2e4c57..6fba5483685 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1593,14 +1593,16 @@ def fundamental_group_from_braid_mon(bm, degree=None, ....: 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 > + 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=[0]); 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 > + Finitely presented group + < x0, x1, x2 | x2*x0*x1*x2^-1*x1^-1*x0^-1, + x2*x0*x1*x0*x1^-1*x0^-1*x2^-1*x1^-1 > sage: fundamental_group_from_braid_mon([]) is None True sage: fundamental_group_from_braid_mon([], degree=2) @@ -1708,7 +1710,8 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): sage: R. = QQ[] sage: f = y^3 + x^3 sage: fundamental_group(f) - Finitely presented group < x0, x1, x2 | x0*x1*x2*x0^-1*x2^-1*x1^-1, x2*x0*x1*x2^-1*x1^-1*x0^-1 > + 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 `\overline{\mathbb{Q}}`:: @@ -1736,7 +1739,8 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): sage: g = fundamental_group(f, puiseux=True); g.sorted_presentation() Finitely presented group < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1*x2, - x3^-1*x2^-1*x1*x2, x2^-1*x1^-1*x0^-1*x1*x2*x1, x2^-1*x0 > + x3^-1*x2^-1*x1*x2, x2^-1*x1^-1*x0^-1*x1*x2*x1, + x2^-1*x0 > sage: g.simplified().sorted_presentation() Finitely presented group < x0, x1 | x1^-2*x0^2, (x1^-1*x0)^3 > sage: g = fundamental_group(f, puiseux=True, projective=True) @@ -1824,18 +1828,21 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, sage: g, dic = fundamental_group_arrangement(flist) sage: g.sorted_presentation() Finitely presented group - < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2^-1*x0*x2*x0, x1^-1*x0^-1*x1*x0 > + < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2^-1*x0*x2*x0, + x1^-1*x0^-1*x1*x0 > sage: dic {0: [x0, x2], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]} sage: g, dic = fundamental_group_arrangement(flist, simplified=False) sage: g.sorted_presentation(), dic (Finitely presented group - < x0, x1, x2, x3 | 1, 1, 1, 1, 1, 1, 1, x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, + < x0, x1, x2, x3 | 1, 1, 1, 1, 1, 1, 1, + x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x3*x2, x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1*x2, - x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, x3^-1*x1^-1*x0*x1, - x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0, - x1^-1*x0^-1*x1*x0 >, + x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, + x3^-1*x1^-1*x0*x1, + x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0, + x1^-1*x0^-1*x1*x0, x1^-1*x0^-1*x1*x0 >, {0: [x0, x2, x3], 1: [x1], 2: [x3^-1*x2^-1*x1^-1*x0^-1]}) sage: fundamental_group_arrangement(flist, projective=True) (Finitely presented group < x | >, {0: [x], 1: [x^-3]}) @@ -1848,16 +1855,16 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, (Finitely presented group < x | x^2 >, {0: [x]}) sage: L = [x, y, x - 1, x -y] sage: fundamental_group_arrangement(L) - (Finitely presented group < x0, x1, x2, x3 | x2*x3^-1*x2^-1*x3, - x0*x1*x3*x0^-1*x3^-1*x1^-1, - x3*x0*x1*x3^-1*x1^-1*x0^-1, - x1*x2*x1^-1*x0*x1*x2^-1*x1^-1*x0^-1 >, + (Finitely presented group + < x0, x1, x2, x3 | x2*x3^-1*x2^-1*x3, x0*x1*x3*x0^-1*x3^-1*x1^-1, + x3*x0*x1*x3^-1*x1^-1*x0^-1, + x1*x2*x1^-1*x0*x1*x2^-1*x1^-1*x0^-1 >, {0: [x1], 1: [x3], 2: [x2], 3: [x0]}) sage: fundamental_group_arrangement(L, vertical=True) - (Finitely presented group < x0, x1, x2, x3 | x3*x0*x3^-1*x0^-1, - x3*x1*x3^-1*x1^-1, - x1*x2*x0*x2^-1*x1^-1*x0^-1, - x1*x2*x0*x1^-1*x0^-1*x2^-1 >, + (Finitely presented group + < x0, x1, x2, x3 | x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1, + x1*x2*x0*x2^-1*x1^-1*x0^-1, + x1*x2*x0*x1^-1*x0^-1*x2^-1 >, {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) """ if len(flist) > 0: From e9b2fbbd72e791e5c6af9d6c5aee957a441f1b90 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 25 Nov 2023 18:02:00 +0100 Subject: [PATCH 51/95] final changes before PR --- .../hyperplane_arrangement/arrangement.py | 11 +- src/sage/schemes/curves/affine_curve.py | 18 +- .../schemes/curves/plane_curve_arrangement.py | 581 +++--------------- src/sage/schemes/curves/zariski_vankampen.py | 18 +- 4 files changed, 95 insertions(+), 533 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index faab16d5d6c..a3229e84fec 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -365,7 +365,6 @@ # - 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.misc.misc_c import prod from sage.groups.free_group import FreeGroup @@ -2850,10 +2849,12 @@ def _bounded_region_indices(self): normal = Polyhedron(vertices=[[0]*self.dimension()], lines=[hyperplane.normal() for hyperplane in self], backend=self._backend) - if normal.dim() == 0: - transverse = lambda poly: poly - else: - transverse = lambda poly: poly.intersection(normal) + + def transverse(poly): + if normal.dim() == 0: + return poly + return poly.intersection(normal) + return tuple(i for i, region in enumerate(self.regions()) if transverse(region).is_compact()) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index e182c53388a..37600e55e09 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1792,7 +1792,7 @@ def is_vertical_line(self): return f.degree(y) == 0 and f.degree() == 1 @cached_method - def fundamental_group(self, simplified=True, puiseux=False): + def fundamental_group(self, simplified=True, puiseux=True): r""" Return a presentation of the fundamental group of the complement of ``self``. @@ -1801,7 +1801,7 @@ def fundamental_group(self, simplified=True, puiseux=False): - ``simplified`` -- (default: ``True``) boolean to simplify the presentation. - - ``puiseux`` -- (default: ``False``) boolean to decide if the + - ``puiseux`` -- (default: ``True``) boolean to decide if the presentation is constructed in the classical way or using Puiseux shortcut. If ``True``, ``simplified`` is set to ``False``. @@ -1827,10 +1827,10 @@ def fundamental_group(self, simplified=True, puiseux=False): sage: # optional - sirocco sage: A. = AffineSpace(QQ, 2) sage: C = A.curve(y^2 - x^3 - x^2) - sage: C.fundamental_group() + sage: C.fundamental_group(puiseux=False) Finitely presented group < x0 | > sage: bm = C.braid_monodromy() - sage: g = C.fundamental_group(puiseux=True) + sage: g = C.fundamental_group(simplified=False) sage: g.sorted_presentation() Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0, x1^-1*x0 > sage: g.simplified() @@ -1839,7 +1839,7 @@ def fundamental_group(self, simplified=True, puiseux=False): In the case of number fields, they need to have an embedding to the algebraic field:: - sage: # needs sage.rings.number_field, sirocco + sage: # needs sage.rings.number_field sirocco sage: T. = QQ[] sage: a = (t^2 + 5).roots(QQbar)[0][0] sage: F = NumberField(a.minpoly(), 'a', embedding=a) @@ -1867,7 +1867,13 @@ def fundamental_group(self, simplified=True, puiseux=False): d = d0 + f0.degree(x) else: d = bm[0].parent().strands() - return fundamental_group_from_braid_mon(self.braid_monodromy(), degree=d, simplified=simplified, puiseux=puiseux) + G = fundamental_group_from_braid_mon(self.braid_monodromy(), + degree=d, + simplified=simplified, + puiseux=puiseux) + if simplified: + G = G.simplified() + return G @cached_method def braid_monodromy(self): diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 546bc849893..40028298f08 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -49,6 +49,7 @@ from sage.categories.sets_cat import Sets from sage.combinat.combination import Combinations +from sage.groups.free_group import FreeGroup from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod from sage.rings.qqbar import QQbar @@ -98,7 +99,6 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - # Añadir más atributos con opciones self._braid_monodromy = None self._braid_monodromy_with_vertical = None self._strands = dict() @@ -435,7 +435,7 @@ def fundamental_group(self, simplified=True, vertical=True, INPUT: - - ``vertical`` -- boolean (default: False); if it is ``True``, there + - ``vertical`` -- boolean (default: True); if it is ``True``, there are no vertical asymptotes, and there are vertical lines, then a simplified braid braid_monodromy is used. @@ -454,14 +454,40 @@ def fundamental_group(self, simplified=True, vertical=True, sage: # needs sirocco sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) + sage: A.fundamental_group() + Finitely presented group + < x0, x1, x2 | x2*x0*x2^-1*x0^-1, x1*x0*x1^-1*x0^-1, (x2*x1)^2*(x2^-1*x1^-1)^2 > + sage: A.meridians() + {0: [x1, x2*x1*x2^-1], 1: [x0], 2: [x2], + 3: [x1^-1*x2^-1*x1^-1*x0^-1]} + sage: A.fundamental_group(simplified=False) + Finitely presented group + < x0, x1, x2, x3 | x1*x0*x1^-1*x0^-1, x2*x0*x2^-1*x0^-1, + x3*x0*x1*x0^-1*x2^-1*x0*x2*x0*x1^-1*x0^-1*x3^-1*x0^-1, + x3*x0*x1*x0^-1*x2^-1*x0^-1*(x2*x0)^2*x1^-1*x0^-1*x3^-1*x1^-1, + x3*x0*x1*x0^-1*x3^-1*x2^-1 > + sage: A.meridians(simplified=False) + {0: [x1, x2], 1: [x0], 2: [x3], 3: [x3^-1*x2^-1*x1^-1*x0^-1]} sage: A.fundamental_group(vertical=False) - Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, - x1*x0*x1^-1*x0^-1, - (x0*x2)^2*(x0^-1*x2^-1)^2 > - sage: A.fundamental_group(vertical=True) - Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, - x1*x0*x1^-1*x0^-1, - (x2*x1)^2*(x2^-1*x1^-1)^2 > + Finitely presented group + < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x1*x0*x1^-1*x0^-1, (x0*x2)^2*(x0^-1*x2^-1)^2 > + sage: A.meridians(vertical=False) + {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0]} + sage: A.fundamental_group(simplified=False, vertical=False) + Finitely presented group + < x0, x1, x2, x3 | x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, + x1*x0*x1^-1*x0^-1, + x3^-1*x2^-1*x0^-1*x2*x3*x2^-1*x0*x2*x3*x2^-1, + x3^-1*(x2^-1*x0*x2*x3)^2*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0^-1*x2, + x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2 > + sage: A.meridians(simplified=False, vertical=False) + {0: [x2, x3], 1: [x1], 2: [x0]} + sage: A = H(x * y^2 + x + y, y + x -1, x, y) + sage: A.fundamental_group() + Finitely presented group + < x0, x1, x2, x3 | x3*x0^-1*x3^-1*x0, x3*x1*x3^-1*x1^-1, + x3*x2*x3^-1*x2^-1, x2*x0*x2^-1*x0^-1, + x1*x0^-1*x1^-1*x0, x1*x2*x1^-1*x2^-1 > .. WARNING:: @@ -487,7 +513,7 @@ def fundamental_group(self, simplified=True, vertical=True, bd = (self._braid_monodromy, self._strands, dict(), d1) elif vertical and self._braid_monodromy_with_vertical is not None: d1 = prod(L).degree(R.gen(1)) - bd = (self._braid_monodromy_with_vertical, self._strands, + bd = (self._braid_monodromy_with_vertical, self._strands_with_vertical, self._vertical_lines_in_braid_mon, d1) else: bd = None @@ -506,7 +532,7 @@ def fundamental_group(self, simplified=True, vertical=True, self._fundamental_group_vertical = G self._meridians_vertical = dic else: - self._fundamental_group_vertical = G + self._fundamental_group_vertical_simplified = G self._meridians_vertical_simplified = dic return G @@ -523,13 +549,17 @@ def meridians(self, simplified=True, vertical=True): sage: # needs sirocco sage: H. = AffinePlaneCurveArrangements(QQ) - sage: A = H(y^2 + x, y + x - 1, x) - sage: G = A.fundamental_group(); G - Finitely presented group < x0, x1, x2 | x2*x0*x2^-1*x0^-1, - x1*x0*x1^-1*x0^-1, - (x2*x1)^2*(x2^-1*x1^-1)^2 > + sage: A = H(x-1, y, x, y - 1) + sage: A.fundamental_group() + Finitely presented group + < x0, x1, x2, x3 | x2*x0*x2^-1*x0^-1, x2*x1*x2^-1*x1^-1, + x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1 > sage: A.meridians() - {0: [x1, x2*x1*x2^-1], 1: [x0], 2: [x2], 3: [x1^-1*x2^-1*x1^-1*x0^-1]} + {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]} + + This function needs + :func:`AffinePlaneCurveArrangements.fundamental_group` + where some examples are shown. .. WARNING:: @@ -544,8 +574,7 @@ def meridians(self, simplified=True, vertical=True): else: computed = bool(self._meridians_vertical_simplified) if not computed: - _ = self._fundamental_group(simplified=simplified, - vertical=vertical) + self.fundamental_group(simplified=simplified, vertical=vertical) if not vertical and not simplified: return self._meridians if simplified and not vertical: @@ -719,314 +748,11 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - # Añadir más atributos con opciones self._fundamental_group_simplified = None self._meridians_simplified = dict() self._fundamental_group = None self._meridians = dict() - # def __getitem__(self, i): - # """ - # Return the `i`-th curve. - # - # INPUT: - # - # - ``i`` -- integer - # - # OUTPUT: - # - # The `i`-th curve. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: h = H(y^2 - x * z, y^3 + 2 * x^2 * z, x^4 + y^4 + z^4); h - # Arrangement (y^2 - x*z, y^3 + 2*x^2*z, x^4 + y^4 + z^4) - # in Projective Space of dimension 2 over Rational Field - # """ - # return self._curves[i] - # - # def __hash__(self): - # r""" - # TESTS:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: h = H((x * y, x + y + z)) - # sage: len_dict = {h: len(h)} - # """ - # return hash(self.curves()) - # - # def n_curves(self): - # r""" - # Return the number of curves in the arrangement. - # - # OUTPUT: - # - # An integer. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: h = H((x * y, x + y + z)) - # sage: h.n_curves() - # 2 - # sage: len(h) # equivalent - # 2 - # """ - # return len(self._curves) - # - # __len__ = n_curves - # - # def curves(self): - # r""" - # Return the curves in the arrangement as a tuple. - # - # OUTPUT: - # - # A tuple. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: h = H((x * y, x + y + z)) - # sage: h.curves() - # (Projective Conic Curve over Rational Field defined by x*y, - # Projective Plane Curve over Rational Field defined by x + y + z) - # - # Note that the hyperplanes can be indexed as if they were a list:: - # - # sage: h[1] - # Projective Plane Curve over Rational Field defined by x + y + z - # """ - # return self._curves - # - # def _repr_(self): - # r""" - # String representation for a curve arrangement. - # - # OUTPUT: - # - # A string. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) - # sage: h - # Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field - # sage: H(()) - # Empty curve arrangement in Projective Space of dimension 2 over Rational Field - # """ - # if len(self) == 0: - # return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) - # elif len(self) < 5: - # curves = ', '.join(h.defining_polynomial()._repr_() - # for h in self._curves) - # return 'Arrangement ({0}) in {1}'.format(curves, - # self.parent().ambient_space()) - # return 'Arrangement of {0} curves in {1}'.format(len(self), - # self.parent().ambient_space()) - # - # def _richcmp_(self, other, op): - # """ - # Compare two curve arrangements. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: H(x) == H(y) - # False - # sage: H(x) == H(2 * x) - # True - # - # TESTS:: - # - # sage: H(x) == 0 - # False - # """ - # return richcmp(self._curves, other._curves, op) - # - # def union(self, other): - # r""" - # The union of ``self`` with ``other``. - # - # INPUT: - # - # - ``other`` -- a curve arrangement or something that can - # be converted into a curve arrangement - # - # OUTPUT: - # - # A new curve arrangement. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) - # sage: C = Curve(x^8 - y^8 -x^4 * y^4) - # sage: h1 = h.union(C); h1 - # Arrangement of 6 curves in Projective Space of dimension 2 over Rational Field - # sage: h1 == h1.union(C) - # Repeated curve - # True - # """ - # P = self.parent() - # other_h = P(other) - # curves0 = self._curves + other_h._curves - # curves = () - # for h in curves0: - # if h not in curves: - # curves += (h, ) - # else: - # print("Repeated curve") - # result = P(*curves) - # return result - # - # add_curves = union - # - # __or__ = union - # - # def deletion(self, curves): - # r""" - # Return the curve arrangement obtained by removing ``h``. - # - # INPUT: - # - # - ``h`` -- a curve or curve arrangement - # - # OUTPUT: - # - # A new curve arrangement with the given curve(s) - # ``h`` removed. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) - # sage: C = h[-1] - # sage: h.deletion(C) - # Arrangement (x*y, x + y + z, -y^5 + x^3*z^2, x^5 + y^5 + x^2*y^2*z) - # in Projective Space of dimension 2 over Rational Field - # """ - # parent = self.parent() - # curves = parent(curves) - # planes = list(self) - # for curve in curves: - # try: - # planes.remove(curve) - # except ValueError: - # raise ValueError('curve is not in the arrangement') - # return parent(planes) - # - # def change_ring(self, base_ring): - # """ - # Return curve arrangement over the new base ring. - # - # INPUT: - # - # - ``base_ring`` -- the new base ring; must be a field for - # curve arrangements - # - # OUTPUT: - # - # The curve arrangement obtained by changing the base - # field, as a new curve arrangement. - # - # EXAMPLES:: - # - # SAGE: # needs sage.rings.number_field - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: A = H(y^2*z - x^3, x, y, y^2 + x * y + x^2) - # sage: K. = CyclotomicField(3) - # sage: A.change_ring(K) - # Arrangement (-x^3 + y^2*z, x, y, x^2 + x*y + y^2) in Projective Space of - # dimension 2 over Cyclotomic Field of order 3 and degree 2 - # """ - # parent = self.parent().change_ring(base_ring) - # curves = tuple(c.change_ring(base_ring) for c in self) - # return parent(curves) - # - # def coordinate_ring(self): - # """ - # Return the coordinate ring of ``self``. - # - # OUTPUT: - # - # The base ring of the curve arrangement. - # - # EXAMPLES:: - # - # sage: L. = ProjectivePlaneCurveArrangements(QQ) - # sage: C = L(x, y) - # sage: C.coordinate_ring() - # Multivariate Polynomial Ring in x, y, z over Rational Field - # """ - # return self.curves()[0].defining_polynomial().parent() - # - # def defining_polynomials(self): - # r""" - # Return the defining polynomials of the elements of``self``. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: A = H(y^2*z - x^3, x, y, y^2 + x * y + x^2) - # sage: A.defining_polynomials() - # (-x^3 + y^2*z, x, y, x^2 + x*y + y^2) - # """ - # return tuple(h.defining_polynomial() for h in self) - # - # def defining_polynomial(self, simplified=True): - # r""" - # Return the defining polynomial of the union of the curves in ``self``. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: A = H(y ** 2 + x ** 2, x, y) - # sage: prod(A.defining_polynomials()) == A.defining_polynomial() - # True - # """ - # return prod(self.defining_polynomials()) - # - # def have_common_factors(self): - # r""" - # Check if the curves have common factors. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: A = H(x * y, x^2 * z^2 + x * y^3) - # sage: A.have_common_factors() - # True - # """ - # L = [c.defining_polynomial() for c in self] - # C = Combinations(L, 2) - # for f1, f2 in C: - # if f1.gcd(f2).degree() > 0: - # return True - # return False - # - # def reduce(self): - # r""" - # Replace the curves by their reduction. - # - # EXAMPLES:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2)) - # sage: A.reduce() - # Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Projective Space - # of dimension 2 over Rational Field - # """ - # P = self.parent() - # L = [c.defining_polynomial().radical() for c in self] - # for f1, f2 in Combinations(L, 2): - # if f1.gcd(f2) != 1: - # print("Some curves have common components") - # return None - # return P(*L) - # def fundamental_group(self, simplified=True): r""" The fundamental group of the complement of the union @@ -1048,6 +774,16 @@ def fundamental_group(self, simplified=True): sage: A = H(y^2 + x*z, y + x - z, x) sage: A.fundamental_group() Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > + sage: A.meridians() + {0: [x1], 1: [x1^-1*x0^-1*x1^-1], 2: [x0]} + sage: A.fundamental_group(simplified=False) + Finitely presented group + < x0, x1, x2, x3 | x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, + x1*x0*x1^-1*x0^-1, + x3^-1*x2^-1*x0^-1*x2*x3*x2^-1*x0*x2*x3*x2^-1, + x3^-1*(x2^-1*x0*x2*x3)^2*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0^-1*x2, x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, x0*x1*x2*x3 > + sage: A.meridians(simplified=False) + {0: [x2, x3], 1: [x1], 2: [x0]} sage: A = H(y^2 + x*z, z, x) sage: A.fundamental_group() Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > @@ -1075,6 +811,13 @@ def fundamental_group(self, simplified=True): n = len(C) infinity = Curve(z) infinity_in_C = infinity in C + if infinity_in_C and n == 1: + G = FreeGroup(0) / [] + self._fundamental_group = G + self._meridians = {0: 0} + self._fundamental_group_simplified = G + self._meridians_simplified = {0: [G.one()]} + return G if infinity_in_C: j = C.curves().index(infinity) C = H(C.curves()[:j] + C.curves()[j + 1:]) @@ -1149,6 +892,10 @@ def meridians(self, simplified=True): sage: A.meridians() {0: [x0, x2*x0*x2^-1], 1: [x2, x0^-1*x2^-1*x1^-1*x0^-1], 2: [x1]} + This function needs + :func:`ProjectivePlaneCurveArrangements.fundamental_group` + where some examples are shown. + .. WARNING:: This functionality requires the sirocco package to be installed. @@ -1466,66 +1213,6 @@ def __init__(self, base_ring, names=tuple()): self._base_ring = base_ring self._names = names - # def base_ring(self): - # """ - # Return the base ring. - # - # OUTPUT: - # - # The base ring of the curve arrangement. - # - # EXAMPLES:: - # - # sage: L. = ProjectivePlaneCurveArrangements(QQ) - # sage: L.base_ring() - # Rational Field - # """ - # return self._base_ring - - # def coordinate_ring(self): - # """ - # Return the base ring. - # - # OUTPUT: - # - # The base ring of the curve arrangement. - # - # EXAMPLES:: - # - # sage: L. = ProjectivePlaneCurveArrangements(QQ) - # sage: L.base_ring() - # Rational Field - # """ - # return self._coordinate_ring - - def change_ring(self, base_ring): - """ - Return curve arrangements over a different base ring. - - INPUT: - - - ``base_ring`` -- a ring; the new base ring. - - OUTPUT: - - A new :class:`ProjectivePlaneCurveArrangements` instance over the new - base ring. - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.gen(0) - x - sage: L.change_ring(RR).base_ring() - Real Field with 53 bits of precision - - TESTS:: - - sage: L.change_ring(QQ) is L - True - """ - return ProjectivePlaneCurveArrangements(base_ring, names=self._names) - @cached_method def ambient_space(self): """ @@ -1538,133 +1225,3 @@ def ambient_space(self): Projective Space of dimension 2 over Rational Field """ return ProjectiveSpace(self.base_ring(), 2, self._names) - - # def _repr_(self): - # """ - # Return a string representation. - # - # OUTPUT: - # - # A string. - # - # EXAMPLES:: - # - # sage: L. = ProjectivePlaneCurveArrangements(QQ); L - # Curve arrangements in Projective Space of dimension 2 over Rational Field - # """ - # return 'Curve arrangements in {0}'.format(self.ambient_space()) - # - # def _element_constructor_(self, *args, **kwds): - # """ - # Construct an element of ``self``. - # - # INPUT: - # - # - ``*args`` -- positional arguments, each defining a curve - # - # EXAMPLES:: - # - # sage: L. = ProjectivePlaneCurveArrangements(QQ) - # sage: A = L._element_constructor_(x, y); A - # Arrangement (x, y) in Projective Space of dimension 2 over Rational Field - # sage: L._element_constructor_([x, y]) == A - # True - # sage: L._element_constructor_(Curve(x), Curve(y)) == A - # True - # sage: L._element_constructor_(y, x) == A - # False - # """ - # if len(args) == 1 and not (isinstance(args[0], (tuple, list))): - # arg = (args[0], ) - # elif len(args) == 1: - # arg = tuple(args[0]) - # else: - # arg = tuple(args) - # # process keyword arguments - # if len(kwds) > 0: - # raise ValueError('unknown keyword argument') - # # process positional arguments - # ambient_space = self.ambient_space() - # R = ambient_space.coordinate_ring() - # curves = () - # for h in arg: - # try: - # ambient = h.ambient_space() - # if ambient == ambient_space: - # curves += (h, ) - # else: - # return None - # except AttributeError: - # try: - # h = R(h) - # curves += (Curve(h), ) - # except TypeError: - # return None - # return self.element_class(self, curves) - # - # def _an_element_(self): - # """ - # Return an element of ``self``. - # - # TESTS:: - # - # sage: H. = ProjectivePlaneCurveArrangements(QQ) - # sage: H._an_element_() - # Arrangement (t) in Projective Space of dimension 2 over Rational Field - # """ - # x = self.gen(0) - # return self(x) - - # @cached_method - # def ngens(self): - # """ - # Return the number of variables, i.e. 3, kept for completness. - # - # OUTPUT: - # - # An integer (3). - # - # EXAMPLES:: - # - # sage: L. = ProjectivePlaneCurveArrangements(QQ) - # sage: L.ngens() - # 3 - # """ - # return len(self._names) - - # @cached_method - # def gens(self): - # """ - # Return the coordinates. - # - # OUTPUT: - # - # A tuple of linear expressions, one for each linear variable. - # - # EXAMPLES:: - # - # sage: L = ProjectivePlaneCurveArrangements(QQ, ('x', 'y', 'z')) - # sage: L.gens() - # (x, y, z) - # """ - # return self.ambient_space().gens() - - # def gen(self, i): - # """ - # Return the `i`-th coordinate. - # - # INPUT: - # - # - ``i`` -- integer - # - # OUTPUT: - # - # A variable. - # - # EXAMPLES:: - # - # sage: L. = ProjectivePlaneCurveArrangements(QQ) - # sage: L.gen(1) - # y - # """ - # return self.gens()[i] diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 6fba5483685..df48daf0c1d 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -849,7 +849,7 @@ def braid_in_segment(glist, x0, x1, precision=dict()): Check that :trac:`26503` is fixed:: - sage: # needs sage.rings.real_mpfr sage.symbolic + sage: # needs sage.rings.real_mpfr sage.symbolic sirocco sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0] sage: Kw. = NumberField(wp.minpoly(), embedding=wp) sage: R. = Kw[] @@ -866,7 +866,7 @@ def braid_in_segment(glist, x0, x1, precision=dict()): sage: p2a = CC(p2) sage: p2b = QQ(p2a.real()) + I*QQ(p2a.imag()) sage: glist = tuple([_[0] for _ in g.factor()]) - sage: B = braid_in_segment(glist, p1b, p2b); B # optional - sirocco + sage: B = braid_in_segment(glist, p1b, p2b); B s5*s3^-1 """ precision1 = {_: precision[_] for _ in precision.keys()} @@ -1235,9 +1235,10 @@ def braid_monodromy(f, arrangement=(), vertical=False): - A dictionnary: ``i``, index of a strand is sent to the index of the corresponding factor in ``arrangement``. - - Another dictionnary, only relevant if ``vertical`` is ``True``. - It attaches the index of a vertical line in ``arrangement`` - to the index of its corresponding braid. + - Another dictionnary ``dv``, only relevant if ``vertical`` is ``True``. + If ``j`` is the index + of a braid corresponding to a vertical line with index ``i`` + in ``arrangement``, then ``dv[j] = i``. - A non-negative integer: the number of strands of the braids, only necessary if the list of braids is empty. @@ -1317,7 +1318,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): transversal[f0] = arrangement1.index(f0) vl.sort() if not disc: - vertical_braids = {i + d: transversal[f0] + vertical_braids = {i: transversal[f0] for i, f0 in enumerate(transversal)} if d > 1: result = [BraidGroup(d).one() for p in transversal] @@ -1892,7 +1893,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, elif len(flist1) == 0: bm = [] dic = dict() - dv = {j: j for j, f in flist} + dv = {j: j for j, f in flist1} d1 = 0 else: bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical0) @@ -1900,9 +1901,6 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, vert_lines.sort() for i, j in enumerate(vert_lines): dic[d1 + i] = dv[j] - # if vertical0: - # for j in dv: - # dic[d1 + j] = dv[j] g = fundamental_group_from_braid_mon(bm, degree=d1, simplified=False, projective=projective, puiseux=puiseux, From 339f220ef7826ebe127827baedb650904549c476 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 26 Nov 2023 11:24:25 +0100 Subject: [PATCH 52/95] compute the meridian of the line at infinity in more cases and correct doc typos --- .../schemes/curves/plane_curve_arrangement.py | 68 +++++++++++-------- src/sage/schemes/curves/zariski_vankampen.py | 45 ++++++------ 2 files changed, 65 insertions(+), 48 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 40028298f08..01b93ed5d65 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -181,7 +181,7 @@ def curves(self): (Affine Plane Curve over Rational Field defined by x*y, Affine Plane Curve over Rational Field defined by x + y + 1) - Note that the hyperplanes can be indexed as if they were a list:: + Note that the curves can be indexed as if they were a list:: sage: h[1] Affine Plane Curve over Rational Field defined by x + y + 1 @@ -276,11 +276,11 @@ def union(self, other): def deletion(self, curves): r""" - Return the curve arrangement obtained by removing ``h``. + Return the curve arrangement obtained by removing ``curves``. INPUT: - - ``h`` -- a curve or curve arrangement + - ``curves`` -- a curve or curve arrangement OUTPUT: @@ -339,7 +339,7 @@ def coordinate_ring(self): OUTPUT: - The base ring of the curve arrangement. + The coordinate ring of the curve arrangement. EXAMPLES:: @@ -352,7 +352,7 @@ def coordinate_ring(self): def defining_polynomials(self): r""" - Return the defining polynomials of the elements of``self``. + Return the defining polynomials of the elements of ``self``. EXAMPLES:: @@ -412,6 +412,15 @@ def reduce(self, clean=False): sage: A.reduce() Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Affine Space of dimension 2 over Rational Field + sage: C = H(x*y, x*(y + 1)) + sage: C.reduce() + Some curves have common components + sage: C.reduce(clean=True) + Arrangement (x*y, y + 1) in Affine Space of dimension 2 + over Rational Field + sage: C = H(x*y, x) + sage: C.reduce(clean=True) + Arrangement (x*y) in Affine Space of dimension 2 over Rational Field """ P = self.parent() R = self.coordinate_ring() @@ -424,7 +433,8 @@ def reduce(self, clean=False): print("Some curves have common components") return None g = R(g / d) - L.append(g) + if g.degree() > 0: + L.append(g) return P(*L) def fundamental_group(self, simplified=True, vertical=True, @@ -472,7 +482,7 @@ def fundamental_group(self, simplified=True, vertical=True, Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x1*x0*x1^-1*x0^-1, (x0*x2)^2*(x0^-1*x2^-1)^2 > sage: A.meridians(vertical=False) - {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0]} + {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]} sage: A.fundamental_group(simplified=False, vertical=False) Finitely presented group < x0, x1, x2, x3 | x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, @@ -481,7 +491,7 @@ def fundamental_group(self, simplified=True, vertical=True, x3^-1*(x2^-1*x0*x2*x3)^2*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0^-1*x2, x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2 > sage: A.meridians(simplified=False, vertical=False) - {0: [x2, x3], 1: [x1], 2: [x0]} + {0: [x2, x3], 1: [x1], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1]} sage: A = H(x * y^2 + x + y, y + x -1, x, y) sage: A.fundamental_group() Finitely presented group @@ -542,8 +552,8 @@ def meridians(self, simplified=True, vertical=True): OUTPUT: - A dictionnary which associates the index of each curve with its meridians, - including the line at infinity if it can be easily computed + A dictionary which associates the index of each curve with its meridians, + including the line at infinity if it can be omputed EXAMPLES:: @@ -558,7 +568,7 @@ def meridians(self, simplified=True, vertical=True): {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]} This function needs - :func:`AffinePlaneCurveArrangements.fundamental_group` + :func:`AffinePlaneCurveArrangements.fundamental_group` with the same options, where some examples are shown. .. WARNING:: @@ -594,12 +604,12 @@ def braid_monodromy(self, vertical=True): - ``vertical`` -- boolean (default: True). If it is ``True``, there are no vertical asymptotes, and there are vertical lines, then a - simplified braid braid_monodromy is computed. + simplified braid_monodromy is computed. OUTPUT: A braid monodromy with dictionnaries identifying strans with components - and braids with vertical lines.. + and braids with vertical lines. EXAMPLES:: @@ -644,8 +654,9 @@ def strands(self): OUTPUT: - A dictionnary which associates to the index of each strand - its associated component. + A dictionary which associates to the index of each strand + its associated component if the braid monodromy has been + calculated with ``vertical=False``. EXAMPLES:: @@ -670,8 +681,9 @@ def vertical_strands(self): OUTPUT: - A dictionnary which associates to the index of each strand - its associated component. + A dictionary which associates to the index of each strand + its associated component if the braid monodromy has been + calculated with ``vertical=True``. EXAMPLES:: @@ -696,7 +708,8 @@ def vertical_lines_in_braid_mon(self): OUTPUT: - A dictionnary which associates an index to the index of a vertical lines. + A dictionary which associates the index of a braid + to the index of the vertical line associated to the braid. EXAMPLES:: @@ -704,6 +717,7 @@ def vertical_lines_in_braid_mon(self): sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) sage: bm = A.braid_monodromy(vertical=True) + [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] sage: A.vertical_lines_in_braid_mon() {1: 2} @@ -867,9 +881,9 @@ def meridians(self, simplified=True): OUTPUT: - A dictionnary which associates the index of each curve with + A dictionary which associates the index of each curve with its meridians, including the line at infinity if it can be - easily computed + computed EXAMPLES:: @@ -894,7 +908,7 @@ def meridians(self, simplified=True): This function needs :func:`ProjectivePlaneCurveArrangements.fundamental_group` - where some examples are shown. + with the same options, where some examples are shown. .. WARNING:: @@ -913,7 +927,7 @@ def meridians(self, simplified=True): class AffinePlaneCurveArrangements(Parent, UniqueRepresentation): """ - Curve arrangements. + Affine curve arrangements. INPUT: @@ -972,17 +986,17 @@ def base_ring(self): def coordinate_ring(self): """ - Return the base ring. + Return the coordinate ring. OUTPUT: - The base ring of the curve arrangement. + The coordinate ring of the curve arrangement. EXAMPLES:: sage: L. = AffinePlaneCurveArrangements(QQ) - sage: L.base_ring() - Rational Field + sage: L.coordinate_ring() + Multivariate Polynomial Ring in x, y over Rational Field """ return self._coordinate_ring @@ -1172,7 +1186,7 @@ def gen(self, i): class ProjectivePlaneCurveArrangements(AffinePlaneCurveArrangements): """ - Curve arrangements. + Projective curve arrangements. INPUT: diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index df48daf0c1d..3500ad0ea2f 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -393,7 +393,7 @@ def voronoi_cells(V, vertical_lines=[]): 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. - - ``vertical_regions`` -- dictionnary for the regions associated + - ``vertical_regions`` -- dictionary for the regions associated with vertical lines. EXAMPLES:: @@ -957,14 +957,14 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): - ``vertical_regions`` -- dictionary with keys the vertices of ``dual_graph`` to fix regions associated with vertical lines - OUTPUT: A geometric basis and a dictionnary. + OUTPUT: A geometric basis and a dictionary. The geometric basis is formed by a list of sequences of paths. Each path is a ist 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``. - The dictionnary associates to each vertical line the index of the generator + The dictionary associates to each vertical line the index of the generator of the geometric basis associated to it. EXAMPLES:: @@ -1138,6 +1138,7 @@ def vertical_lines_in_braidmon(flist): list. EXAMPLES:: + sage: from sage.schemes.curves.zariski_vankampen import vertical_lines_in_braidmon sage: R. = QQ[] sage: flist = [x^2 - y^3, x, x + 3 * y - 5, 1 - x] @@ -1220,22 +1221,25 @@ def braid_monodromy(f, arrangement=(), vertical=False): - ``arrangement`` -- tuple (default: ``[]``); an optional tuple of polynomials whose product equals ``f``. - - `vertical` .. boolean (default: ``False`). If set to ``True``, + - ``vertical`` -- boolean (default: ``False`); if set to ``True``, ``arrangements`` contains more than one polynomial, some of them are of degree `1` in `x` and degree `0` in `y`, and none of the other components have vertical asymptotes, then these - components are marked and not used for the computation - of the braid monodromy. + components are marked as *vertical* and not used for the computation + of the braid monodromy. The other ones are marked as *horizontal*. If + a vertical component does not pass through a singular points of the + projection of the horizontal components a trivial braid is added + to the list. OUTPUT: - A list of braids, images by the braid monodromy of a geometric basis of the complement of the discriminant of `f` in `\mathbb{C}`. - - A dictionnary: ``i``, index of a strand is sent to the index of + - A dictionary: ``i``, index of a strand is sent to the index of the corresponding factor in ``arrangement``. - - Another dictionnary ``dv``, only relevant if ``vertical`` is ``True``. + - Another dictionary ``dv``, only relevant if ``vertical`` is ``True``. If ``j`` is the index of a braid corresponding to a vertical line with index ``i`` in ``arrangement``, then ``dv[j] = i``. @@ -1813,11 +1817,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, 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`` + - A dictionary attaching to ``j`` a tuple a list of elements + of the group which are meridians of the curve in position ``j``. + If ``projective`` is ``False`` and the `y`-degree of the horizontal + components coincide with the total degree, another key is added + to give a meridian of the line at infinity. EXAMPLES:: @@ -1851,7 +1855,8 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, (Finitely presented group < | >, {}) sage: g, dic = fundamental_group_arrangement([x * y]) sage: g.sorted_presentation(), dic - (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, {0: [x0, x1]}) + (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, + {0: [x0, x1], 1: [x1^-1*x0^-1]}) sage: fundamental_group_arrangement([y + x^2], projective=True) (Finitely presented group < x | x^2 >, {0: [x]}) sage: L = [x, y, x - 1, x -y] @@ -1860,7 +1865,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, < x0, x1, x2, x3 | x2*x3^-1*x2^-1*x3, x0*x1*x3*x0^-1*x3^-1*x1^-1, x3*x0*x1*x3^-1*x1^-1*x0^-1, x1*x2*x1^-1*x0*x1*x2^-1*x1^-1*x0^-1 >, - {0: [x1], 1: [x3], 2: [x2], 3: [x0]}) + {0: [x1], 1: [x3], 2: [x2], 3: [x0], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) sage: fundamental_group_arrangement(L, vertical=True) (Finitely presented group < x0, x1, x2, x3 | x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1, @@ -1883,11 +1888,9 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, while f.degree(y) < d: flist1 = [g.subs({x: x + y}) for g in flist1] f = prod(flist1) - if not vertical0: - infinity = all([g.degree(y) == g.degree() or - Curve(g).is_vertical_line() for g in flist1]) - if vertical0: - infinity = all([not Curve(g).has_vertical_asymptote() for g in flist1]) + infinity = not vertical0 or all([Curve(g).is_vertical_line() or + g.degree(y) == g.degree() + for g in flist1]) if braid_data: bm, dic, dv, d1 = braid_data elif len(flist1) == 0: @@ -1916,7 +1919,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, for i in range(len(flist1)): L = [j1 for j1 in dic if dic[j1] == i] dic1[i] = [hom(g.gen(j)) for j in L] - if not projective and infinity and d1 == f.degree(y): + if not projective and infinity: t = prod(hom(a) for a in g.gens()).inverse() dic1[len(flist1)] = [t] n = g1.ngens() From 4d1bfeaa246658ab6df7737ce2df435f020a5563 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 26 Nov 2023 11:40:01 +0100 Subject: [PATCH 53/95] typo --- src/sage/schemes/curves/plane_curve_arrangement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 01b93ed5d65..082f7e2e55f 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -420,7 +420,7 @@ def reduce(self, clean=False): over Rational Field sage: C = H(x*y, x) sage: C.reduce(clean=True) - Arrangement (x*y) in Affine Space of dimension 2 over Rational Field + Arrangement (x*y) in Affine Space of dimension 2 over Rational Field """ P = self.parent() R = self.coordinate_ring() From 345bee52315a6339d24b11b2c28077d1f4b1140e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 26 Nov 2023 15:07:07 +0100 Subject: [PATCH 54/95] some more typos --- .../geometry/hyperplane_arrangement/arrangement.py | 8 ++++---- src/sage/schemes/curves/plane_curve_arrangement.py | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index a3229e84fec..f4a5cb0eccd 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3821,7 +3821,7 @@ def affine_fundamental_group(self): r""" It computes the fundamental group of the complement of an affine hyperplane arrangement in `\mathbb{C}^n` whose equations have - coefficients in a subfield of `\overline{\mathbb{Q}` + coefficients in a subfield of `\overline{\mathbb{Q}}` OUTPUT: @@ -3892,7 +3892,7 @@ def affine_meridians(self): a meridian in the fundamental group, of the complement, for a hyperplane arrangement in `\mathbb{C}^n` whose equations have - coefficients in a subfield of `\overline{\mathbb{Q}` + coefficients in a subfield of `\overline{\mathbb{Q}}` OUTPUT: @@ -3926,7 +3926,7 @@ def projective_fundamental_group(self): r""" It computes the fundamental group of the complement of a projective hyperplane arrangement in `\mathbb{P}^n` whose equations have - coefficients in a subfield of `\overline{\mathbb{Q}`. + coefficients in a subfield of `\overline{\mathbb{Q}}`. OUTPUT: @@ -4013,7 +4013,7 @@ def projective_meridians(self): a meridian in the fundamental group, of the complement, for a hyperplane arrangement in `\mathbb{P}^n` whose equations have - coefficients in a subfield of `\overline{\mathbb{Q}` + coefficients in a subfield of `\overline{\mathbb{Q}}` OUTPUT: diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 082f7e2e55f..b0860016f5d 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -52,6 +52,7 @@ from sage.groups.free_group import FreeGroup from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.qqbar import QQbar from sage.rings.ring import _Fields from sage.schemes.affine.affine_space import AffineSpace @@ -716,7 +717,7 @@ def vertical_lines_in_braid_mon(self): sage: # needs sirocco sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) - sage: bm = A.braid_monodromy(vertical=True) + sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] sage: A.vertical_lines_in_braid_mon() {1: 2} @@ -793,9 +794,9 @@ def fundamental_group(self, simplified=True): sage: A.fundamental_group(simplified=False) Finitely presented group < x0, x1, x2, x3 | x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, - x1*x0*x1^-1*x0^-1, - x3^-1*x2^-1*x0^-1*x2*x3*x2^-1*x0*x2*x3*x2^-1, - x3^-1*(x2^-1*x0*x2*x3)^2*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0^-1*x2, x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, x0*x1*x2*x3 > + x1*x0*x1^-1*x0^-1, x3^-1*x2^-1*x0^-1*x2*x3*x2^-1*x0*x2*x3*x2^-1, + x3^-1*(x2^-1*x0*x2*x3)^2*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0^-1*x2, + x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, x0*x1*x2*x3 > sage: A.meridians(simplified=False) {0: [x2, x3], 1: [x1], 2: [x0]} sage: A = H(y^2 + x*z, z, x) @@ -998,7 +999,7 @@ def coordinate_ring(self): sage: L.coordinate_ring() Multivariate Polynomial Ring in x, y over Rational Field """ - return self._coordinate_ring + return PolynomialRing(self._base_ring, self._names) def change_ring(self, base_ring): """ From 483aa6cc9ed69ec3abe65f5cdf179c58fda758af Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 26 Nov 2023 15:24:22 +0100 Subject: [PATCH 55/95] another typo --- src/sage/schemes/curves/plane_curve_arrangement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index b0860016f5d..bb6cf4b73cd 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -597,7 +597,7 @@ def meridians(self, simplified=True, vertical=True): def braid_monodromy(self, vertical=True): r""" - It computes the braid monodromy of the compleme+nt of the union + It computes the braid monodromy of the complement of the union of affine plane curves in `\mathbb{C}^2`. If there are vertical asymptotes a change of variable is done. From 9ffb6e292c68bedcd9b157a7beaf7d0fc6cb3687 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 26 Nov 2023 17:10:01 +0100 Subject: [PATCH 56/95] yet another typo --- src/sage/schemes/curves/plane_curve_arrangement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index bb6cf4b73cd..73ef8420bbc 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -609,7 +609,7 @@ def braid_monodromy(self, vertical=True): OUTPUT: - A braid monodromy with dictionnaries identifying strans with components + A braid monodromy with dictionnaries identifying strands with components and braids with vertical lines. EXAMPLES:: From 520c36576cf73e7921c547b2e9bc670600e27912 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 26 Nov 2023 20:14:37 +0100 Subject: [PATCH 57/95] use sorted_presentation to avoid fake failing tests --- .../schemes/curves/plane_curve_arrangement.py | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 73ef8420bbc..686cb7e8c35 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -471,12 +471,13 @@ def fundamental_group(self, simplified=True, vertical=True, sage: A.meridians() {0: [x1, x2*x1*x2^-1], 1: [x0], 2: [x2], 3: [x1^-1*x2^-1*x1^-1*x0^-1]} - sage: A.fundamental_group(simplified=False) + sage: G = A.fundamental_group(simplified=False) + sage: G.sorted_presentation() Finitely presented group - < x0, x1, x2, x3 | x1*x0*x1^-1*x0^-1, x2*x0*x2^-1*x0^-1, - x3*x0*x1*x0^-1*x2^-1*x0*x2*x0*x1^-1*x0^-1*x3^-1*x0^-1, - x3*x0*x1*x0^-1*x2^-1*x0^-1*(x2*x0)^2*x1^-1*x0^-1*x3^-1*x1^-1, - x3*x0*x1*x0^-1*x3^-1*x2^-1 > + < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x0*x1*x0^-1, + x3^-1*x1^-1*x3*x0*x1*x0^-1*x2^-1*x0^-1*(x2*x0)^2*x1^-1*x0^-1, + x3^-1*x0^-1*x3*x0*x1*x0^-1*x2^-1*x0*x2*x0*x1^-1*x0^-1, + x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > sage: A.meridians(simplified=False) {0: [x1, x2], 1: [x0], 2: [x3], 3: [x3^-1*x2^-1*x1^-1*x0^-1]} sage: A.fundamental_group(vertical=False) @@ -484,21 +485,23 @@ def fundamental_group(self, simplified=True, vertical=True, < x0, x1, x2 | x2^-1*x1^-1*x2*x1, x1*x0*x1^-1*x0^-1, (x0*x2)^2*(x0^-1*x2^-1)^2 > sage: A.meridians(vertical=False) {0: [x2, x0*x2*x0^-1], 1: [x1], 2: [x0], 3: [x0*x2^-1*x0^-1*x2^-1*x1^-1*x0^-1]} - sage: A.fundamental_group(simplified=False, vertical=False) + sage: G = A.fundamental_group(simplified=False, vertical=False) + sage: G.sorted_presentation() Finitely presented group - < x0, x1, x2, x3 | x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, - x1*x0*x1^-1*x0^-1, - x3^-1*x2^-1*x0^-1*x2*x3*x2^-1*x0*x2*x3*x2^-1, - x3^-1*(x2^-1*x0*x2*x3)^2*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0^-1*x2, - x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2 > + < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, + x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, + (x3^-1*x2^-1*x0^-1*x2)^2*(x3*x2^-1*x0*x2)^2, + x3^-1*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0*x2*x3*x2, + x1^-1*x0^-1*x1*x0 > sage: A.meridians(simplified=False, vertical=False) {0: [x2, x3], 1: [x1], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1]} sage: A = H(x * y^2 + x + y, y + x -1, x, y) - sage: A.fundamental_group() + sage: G = A.fundamental_group() + sage: G.sorted_presentation() Finitely presented group - < x0, x1, x2, x3 | x3*x0^-1*x3^-1*x0, x3*x1*x3^-1*x1^-1, - x3*x2*x3^-1*x2^-1, x2*x0*x2^-1*x0^-1, - x1*x0^-1*x1^-1*x0, x1*x2*x1^-1*x2^-1 > + < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1, + x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, + x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > .. WARNING:: @@ -791,12 +794,15 @@ def fundamental_group(self, simplified=True): Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > sage: A.meridians() {0: [x1], 1: [x1^-1*x0^-1*x1^-1], 2: [x0]} - sage: A.fundamental_group(simplified=False) + sage: G = A.fundamental_group(simplified=False) + sage: G.sorted_presentation() Finitely presented group - < x0, x1, x2, x3 | x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, - x1*x0*x1^-1*x0^-1, x3^-1*x2^-1*x0^-1*x2*x3*x2^-1*x0*x2*x3*x2^-1, - x3^-1*(x2^-1*x0*x2*x3)^2*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0^-1*x2, - x3*x2^-1*x1*x2*x3^-1*x2^-1*x1^-1*x2, x0*x1*x2*x3 > + < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x0^-1, + x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, + x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, + (x3^-1*x2^-1*x0^-1*x2)^2*(x3*x2^-1*x0*x2)^2, + x3^-1*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0*x2*x3*x2, + x1^-1*x0^-1*x1*x0 > sage: A.meridians(simplified=False) {0: [x2, x3], 1: [x1], 2: [x0]} sage: A = H(y^2 + x*z, z, x) From 38cf9230563e02278bdbf649257f7d47b1a86d8c Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 27 Nov 2023 17:49:49 +0100 Subject: [PATCH 58/95] make faster the computation in the projective case --- .../hyperplane_arrangement/arrangement.py | 34 +++++++++-------- .../schemes/curves/plane_curve_arrangement.py | 24 ++++++------ src/sage/schemes/curves/zariski_vankampen.py | 38 ++++++++++--------- 3 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index f4a5cb0eccd..5cc43cc62dd 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -3941,24 +3941,26 @@ def projective_fundamental_group(self): Finitely presented group < x0, x1 | > sage: A3. = OrderedHyperplaneArrangements(QQ) sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) # optional - sage.graphs - sage: G3 = H.projective_fundamental_group(); G3 # optional - sage.graphs + sage: G3 = H.projective_fundamental_group(); G3.sorted_presentation() # optional - sage.graphs 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 > + < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1, + x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0, + x4^-1*x1^-1*x0^-1*x4*x0*x1, + x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2, + x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2, + x3^-1*x1^-1*x3*x1 > sage: G3.abelian_invariants() # optional - sage.graphs (0, 0, 0, 0, 0) sage: A4. = OrderedHyperplaneArrangements(QQ) sage: H = A4(hyperplane_arrangements.braid(4)) # optional - sage.graphs - sage: G4 = H.projective_fundamental_group(); G4 # optional - sage.graphs + sage: G4 = H.projective_fundamental_group(); G4.sorted_presentation() # optional - sage.graphs 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 > + < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1, + x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0, + x4^-1*x1^-1*x0^-1*x4*x0*x1, + x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2, + x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2, + x3^-1*x1^-1*x3*x1 > sage: G4.abelian_invariants() # optional - sage.graphs (0, 0, 0, 0, 0) sage: L. = OrderedHyperplaneArrangements(QQ) @@ -4029,13 +4031,13 @@ def projective_meridians(self): sage: A3. = OrderedHyperplaneArrangements(QQ) sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) # optional - sage.graphs sage: H.projective_meridians() # optional - sage.graphs - {0: [x4], 1: [x0], 2: [x1], 3: [x2], 4: [x3], - 5: [x0^-1*x4^-1*x3^-1*x1^-1*x2^-1]} + {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], + 1: [x3], 2: [x4], 3: [x1], 4: [x2], 5: [x0]} sage: A4. = OrderedHyperplaneArrangements(QQ) sage: H = A4(hyperplane_arrangements.braid(4)) # optional - sage.graphs sage: H.projective_meridians() # optional - sage.graphs - {0: [x4], 1: [x0], 2: [x1], 3: [x0^-1*x4^-1*x3^-1*x1^-1*x2^-1], - 4: [x3], 5: [x2]} + {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], 1: [x3], + 2: [x4], 3: [x0], 4: [x2], 5: [x1]} sage: L. = OrderedHyperplaneArrangements(QQ) sage: H = hyperplane_arrangements.coordinate(5) sage: H = L(H) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 686cb7e8c35..175830f2409 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -790,21 +790,19 @@ def fundamental_group(self, simplified=True): sage: # needs sirocco sage: H. = ProjectivePlaneCurveArrangements(QQ) sage: A = H(y^2 + x*z, y + x - z, x) - sage: A.fundamental_group() - Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > + sage: A.fundamental_group().sorted_presentation() + Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 > sage: A.meridians() - {0: [x1], 1: [x1^-1*x0^-1*x1^-1], 2: [x0]} + {0: [x1], 1: [x0], 2: [x1^-1*x0^-1*x1^-1]} sage: G = A.fundamental_group(simplified=False) sage: G.sorted_presentation() Finitely presented group - < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x0^-1, - x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, - x3^-1*x2^-1*x1^-1*x2*x3*x2^-1*x1*x2, - (x3^-1*x2^-1*x0^-1*x2)^2*(x3*x2^-1*x0*x2)^2, - x3^-1*x2^-1*x0^-1*x2*x3^-1*x2^-1*x0*x2*x3*x2, - x1^-1*x0^-1*x1*x0 > + < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x0^-1, x3^-1*x2^-1*x3*x0*x1*x0^-1, + x3^-1*x1^-1*x3*x0*x1*x0^-1*x2^-1*x0^-1*(x2*x0)^2*x1^-1*x0^-1, + x3^-1*x0^-1*x3*x0*x1*x0^-1*x2^-1*x0*x2*x0*x1^-1*x0^-1, + x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > sage: A.meridians(simplified=False) - {0: [x2, x3], 1: [x1], 2: [x0]} + {0: [x1, x2], 1: [x0], 2: [x3]} sage: A = H(y^2 + x*z, z, x) sage: A.fundamental_group() Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > @@ -897,10 +895,10 @@ def meridians(self, simplified=True): sage: # needs sirocco sage: H. = ProjectivePlaneCurveArrangements(QQ) sage: A = H(y^2 + x*z, y + x - z, x) - sage: A.fundamental_group() - Finitely presented group < x0, x1 | x1^-1*x0*x1*x0^-1 > + sage: A.fundamental_group().sorted_presentation() + Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 > sage: A.meridians() - {0: [x1], 1: [x1^-1*x0^-1*x1^-1], 2: [x0]} + {0: [x1], 1: [x0], 2: [x1^-1*x0^-1*x1^-1]} sage: A = H(y^2 + x*z, z, x) sage: A.fundamental_group() Finitely presented group < x0, x1 | (x1*x0)^2*(x1^-1*x0^-1)^2 > diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 3500ad0ea2f..c82ca9349d6 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1857,15 +1857,19 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, sage: g.sorted_presentation(), dic (Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 >, {0: [x0, x1], 1: [x1^-1*x0^-1]}) - sage: fundamental_group_arrangement([y + x^2], projective=True) + sage: fundamental_group_arrangement([y + x^2]) + (Finitely presented group < x | >, {0: [x]}) + sage: fundamental_group_arrangement([y^2 + x], projective=True) (Finitely presented group < x | x^2 >, {0: [x]}) sage: L = [x, y, x - 1, x -y] - sage: fundamental_group_arrangement(L) - (Finitely presented group - < x0, x1, x2, x3 | x2*x3^-1*x2^-1*x3, x0*x1*x3*x0^-1*x3^-1*x1^-1, - x3*x0*x1*x3^-1*x1^-1*x0^-1, - x1*x2*x1^-1*x0*x1*x2^-1*x1^-1*x0^-1 >, - {0: [x1], 1: [x3], 2: [x2], 3: [x0], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) + sage: G, dic =fundamental_group_arrangement(L) + sage: G.sorted_presentation() + Finitely presented group + < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x0^-1*x1*x3*x0, + x3^-1*x1^-1*x0^-1*x3*x0*x1, + x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1 > + sage: dic + {0: [x1], 1: [x3], 2: [x2], 3: [x0], 4: [x3^-1*x2^-1*x1^-1*x0^-1]} sage: fundamental_group_arrangement(L, vertical=True) (Finitely presented group < x0, x1, x2, x3 | x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1, @@ -1881,16 +1885,14 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, f = R(1) x, y = R.gens() flist1 = [_ for _ in flist] - d = f.degree() - vertical0 = bool(vertical) - if projective: - vertical0 = False - while f.degree(y) < d: - flist1 = [g.subs({x: x + y}) for g in flist1] - f = prod(flist1) - infinity = not vertical0 or all([Curve(g).is_vertical_line() or - g.degree(y) == g.degree() - for g in flist1]) + if vertical and vertical_lines_in_braidmon(flist1): + infinity = all([Curve(g).is_vertical_line() or + g.degree(y) == g.degree() for g in flist1]) + else: + infinity = any([Curve(g).has_vertical_asymptote() or + Curve(g).is_vertical_line() for g in flist1]) + if not infinity: + infinity = all([g.degree(y) == g.degree() for g in flist1]) if braid_data: bm, dic, dv, d1 = braid_data elif len(flist1) == 0: @@ -1899,7 +1901,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, dv = {j: j for j, f in flist1} d1 = 0 else: - bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical0) + bm, dic, dv, d1 = braid_monodromy(f, flist1, vertical=vertical) vert_lines = list(dv) vert_lines.sort() for i, j in enumerate(vert_lines): From 3770b4d3a3e10c14b4d03ff128329c9a270d2609 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 6 Dec 2023 16:56:28 +0100 Subject: [PATCH 59/95] small reviewer comments --- src/sage/schemes/curves/projective_curve.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 9c4e4a8900c..210e4123669 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1224,8 +1224,8 @@ def excellent_position(self, Q): P = [0]*3 P[i] = 1 P = PP(P) - ll = [0, 1, 2] - ll.pop(i) + l = [0, 1, 2] + l.pop(i) # choose points forming a triangle with one vertex at P to map to the coordinate triangle good = False a = 0 @@ -1233,11 +1233,11 @@ def excellent_position(self, Q): a = a + 1 # find points to map to (1 : 0 : 0) and (0 : 1 : 0), not on the curve Px = [0]*3 - Px[ll[0]] = a - Px[ll[1]] = 1 + Px[l[0]] = a + Px[l[1]] = 1 Py = [0]*3 - Py[ll[0]] = -a - Py[ll[1]] = 1 + Py[l[0]] = -a + Py[l[1]] = 1 Py[i] = 1 try: Px = baseC(Px) @@ -1778,7 +1778,7 @@ def fundamental_group(self): g = self.defining_polynomial() ring = self.ambient_space().affine_patch(2).coordinate_ring() if g.degree() == 1: - return fundamental_group(ring(1)) + return fundamental_group(ring.one()) f = ring(self.affine_patch(2).defining_polynomial()) if f.degree() == self.degree(): return fundamental_group(f, projective=True) From be89ec5cc999e4d91f21c2feaa2546b78c09abe5 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 7 Dec 2023 09:53:51 +0100 Subject: [PATCH 60/95] unnecessary call to braid_monodromy --- src/sage/schemes/curves/affine_curve.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 609d4d9042b..56b004f4d74 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1867,8 +1867,7 @@ def fundamental_group(self, simplified=True, puiseux=True): d = d0 + f0.degree(x) else: d = bm[0].parent().strands() - G = fundamental_group_from_braid_mon(self.braid_monodromy(), - degree=d, + G = fundamental_group_from_braid_mon(bm, degree=d, simplified=simplified, puiseux=puiseux) if simplified: From 281aa7694be88648f991017e27b31ea48ffc1aab Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 28 Dec 2023 14:37:25 +0100 Subject: [PATCH 61/95] changes from review --- .../en/reference/discrete_geometry/index.rst | 1 + src/sage/geometry/all.py | 2 +- .../hyperplane_arrangement/arrangement.py | 660 ++---------------- .../ordered_arrangement.py | 615 ++++++++++++++++ .../schemes/curves/plane_curve_arrangement.py | 17 +- src/sage/schemes/curves/zariski_vankampen.py | 22 +- 6 files changed, 697 insertions(+), 620 deletions(-) create mode 100644 src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py diff --git a/src/doc/en/reference/discrete_geometry/index.rst b/src/doc/en/reference/discrete_geometry/index.rst index 7c0fb57bb1e..c64c6d9cd8a 100644 --- a/src/doc/en/reference/discrete_geometry/index.rst +++ b/src/doc/en/reference/discrete_geometry/index.rst @@ -12,6 +12,7 @@ Hyperplane arrangements :maxdepth: 1 sage/geometry/hyperplane_arrangement/arrangement + sage/geometry/hyperplane_arrangement/ordered_arrangement sage/geometry/hyperplane_arrangement/library sage/geometry/hyperplane_arrangement/hyperplane sage/geometry/hyperplane_arrangement/affine_subspace diff --git a/src/sage/geometry/all.py b/src/sage/geometry/all.py index 263a530b989..e4b4d933bc4 100644 --- a/src/sage/geometry/all.py +++ b/src/sage/geometry/all.py @@ -16,5 +16,5 @@ lazy_import('sage.geometry.voronoi_diagram', 'VoronoiDiagram') lazy_import('sage.geometry.ribbon_graph', 'RibbonGraph') lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'HyperplaneArrangements') -lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'OrderedHyperplaneArrangements') +lazy_import('sage.geometry.hyperplane_arrangement.ordered_arrangement', 'OrderedHyperplaneArrangements') lazy_import('sage.geometry.hyperplane_arrangement.library', 'hyperplane_arrangements') diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 14ffcc5c51a..1e932b232b4 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -132,17 +132,6 @@ sage: b == hyperplane_arrangements.coordinate(3) True -In some cases ordered arrangements are useful and the :class:`OrderedHyperplaneArrangements` for which the -hyperplanes are not sorted:: - - sage: H0. = HyperplaneArrangements(QQ) - sage: H0(t0 - t1, t1 - t2, t0 - t2) - Arrangement - sage: H. = OrderedHyperplaneArrangements(QQ) - sage: H(t0 - t1, t1 - t2, t0 - t2) - Arrangement - - Properties of Arrangements -------------------------- @@ -309,8 +298,6 @@ low dimensions. See :meth:`~HyperplaneArrangementElement.plot` for details. -For ordered hyperplane arrangements hyperplane sections and fundamental group are also defined. - TESTS:: sage: H. = HyperplaneArrangements(QQ) @@ -364,18 +351,14 @@ # - create ties with the Sage matroid methods # - hyperplane arrangements over other fields +# from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane -from sage.misc.misc_c import prod -from sage.groups.free_group import FreeGroup 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.qqbar import QQbar from sage.rings.rational_field import QQ -from sage.schemes.curves.plane_curve_arrangement import AffinePlaneCurveArrangements -from sage.schemes.curves.plane_curve_arrangement import ProjectivePlaneCurveArrangements from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import richcmp @@ -403,10 +386,9 @@ def __init__(self, parent, hyperplanes, check=True, backend=None): - ``hyperplanes`` -- a tuple of hyperplanes - - ``check`` -- boolean (optional; default ``True``); whether - to check input + - ``check`` -- boolean (default: ``True``); whether to check input - - ``backend`` -- string (optional; default: ``None``); the backend to + - ``backend`` -- string (optional); the backend to use for the related polyhedral objects EXAMPLES:: @@ -517,7 +499,11 @@ def hyperplanes(self): OUTPUT: - A tuple. + A tuple + + REMARK: + + It applies also to ordered arrangements.. EXAMPLES:: @@ -672,22 +658,19 @@ def union(self, other): A new hyperplane arrangement. + REMARK: + + It applies also to ordered arrangements. + EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) - sage: H1. = OrderedHyperplaneArrangements(QQ) sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0]) sage: B = H([1,1,1], [1,-1,1], [1,0,-1]) sage: C = A.union(B); C Arrangement of 8 hyperplanes of dimension 2 and rank 2 sage: C == A | B # syntactic sugar True - sage: A1 = H1(A) - sage: B1 = H1(B) - sage: C1 = A1.union(B1); C1 - Arrangement of 8 hyperplanes of dimension 2 and rank 2 - sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()] - [0, 5, 6, 1, 2, 3, 7, 4] A single hyperplane is coerced into a hyperplane arrangement if necessary:: @@ -751,16 +734,14 @@ def cone(self, variable='t'): no guarantee that the order in which they appear in ``self.hyperplanes()`` will match the order in which their counterparts in ``self.cone()`` will appear in - ``self.cone().hyperplanes()``! + ``self.cone().hyperplanes()``! This warning does not apply + to ordered hyperplane arrangements, EXAMPLES:: sage: # needs sage.combinat sage: a. = hyperplane_arrangements.semiorder(3) - sage: H. = OrderedHyperplaneArrangements(QQ) - sage: a1 = H(a) sage: b = a.cone() - sage: b1 = a1.cone() sage: a.characteristic_polynomial().factor() x * (x^2 - 6*x + 12) sage: b.characteristic_polynomial().factor() @@ -780,8 +761,6 @@ 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.hyperplanes().index(h) for h in b.hyperplanes()] - [0, 2, 4, 6, 1, 3, 5] """ hyperplanes = [] for h in self.hyperplanes(): @@ -791,6 +770,7 @@ def cone(self, variable='t'): P = self.parent() names = (variable,) + P._names if 'Ordered' in str(type(self)): + from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements H = OrderedHyperplaneArrangements(self.parent().base_ring(), names=names) else: H = HyperplaneArrangements(self.parent().base_ring(), names=names) @@ -1268,25 +1248,19 @@ def restriction(self, hyperplane, repetitions=False): The restriction `\mathcal{A}_H` of the hyperplane arrangement `\mathcal{A}` to the given ``hyperplane`` `H`. + REMARK: + + It applies also to ordered arrangements. + EXAMPLES:: sage: # needs sage.graphs sage: A. = hyperplane_arrangements.braid(4); A Arrangement of 6 hyperplanes of dimension 4 and rank 3 - sage: L. = OrderedHyperplaneArrangements(QQ) - sage: A1 = L(A) sage: H = A[0]; H Hyperplane 0*u + 0*x + y - z + 0 sage: R = A.restriction(H); R Arrangement - sage: A1.restriction(H, repetitions=True).hyperplanes() - (Hyperplane 0*u + x - z + 0, - Hyperplane 0*u + x - z + 0, - Hyperplane u - x + 0*z + 0, - Hyperplane u + 0*x - z + 0, - Hyperplane u + 0*x - z + 0) - sage: A1.restriction(H) - Arrangement sage: A.add_hyperplane(z).restriction(z) Arrangement of 6 hyperplanes of dimension 3 and rank 3 sage: A.add_hyperplane(u).restriction(u) @@ -1335,6 +1309,7 @@ def restriction(self, hyperplane, repetitions=False): names = list(parent._names) names.pop(pivot) if 'Ordered' in str(type(self)): + from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements H = OrderedHyperplaneArrangements(parent.base_ring(), names=tuple(names)) if not repetitions: L = list(hyperplanes) @@ -2058,8 +2033,9 @@ def regions(self): EXAMPLES:: - sage: a = hyperplane_arrangements.braid(2) # needs sage.graphs - sage: a.regions() # needs sage.graphs + sage: # needs sage.graphs + 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 @@ -2223,10 +2199,10 @@ def poset_of_regions(self, B=None, numbered_labels=True): INPUT: - - ``B`` -- a region (optional; default: ``None``); if ``None``, then + - ``B`` -- a region (optional); if ``None``, then an arbitrary region is chosen as the base region. - - ``numbered_labels`` -- bool (optional; default: ``True``); if ``True``, + - ``numbered_labels`` -- bool (default: ``True``); if ``True``, then the elements of the poset are numbered. Else they are labelled with the regions themselves. @@ -2236,9 +2212,10 @@ def poset_of_regions(self, B=None, numbered_labels=True): EXAMPLES:: + sage: # needs sage.combinat sage: H. = HyperplaneArrangements(QQ) sage: A = H([[0,1,1,1], [0,1,2,3]]) - sage: A.poset_of_regions() # needs sage.combinat + sage: A.poset_of_regions() Finite poset containing 4 elements sage: # needs sage.combinat sage.graphs @@ -2251,11 +2228,12 @@ def poset_of_regions(self, B=None, numbered_labels=True): sage: A.poset_of_regions() Finite poset containing 24 elements + sage: # needs sage.combinat 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) # needs sage.combinat + sage: A.poset_of_regions(B=base_region) Finite poset containing 14 elements """ from sage.combinat.posets.posets import Poset @@ -2410,12 +2388,13 @@ def closed_faces(self, labelled=True): ((-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) # needs sage.graphs - sage: a.hyperplanes() # needs sage.graphs + sage: # needs sage.graphs + sage: a = hyperplane_arrangements.braid(3) + sage: a.hyperplanes() (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()] # needs sage.graphs + 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 @@ -2447,16 +2426,17 @@ def closed_faces(self, labelled=True): dimension computed using ``self.closed_faces()`` equals the one computed using :meth:`face_vector`:: + sage: # needs sage.combinat sage: def test_number(a): ....: Qx = PolynomialRing(QQ, 'x'); x = Qx.gen() ....: RHS = Qx.sum(vi * x ** i for i, vi in enumerate(a.face_vector())) ....: 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) # needs sage.combinat + sage: test_number(a) True sage: a = hyperplane_arrangements.Shi(3) - sage: test_number(a) # long time # needs sage.combinat + sage: test_number(a) # long time True TESTS: @@ -2729,7 +2709,8 @@ def face_semigroup_algebra(self, field=None, names='e'): sage: (e3 + 2*e4) * (e1 - e7) e4 - e6 - sage: U3 = a.face_semigroup_algebra(field=GF(3)); U3 # needs sage.graphs sage.rings.finite_rings + sage: # needs sage.graphs sage.rings.finite_rings + sage: U3 = a.face_semigroup_algebra(field=GF(3)); U3 Finite-dimensional algebra of degree 13 over Finite Field of size 3 TESTS: @@ -2971,8 +2952,9 @@ def whitney_data(self): EXAMPLES:: + sage:# needs sage.combinat sage: A = hyperplane_arrangements.Shi(3) - sage: A.whitney_data() # needs sage.combinat + sage: A.whitney_data() ( [ 1 -6 9] [ 1 6 6] [ 0 6 -15] [ 0 6 15] @@ -3280,9 +3262,10 @@ def matroid(self): We check the lattice of flats is isomorphic to the intersection lattice:: + sage: # needs sage.combinat sage: f = sum([list(M.flats(i)) for i in range(M.rank() + 1)], []) - sage: PF = Poset([f, lambda x, y: x < y]) # needs sage.combinat - sage: PF.is_isomorphic(A.intersection_poset()) # needs sage.combinat + sage: PF = Poset([f, lambda x, y: x < y]) + sage: PF.is_isomorphic(A.intersection_poset()) True """ if not self.is_central(): @@ -3461,9 +3444,10 @@ def derivation_module_free_chain(self): EXAMPLES:: - sage: W = WeylGroup(['A',3], prefix='s') # needs sage.combinat sage.groups - sage: A = W.long_element().inversion_arrangement() # needs sage.combinat sage.groups - sage: for M in A.derivation_module_free_chain(): print("%s\n"%M) # needs sage.combinat sage.groups + sage: # needs sage.combinat sage.groups + 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) [ 1 0 0] [ 0 1 0] [ 0 0 a3] @@ -3537,8 +3521,9 @@ 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') # needs sage.combinat sage.groups - sage: for x in W: # needs sage.combinat sage.groups + sage: # needs sage.combinat sage.groups + sage: W = WeylGroup(['A', 3], prefix='s') + sage: for x in W: ....: A = x.inversion_arrangement() ....: assert A.matroid().is_chordal() == A.is_free() @@ -3546,8 +3531,9 @@ def is_free(self, algorithm="singular"): We check that the algorithms agree:: - sage: W = WeylGroup(['B', 3], prefix='s') # needs sage.combinat sage.groups - sage: for x in W: # long time # needs sage.combinat sage.groups + sage: # needs sage.combinat sage.groups + sage: W = WeylGroup(['B', 3], prefix='s') + sage: for x in W: # long time ....: A = x.inversion_arrangement() ....: assert (A.is_free(algorithm="BC") ....: == A.is_free(algorithm="singular")) @@ -3606,19 +3592,21 @@ def derivation_module_basis(self, algorithm="singular"): EXAMPLES:: - sage: W = WeylGroup(['A', 2], prefix='s') # needs sage.combinat sage.groups - sage: A = W.long_element().inversion_arrangement() # needs sage.combinat sage.groups - sage: A.derivation_module_basis() # needs sage.combinat sage.groups + sage: # needs sage.combinat sage.groups + sage: W = WeylGroup(['A', 2], prefix='s') + sage: A = W.long_element().inversion_arrangement() + sage: A.derivation_module_basis() [(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') # needs sage.combinat sage.groups + sage: # needs sage.combinat sage.groups + sage: W = WeylGroup(['A', 2], prefix='s') sage: def exponents(B): ....: return sorted([max(x.degree() for x in b) for b in B]) - sage: for x in W: # long time # needs sage.combinat sage.groups + sage: for x in W: # long time ....: A = x.inversion_arrangement() ....: B = A.derivation_module_basis(algorithm="singular") ....: Bp = A.derivation_module_basis(algorithm="BC") @@ -3663,395 +3651,6 @@ def derivation_module_basis(self, algorithm="singular"): raise ValueError("invalid algorithm") -class OrderedHyperplaneArrangementElement(HyperplaneArrangementElement): - """ - An ordered hyperplane arrangement. - - .. WARNING:: - - You should never create - :class:`OrderedHyperplaneArrangementElement` instances directly, - always use the parent. - """ - - def __init__(self, parent, hyperplanes, check=True, backend=None): - """ - Construct an ordered hyperplane arrangement. - - INPUT: - - - ``parent`` -- the parent :class:`OrderedHyperplaneArrangements` - - - ``hyperplanes`` -- a tuple of hyperplanes - - - ``check`` -- boolean (optional; default ``True``); whether - to check input - - - ``backend`` -- string (optional; default: ``None``); the backend to - use for the related polyhedral objects - - EXAMPLES:: - - sage: H. = OrderedHyperplaneArrangements(QQ) - sage: elt = H(x, y); elt - Arrangement - sage: TestSuite(elt).run() - - It is possible to specify a backend for polyhedral computations:: - - sage: # needs sage.rings.number_field - sage: R. = QuadraticField(5) - sage: H = OrderedHyperplaneArrangements(R, names='xyz') - sage: x, y, z = H.gens() - sage: A = H(sqrt5*x + 2*y + 3*z, backend='normaliz') - sage: A.backend() - 'normaliz' - sage: A.regions()[0].backend() # optional - pynormaliz - 'normaliz' - """ - super().__init__(parent, hyperplanes, check=check, backend=backend) - # self._hyperplanes = hyperplanes - # self._backend = backend - # if check: - # if not isinstance(hyperplanes, tuple): - # raise ValueError("the hyperplanes must be given as a tuple") - # if not all(isinstance(h, Hyperplane) for h in hyperplanes): - # raise ValueError("not all elements are hyperplanes") - # if not all(h.parent() is self.parent().ambient_space() for h in hyperplanes): - # raise ValueError("not all hyperplanes are in the ambient space") - self._affine_fundamental_group_ = None - self._affine_meridians_ = None - self._projective_fundamental_group_ = None - self._projective_meridians_ = None - - 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. - - EXAMPLES:: - - sage: A0. = hyperplane_arrangements.braid(4); A0 # optional - sage.graphs - Arrangement of 6 hyperplanes of dimension 4 and rank 3 - sage: L. = OrderedHyperplaneArrangements(QQ) - sage: A = L(A0) # optional - sage.graphs - sage: M = A.matroid() # optional - sage.graphs - sage: A1 = A.hyperplane_section() # optional - sage.graphs - sage: A1 # optional - sage.graphs - Arrangement of 6 hyperplanes of dimension 3 and rank 3 - sage: M1 = A1.matroid() # optional - sage.graphs - sage: A2 = 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_isomorphic(M2) # optional - sage.graphs - True - sage: T1.isomorphism(M2) # optional - sage.graphs - {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5} - sage: a0 = hyperplane_arrangements.semiorder(3); a0 # optional - sage.combinat - Arrangement of 6 hyperplanes of dimension 3 and rank 2 - sage: L. = OrderedHyperplaneArrangements(QQ) - sage: a = L(a0) # optional - sage.combinat - sage: ca = a.cone() # optional - sage.combinat - sage: m = ca.matroid() # optional - sage.combinat - sage: a1 = a.hyperplane_section(proj=False) # optional - sage.combinat - sage: a1 # optional - sage.combinat - Arrangement of 6 hyperplanes of dimension 2 and rank 2 - sage: ca1 = a1.cone() # optional - sage.combinat - sage: m1 = ca1.matroid() # optional - sage.combinat - sage: m.isomorphism(m1) # optional - sage.combinat - {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6} - sage: p0 = hyperplane_arrangements.Shi(4) # optional - sage.combinat - sage: L. = OrderedHyperplaneArrangements(QQ) - sage: p = L(p0) # optional - sage.combinat - sage: a = p.hyperplane_section(proj=False); a # optional - sage.combinat - Arrangement of 12 hyperplanes of dimension 3 and rank 3 - sage: ca = a.cone() # optional - sage.combinat - sage: m = ca.matroid().truncation() # optional - sage.combinat - sage: a1 = a.hyperplane_section(proj=False); a1 # optional - sage.combinat - Arrangement of 12 hyperplanes of dimension 2 and rank 2 - sage: ca1 = a1.cone() # optional - sage.combinat - sage: m1 = ca1.matroid() # optional - sage.combinat - sage: m1.is_isomorphism(m, {j: j for j in range(13)}) # 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() - if not proj: - H = self.cone() - H1 = H.hyperplane_section() - mat = Matrix(h.coefficients()[1:] for h in H1) - m = mat.nrows() - for j in range(mat.ncols()): - if mat[m - 1, j] != 0: - mat.swap_columns(0, j) - break - for j in range(1, mat.ncols()): - mat.add_multiple_of_column(j, 0, -mat[m - 1, j] / mat[m - 1, 0]) - vrs = H1.parent().variable_names()[1:] - A1 = OrderedHyperplaneArrangements(self.base_ring(), names=vrs) - mat_rows = mat.rows()[:-1] - H1b = A1(mat_rows) - return H1b - 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) - - def affine_fundamental_group(self): - r""" - It computes the fundamental group of the complement of an affine - hyperplane arrangement in `\mathbb{C}^n` whose equations have - coefficients in a subfield of `\overline{\mathbb{Q}}` - - OUTPUT: - - A finitely presented group. - - EXAMPLES:: - - sage: # needs sirocco - sage: A. = OrderedHyperplaneArrangements(QQ) - sage: L = [y + x, y + x - 1] - sage: H = A(L) - sage: H.affine_fundamental_group() - Finitely presented group < x0, x1 | > - sage: L = [x, y, x + 1, y + 1, x - y] - sage: A(L).affine_fundamental_group() - Finitely presented group - < x0, x1, x2, x3, x4 | x4*x0*x4^-1*x0^-1, - x0*x2*x3*x2^-1*x0^-1*x3^-1, - x1*x2*x4*x2^-1*x1^-1*x4^-1, - x2*x3*x0*x2^-1*x0^-1*x3^-1, - x2*x4*x1*x2^-1*x1^-1*x4^-1, - x4*x1*x4^-1*x3^-1*x2^-1*x1^-1*x2*x3 > - sage: H = A(x, y, x + y) - sage: H.affine_fundamental_group() - Finitely presented group - < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-1 > - - .. WARNING:: - - This functionality requires the sirocco package to be installed. - """ - K = self.base_ring() - if not K.is_subring(QQbar): - raise TypeError('the base field is not in QQbar') - if self._affine_fundamental_group_: - return self._affine_fundamental_group_ - n = self.dimension() - r = len(self) - if n == 0: - print("Only empty arrangements for dimension ", n) - return None - if n == 1: - G = FreeGroup(r) / [] - dic = {j: G.gen(j) for j in range(r)} - dic[r] = [prod(G.gens()) ** -1] - self._affine_fundamental_group_ = G - self._affine_meridians_ = dic - return G - if n == 2: - S = self.parent().ambient_space().symmetric_space() - coord = vector((1,) + S.gens()) - Af = AffinePlaneCurveArrangements(K, - names=self.parent().variable_names()) - L = Af([vector(line.coefficients()) * coord for line in self]) - G = L.fundamental_group() - self._affine_fundamental_group_ = G - self._affine_meridians_ = L._meridians_vertical_simplified - return G - H1 = self.hyperplane_section(proj=False) - G = H1.affine_fundamental_group() - self._affine_fundamental_group_ = G - self._affine_meridians_ = H1._affine_meridians_ - return G - - def affine_meridians(self): - r""" - It assigns to each hyperplane (including the one at infinity) - a meridian in the fundamental group, - of the complement, for a hyperplane arrangement in - `\mathbb{C}^n` whose equations have - coefficients in a subfield of `\overline{\mathbb{Q}}` - - OUTPUT: - - A dictionary - - EXAMPLES:: - - sage: # needs sirocco - sage: A. = OrderedHyperplaneArrangements(QQ) - sage: L = [y + x, y + x - 1] - sage: H = A(L) - sage: H.affine_meridians() - {0: [x0], 1: [x1], 2: [x1^-1*x0^-1]} - sage: L = [x, y, x + 1, y + 1, x - y] - sage: H = A(L) - sage: H.affine_meridians() - {0: [x4], 1: [x1], 2: [x3], 3: [x0], 4: [x2], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} - sage: H = A(x, y, x + y) - sage: H.affine_meridians() - {0: [x2], 1: [x1], 2: [x0], 3: [x2^-1*x1^-1*x0^-1]} - - .. WARNING:: - - This functionality requires the sirocco package to be installed. - """ - if not self._affine_meridians_: - self.affine_fundamental_group() - return self._affine_meridians_ - - def projective_fundamental_group(self): - r""" - It computes the fundamental group of the complement of a projective - hyperplane arrangement in `\mathbb{P}^n` whose equations have - coefficients in a subfield of `\overline{\mathbb{Q}}`. - - OUTPUT: - - A finitely presented group. - - EXAMPLES:: - - sage: # needs sirocco - sage: A. = OrderedHyperplaneArrangements(QQ) - sage: H = A(x, y, x + y) - sage: H.projective_fundamental_group() - Finitely presented group < x0, x1 | > - sage: A3. = OrderedHyperplaneArrangements(QQ) - sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) # optional - sage.graphs - sage: G3 = H.projective_fundamental_group(); G3.sorted_presentation() # optional - sage.graphs - Finitely presented group - < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1, - x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0, - x4^-1*x1^-1*x0^-1*x4*x0*x1, - x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2, - x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2, - x3^-1*x1^-1*x3*x1 > - sage: G3.abelian_invariants() # optional - sage.graphs - (0, 0, 0, 0, 0) - sage: A4. = OrderedHyperplaneArrangements(QQ) - sage: H = A4(hyperplane_arrangements.braid(4)) # optional - sage.graphs - sage: G4 = H.projective_fundamental_group(); G4.sorted_presentation() # optional - sage.graphs - Finitely presented group - < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1, - x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0, - x4^-1*x1^-1*x0^-1*x4*x0*x1, - x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2, - x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2, - x3^-1*x1^-1*x3*x1 > - sage: G4.abelian_invariants() # optional - sage.graphs - (0, 0, 0, 0, 0) - sage: L. = OrderedHyperplaneArrangements(QQ) - sage: H = hyperplane_arrangements.coordinate(5) - sage: H = L(H) - sage: g = H.projective_fundamental_group() - sage: g.is_abelian(), g.abelian_invariants() - (True, (0, 0, 0, 0)) - - .. WARNING:: - - This functionality requires the sirocco package to be installed. - """ - K = self.base_ring() - if not K.is_subring(QQbar): - raise TypeError('the base field is not in QQbar') - if not self.is_central(): - raise TypeError('the arrangement is not projective') - if self._projective_fundamental_group_: - return self._projective_fundamental_group_ - n = self.dimension() - r = len(self) - if n == 1: - print("Only empty arrangements for dimension ", n - 1) - return None - if n == 2: - G = FreeGroup(r - 1) / [] - dic = {j: G.gen(j) for j in range(r - 1)} - dic[r - 1] = [prod(G.gens()) ** -1] - self._projective_fundamental_group_ = G - self._projective_meridians_ = dic - return G - if n == 3: - S = self.parent().ambient_space().symmetric_space() - coord = vector(S.gens()) - Proj = ProjectivePlaneCurveArrangements(K, - names=self.parent().variable_names()) - L = Proj([vector(line.coefficients()[1:]) * coord for line in self]) - G = L.fundamental_group() - self._projective_fundamental_group_ = G - self._projective_meridians_ = L._meridians_simplified - return G - H1 = self.hyperplane_section() - G = H1.projective_fundamental_group() - self._projective_fundamental_group_ = G - self._projective_meridians_ = H1._projective_meridians_ - return G - - def projective_meridians(self): - r""" - It assigns to each hyperplane - a meridian in the fundamental group, - of the complement, for a hyperplane arrangement in - `\mathbb{P}^n` whose equations have - coefficients in a subfield of `\overline{\mathbb{Q}}` - - OUTPUT: - - A dictionary - - EXAMPLES:: - - sage: # needs sirocco - sage: A. = OrderedHyperplaneArrangements(QQ) - sage: H = A(x, y, x + y) - sage: H.projective_meridians() - {0: x0, 1: x1, 2: [x1^-1*x0^-1]} - sage: A3. = OrderedHyperplaneArrangements(QQ) - sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) # optional - sage.graphs - sage: H.projective_meridians() # optional - sage.graphs - {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], - 1: [x3], 2: [x4], 3: [x1], 4: [x2], 5: [x0]} - sage: A4. = OrderedHyperplaneArrangements(QQ) - sage: H = A4(hyperplane_arrangements.braid(4)) # optional - sage.graphs - sage: H.projective_meridians() # optional - sage.graphs - {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], 1: [x3], - 2: [x4], 3: [x0], 4: [x2], 5: [x1]} - sage: L. = OrderedHyperplaneArrangements(QQ) - sage: H = hyperplane_arrangements.coordinate(5) - sage: H = L(H) - sage: H.projective_meridians() - {0: [x2], 1: [x3], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1], 4: [x1]} - - .. WARNING:: - - This functionality requires the sirocco package to be installed. - """ - if not self._projective_meridians_: - self.projective_fundamental_group() - return self._projective_meridians_ - - class HyperplaneArrangements(Parent, UniqueRepresentation): """ Hyperplane arrangements. @@ -4195,15 +3794,15 @@ def _element_constructor_(self, *args, **kwds): hyperplane; alternatively, a single polytope or a single hyperplane arrangement - - ``signed`` -- boolean (optional, default: ``True``); whether to + - ``signed`` -- boolean (default: ``True``); whether to preserve signs of hyperplane equations - - ``warn_duplicates`` -- boolean (optional, default: ``False``); + - ``warn_duplicates`` -- boolean (default: ``False``); whether to issue a warning if duplicate hyperplanes were passed -- note that duplicate hyperplanes are always removed, whether or not there is a warning shown - - ``check`` -- boolean (optional, default: ``True``); whether to + - ``check`` -- boolean (default: ``True``); whether to perform argument checking. EXAMPLES:: @@ -4373,136 +3972,3 @@ def _coerce_map_from_(self, P): if isinstance(P, HyperplaneArrangements): return self.base_ring().has_coerce_map_from(P.base_ring()) return super()._coerce_map_from_(P) - - -class OrderedHyperplaneArrangements(HyperplaneArrangements): - """ - Ordered Hyperplane arrangements. - - For more information on hyperplane arrangements, see - :mod:`sage.geometry.hyperplane_arrangement.arrangement`. - - INPUT: - - - ``base_ring`` -- ring; the base ring - - - ``names`` -- tuple of strings; the variable names - - EXAMPLES:: - - sage: H. = HyperplaneArrangements(QQ) - sage: x - Hyperplane x + 0*y + 0 - sage: x + y - Hyperplane x + y + 0 - sage: H(x, y, x-1, y-1) - Arrangement - """ - Element = OrderedHyperplaneArrangementElement - - def _element_constructor_(self, *args, **kwds): - """ - Repetition of the corresponding function for hyperplane arrangements without reordering. - - INPUT: - - - ``*args`` -- positional arguments, each defining a - hyperplane; alternatively, a single polytope or a single - hyperplane arrangement - - - ``signed`` -- boolean (optional, default: ``True``); whether to - preserve signs of hyperplane equations - - - ``check`` -- boolean (optional, default: ``True``); whether to - perform argument checking. - - EXAMPLES:: - - sage: L. = OrderedHyperplaneArrangements(QQ) - sage: L._element_constructor_(x, y) - Arrangement - sage: L._element_constructor_([x, y]) - Arrangement - sage: L._element_constructor_([0, 1, 0], [0, 0, 1]) - Arrangement - sage: L._element_constructor_([[0, 0, 1], [0, 1, 0]]) - Arrangement - - sage: L._element_constructor_(polytopes.hypercube(2)) - Arrangement <-x + 1 | -y + 1 | x + 1 | y + 1> - - sage: L(-x, x + y - 1, signed=False) - Arrangement - - TESTS:: - - sage: L() - Empty hyperplane arrangement of dimension 2 - sage: L(0) # zero is equivalent to no argument, Issue #8648 - Empty hyperplane arrangement of dimension 2 - sage: L(0*x) # degenerate hyperplane is NOT allowed - Traceback (most recent call last): - ... - ValueError: linear expression must be non-constant to define a hyperplane - sage: L(0*x, y) # ditto - Traceback (most recent call last): - ... - ValueError: linear expression must be non-constant to define a hyperplane - """ - if len(args) == 1: - arg = args[0] - if isinstance(arg, HyperplaneArrangementElement) and args[0].parent() is self: - # optimization if argument is already a hyperplane arrangement - return arg - if arg == 0 and not isinstance(arg, Hyperplane): - # zero = neutral element under addition = the empty hyperplane arrangement - args = [] - # process keyword arguments - not_char2 = (self.base_ring().characteristic() != 2) - signed = kwds.pop('signed', not_char2) - warn_duplicates = kwds.pop('warn_duplicates', False) - check = kwds.pop('check', True) - backend = kwds.pop('backend', None) - if len(kwds) > 0: - raise ValueError('unknown keyword argument') - # process positional arguments - AA = self.ambient_space() - try: - hyperplanes = [AA(_) for _ in args] - except (TypeError, ValueError, AttributeError): - if len(args) > 1: - raise - arg = args[0] - if hasattr(arg, 'Hrepresentation'): - hyperplanes = [AA(h) for h in arg.Hrepresentation()] - else: - hyperplanes = [AA(_) for _ in arg] - hyperplanes = [h.primitive(signed) for h in hyperplanes] - if warn_duplicates: - from warnings import warn - warn('Input contained repeated hyperplanes.') - # argument checking (optional but recommended) - if check: - if signed and not not_char2: - raise ValueError('cannot be signed in characteristic 2') - for h in hyperplanes: - if h.A() == 0: - raise ValueError('linear expression must be non-constant to define a hyperplane') - if not_char2 and -h in hyperplanes: - raise ValueError('arrangement cannot simultaneously have h and -h as hyperplane') - return self.element_class(self, tuple(hyperplanes), backend=backend) - - def _repr_(self): - """ - Return a string representation. - - OUTPUT: - - A string. - - EXAMPLES:: - - sage: L. = OrderedHyperplaneArrangements(QQ); L - Ordered hyperplane arrangements in 2-dimensional linear space over Rational Field with coordinates x, y - """ - return 'Ordered hyperplane arrangements in {0}'.format(self.ambient_space()) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py new file mode 100644 index 00000000000..89cc5a1a9be --- /dev/null +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -0,0 +1,615 @@ +r""" +Ordered Hyperplane Arrangements + +The :class:`HyperplaneArrangements` ordered the arrangements in a hyperplane +independently of the way the hyperplanes are introduced. With the class +:class:`OrderedHyperplaneArrangements` it is allowed to fix an order by +the user, what is needed for some properties, e.g., topological one. +Besides this fact, it inherits the rest of the properties from :class:`HyperplaneArrangements.` + +Ordered arrangements +-------------------- + +An ordered arrangement is an arrangement where the hyperplanes are sorted +by the user:: + + sage: H0. = HyperplaneArrangements(QQ) + sage: H0(t0 - t1, t1 - t2, t0 - t2) + Arrangement + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: H(t0 - t1, t1 - t2, t0 - t2) + Arrangement + +Some methods are adapted and some new ones are created, regarding +hyperplane sections and fundamental groups, e.g., ``hyperplanes``:: + + sage: H. = HyperplaneArrangements(QQ) + sage: H1. = OrderedHyperplaneArrangements(QQ) + sage: A1 = H1(x, y); A = H(A1) + sage: A.hyperplanes() + (Hyperplane 0*x + y + 0, Hyperplane x + 0*y + 0) + sage: A1.hyperplanes() + (Hyperplane x + 0*y + 0, Hyperplane 0*x + y + 0) + +We see the differences in ``union``:: + + sage: H. = HyperplaneArrangements(QQ) + sage: H1. = OrderedHyperplaneArrangements(QQ) + sage: A = H([1,2,3], [0,1,1], [0,1,-1], [1,-1,0], [1,1,0]) + sage: B = H([1,1,1], [1,-1,1], [1,0,-1]) + sage: C = A.union(B) + sage: A1 = H1(A); B1 = H1(B); C1 = A1.union(B1) + sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()] + [0, 5, 6, 1, 2, 3, 7, 4] + +Also in ``cone``:: + + sage: # needs sage.combinat + sage: a. = hyperplane_arrangements.semiorder(3) + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: a1 = H(a) + sage: b = a.cone(); b1 = a1.cone() + sage: [b1.hyperplanes().index(h) for h in b.hyperplanes()] + [0, 2, 4, 6, 1, 3, 5] + +And in ``restriction``:: + + sage: # needs sage.graphs + sage: A. = hyperplane_arrangements.braid(4) + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: A1 = L(A) + sage: H = A[0]; H + Hyperplane 0*u + 0*x + y - z + 0 + sage: A.restriction(H) + Arrangement + sage: A1.restriction(H) + Arrangement + sage: A1.restriction(H, repetitions=True) + Arrangement of 5 hyperplanes of dimension 3 and rank 2 + +AUTHORS: + +- Enrique Artal (2023-12): initial version + +This module add some features to the *unordered* one for some +properties which depend on the order. +""" + +# ***************************************************************************** +# Copyright (C) 2023 Enrique Artal +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License 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.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangementElement +from sage.geometry.hyperplane_arrangement.arrangement import HyperplaneArrangements +from sage.geometry.hyperplane_arrangement.hyperplane import Hyperplane +from sage.matrix.constructor import matrix, vector +from sage.misc.misc_c import prod +from sage.groups.free_group import FreeGroup +from sage.rings.integer_ring import ZZ +from sage.rings.qqbar import QQbar +from sage.schemes.curves.plane_curve_arrangement import AffinePlaneCurveArrangements +from sage.schemes.curves.plane_curve_arrangement import ProjectivePlaneCurveArrangements + + +class OrderedHyperplaneArrangementElement(HyperplaneArrangementElement): + """ + An ordered hyperplane arrangement. + + .. WARNING:: + + You should never create + :class:`OrderedHyperplaneArrangementElement` instances directly, + always use the parent. + """ + + def __init__(self, parent, hyperplanes, check=True, backend=None): + """ + Construct an ordered hyperplane arrangement. + + INPUT: + + - ``parent`` -- the parent :class:`OrderedHyperplaneArrangements` + + - ``hyperplanes`` -- a tuple of hyperplanes + + - ``check`` -- boolean (optional; default ``True``); whether + to check input + + - ``backend`` -- string (optional; default: ``None``); the backend to + use for the related polyhedral objects + + EXAMPLES:: + + sage: H. = OrderedHyperplaneArrangements(QQ) + sage: elt = H(x, y); elt + Arrangement + sage: TestSuite(elt).run() + """ + super().__init__(parent, hyperplanes, check=check, backend=backend) + # self._hyperplanes = hyperplanes + # self._backend = backend + # if check: + # if not isinstance(hyperplanes, tuple): + # raise ValueError("the hyperplanes must be given as a tuple") + # if not all(isinstance(h, Hyperplane) for h in hyperplanes): + # raise ValueError("not all elements are hyperplanes") + # if not all(h.parent() is self.parent().ambient_space() for h in hyperplanes): + # raise ValueError("not all hyperplanes are in the ambient space") + self._affine_fundamental_group = None + self._affine_meridians = None + self._projective_fundamental_group = None + self._projective_meridians = None + + def hyperplane_section(self, proj=True): + r""" + Compute a generic hyperplane section of ``self``. + + INPUT: + + - ``proj`` -- (default: ``True``); if the + ambient space is affine or projective + + OUTPUT: + + An arrangement `\mathcal{A}` obtained by intersecting with a + generic hyperplane + + EXAMPLES:: + + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L(x, y - 1, z).hyperplane_section() + Traceback (most recent call last): + ... + TypeError: the arrangement is not projective + + sage: # needs sage.graphs + sage: A0. = hyperplane_arrangements.braid(4); A0 + Arrangement of 6 hyperplanes of dimension 4 and rank 3 + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: A = L(A0) + sage: M = A.matroid() + sage: A1 = A.hyperplane_section() + sage: A1 + Arrangement of 6 hyperplanes of dimension 3 and rank 3 + sage: M1 = A1.matroid() + sage: A2 = 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_isomorphic(M2) + True + sage: T1.isomorphism(M2) + {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5} + + sage: # needs sage.combinat + sage: a0 = hyperplane_arrangements.semiorder(3); a0 + Arrangement of 6 hyperplanes of dimension 3 and rank 2 + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: a = L(a0) + sage: ca = a.cone() + sage: m = ca.matroid() + sage: a1 = a.hyperplane_section(proj=False) + sage: a1 + Arrangement of 6 hyperplanes of dimension 2 and rank 2 + sage: ca1 = a1.cone() + sage: m1 = ca1.matroid() + sage: m.isomorphism(m1) + {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6} + sage: p0 = hyperplane_arrangements.Shi(4) + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: p = L(p0) + sage: a = p.hyperplane_section(proj=False); a + Arrangement of 12 hyperplanes of dimension 3 and rank 3 + sage: ca = a.cone() + sage: m = ca.matroid().truncation() + sage: a1 = a.hyperplane_section(proj=False); a1 + Arrangement of 12 hyperplanes of dimension 2 and rank 2 + sage: ca1 = a1.cone() + sage: m1 = ca1.matroid() + sage: m1.is_isomorphism(m, {j: j for j in range(13)}) + True + """ + if proj and not self.is_linear(): + raise TypeError('the arrangement is not projective') + n0 = self.dimension() + if not proj: + H = self.cone() + H1 = H.hyperplane_section() + mat = matrix(h.coefficients()[1:] for h in H1) + m = mat.nrows() + for j in range(mat.ncols()): + if mat[m - 1, j] != 0: + mat.swap_columns(0, j) + break + for j in range(1, mat.ncols()): + mat.add_multiple_of_column(j, 0, -mat[m - 1, j] / mat[m - 1, 0]) + vrs = H1.parent().variable_names()[1:] + A1 = OrderedHyperplaneArrangements(self.base_ring(), names=vrs) + mat_rows = mat.rows()[:-1] + H1b = A1(mat_rows) + return H1b + 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) + + def affine_fundamental_group(self): + r""" + Return the fundamental group of the complement of an affine + hyperplane arrangement in `\CC^n` whose equations have + coefficients in a subfield of `\overline{\QQ}.` + + OUTPUT: + + A finitely presented fundamental group`. + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: L = [y + x, y + x - 1] + sage: H = A(L) + sage: H.affine_fundamental_group() + Finitely presented group < x0, x1 | > + sage: L = [x, y, x + 1, y + 1, x - y] + sage: A(L).affine_fundamental_group() + Finitely presented group + < x0, x1, x2, x3, x4 | x4*x0*x4^-1*x0^-1, + x0*x2*x3*x2^-1*x0^-1*x3^-1, + x1*x2*x4*x2^-1*x1^-1*x4^-1, + x2*x3*x0*x2^-1*x0^-1*x3^-1, + x2*x4*x1*x2^-1*x1^-1*x4^-1, + x4*x1*x4^-1*x3^-1*x2^-1*x1^-1*x2*x3 > + sage: H = A(x, y, x + y) + sage: H.affine_fundamental_group() + Finitely presented group + < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-1 > + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + K = self.base_ring() + if not K.is_subring(QQbar): + raise TypeError('the base field is not in QQbar') + if self._affine_fundamental_group: + return self._affine_fundamental_group + n = self.dimension() + r = len(self) + if n == 0: + raise TypeError('Only empty arrangements for dimension ', n) + if n == 1: + G = FreeGroup(r) / [] + dic = {j: G.gen(j) for j in range(r)} + dic[r] = [prod(G.gens()) ** -1] + self._affine_fundamental_group = G + self._affine_meridians = dic + return G + if n == 2: + S = self.parent().ambient_space().symmetric_space() + coord = vector((1,) + S.gens()) + Af = AffinePlaneCurveArrangements(K, + names=self.parent().variable_names()) + L = Af([vector(line.coefficients()) * coord for line in self]) + G = L.fundamental_group() + self._affine_fundamental_group = G + self._affine_meridians = L._meridians_vertical_simplified + return G + H1 = self.hyperplane_section(proj=False) + G = H1.affine_fundamental_group() + self._affine_fundamental_group = G + self._affine_meridians = H1._affine_meridians + return G + + def affine_meridians(self): + r""" + Return the meridians of each hyperplane (including the one at infinity) + + OUTPUT: + + A dictionary + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: L = [y + x, y + x - 1] + sage: H = A(L) + sage: H.affine_meridians() + {0: [x0], 1: [x1], 2: [x1^-1*x0^-1]} + sage: L = [x, y, x + 1, y + 1, x - y] + sage: H = A(L) + sage: H.affine_meridians() + {0: [x4], 1: [x1], 2: [x3], 3: [x0], 4: [x2], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} + sage: H = A(x, y, x + y) + sage: H.affine_meridians() + {0: [x2], 1: [x1], 2: [x0], 3: [x2^-1*x1^-1*x0^-1]} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not self._affine_meridians: + self.affine_fundamental_group() + return self._affine_meridians + + def projective_fundamental_group(self): + r""" + Return the fundamental group of the complement of a projective + hyperplane arrangement. + + OUTPUT: + + The finitely presented group of the complement + in `\PP^n` whose equations have + coefficients in a subfield of `\bar\QQ`. + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(x, y, x + y) + sage: H.projective_fundamental_group() + Finitely presented group < x0, x1 | > + + sage: # needs sirocco sage.graphs + sage: A3. = OrderedHyperplaneArrangements(QQ) + sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) + sage: G3 = H.projective_fundamental_group(); G3.sorted_presentation() + Finitely presented group + < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1, + x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0, + x4^-1*x1^-1*x0^-1*x4*x0*x1, + x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2, + x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2, + x3^-1*x1^-1*x3*x1 > + sage: G3.abelian_invariants() + (0, 0, 0, 0, 0) + sage: A4. = OrderedHyperplaneArrangements(QQ) + sage: H = A4(hyperplane_arrangements.braid(4)) + sage: G4 = H.projective_fundamental_group(); G4.sorted_presentation() + Finitely presented group + < x0, x1, x2, x3, x4 | x4^-1*x3^-1*x2^-1*x3*x4*x0*x2*x0^-1, + x4^-1*x2^-1*x4*x2, x4^-1*x1^-1*x0^-1*x1*x4*x0, + x4^-1*x1^-1*x0^-1*x4*x0*x1, + x4^-1*x1^-1*x3*x0*x1*x3^-1*x2^-1*x4*x0^-1*x2, + x3^-1*x2^-1*x1^-1*x0^-1*x3*x0*x1*x2, + x3^-1*x1^-1*x3*x1 > + sage: G4.abelian_invariants() + (0, 0, 0, 0, 0) + + sage: # needs sirocco + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: H = hyperplane_arrangements.coordinate(5) + sage: H = L(H) + sage: g = H.projective_fundamental_group() + sage: g.is_abelian(), g.abelian_invariants() + (True, (0, 0, 0, 0)) + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + K = self.base_ring() + if not K.is_subring(QQbar): + raise TypeError('the base field is not in QQbar') + if not self.is_central(): + raise TypeError('the arrangement is not projective') + if self._projective_fundamental_group: + return self._projective_fundamental_group + n = self.dimension() + r = len(self) + if n == 1: + print("Only empty arrangements for dimension ", n - 1) + return None + if n == 2: + G = FreeGroup(r - 1) / [] + dic = {j: G.gen(j) for j in range(r - 1)} + dic[r - 1] = [prod(G.gens()) ** -1] + self._projective_fundamental_group = G + self._projective_meridians = dic + return G + if n == 3: + S = self.parent().ambient_space().symmetric_space() + coord = vector(S.gens()) + Proj = ProjectivePlaneCurveArrangements(K, + names=self.parent().variable_names()) + L = Proj([vector(line.coefficients()[1:]) * coord for line in self]) + G = L.fundamental_group() + self._projective_fundamental_group = G + self._projective_meridians = L._meridians_simplified + return G + H1 = self.hyperplane_section() + G = H1.projective_fundamental_group() + self._projective_fundamental_group = G + self._projective_meridians = H1._projective_meridians + return G + + def projective_meridians(self): + r""" + Return the meridian of each hyperplane + + OUTPUT: + + A dictionary + + EXAMPLES:: + + sage: # needs sirocco + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(x, y, x + y) + sage: H.projective_meridians() + {0: x0, 1: x1, 2: [x1^-1*x0^-1]} + + sage: # needs sirocco sage.graphs + sage: A3. = OrderedHyperplaneArrangements(QQ) + sage: H = A3(hyperplane_arrangements.braid(4).essentialization()) + sage: H.projective_meridians() + {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], + 1: [x3], 2: [x4], 3: [x1], 4: [x2], 5: [x0]} + sage: A4. = OrderedHyperplaneArrangements(QQ) + sage: H = A4(hyperplane_arrangements.braid(4)) + sage: H.projective_meridians() + {0: [x2^-1*x0^-1*x4^-1*x3^-1*x1^-1], 1: [x3], + 2: [x4], 3: [x0], 4: [x2], 5: [x1]} + + sage: # needs sirocco + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: H = hyperplane_arrangements.coordinate(5) + sage: H = L(H) + sage: H.projective_meridians() + {0: [x2], 1: [x3], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1], 4: [x1]} + + .. WARNING:: + + This functionality requires the sirocco package to be installed. + """ + if not self._projective_meridians: + self.projective_fundamental_group() + return self._projective_meridians + + +class OrderedHyperplaneArrangements(HyperplaneArrangements): + """ + Ordered Hyperplane arrangements. + + For more information on hyperplane arrangements, see + :mod:`sage.geometry.hyperplane_arrangement.arrangement`. + + INPUT: + + - ``base_ring`` -- ring; the base ring + + - ``names`` -- tuple of strings; the variable names + + EXAMPLES:: + + sage: H. = HyperplaneArrangements(QQ) + sage: x + Hyperplane x + 0*y + 0 + sage: x + y + Hyperplane x + y + 0 + sage: H(x, y, x-1, y-1) + Arrangement + """ + Element = OrderedHyperplaneArrangementElement + + def _element_constructor_(self, *args, **kwds): + """ + Repetition of the corresponding function for hyperplane arrangements without reordering. + + INPUT: + + - ``*args`` -- positional arguments, each defining a + hyperplane; alternatively, a single polytope or a single + hyperplane arrangement + + - ``signed`` -- boolean (optional, default: ``True``); whether to + preserve signs of hyperplane equations + + - ``check`` -- boolean (optional, default: ``True``); whether to + perform argument checking. + + EXAMPLES:: + + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L._element_constructor_(x, y) + Arrangement + sage: L._element_constructor_([x, y]) + Arrangement + sage: L._element_constructor_([0, 1, 0], [0, 0, 1]) + Arrangement + sage: L._element_constructor_([[0, 0, 1], [0, 1, 0]]) + Arrangement + + sage: L._element_constructor_(polytopes.hypercube(2)) + Arrangement <-x + 1 | -y + 1 | x + 1 | y + 1> + + sage: L(-x, x + y - 1, signed=False) + Arrangement + + TESTS:: + + sage: L() + Empty hyperplane arrangement of dimension 2 + sage: L(0) # zero is equivalent to no argument, Issue #8648 + Empty hyperplane arrangement of dimension 2 + sage: L(0*x) # degenerate hyperplane is NOT allowed + Traceback (most recent call last): + ... + ValueError: linear expression must be non-constant to define a hyperplane + sage: L(0*x, y) # ditto + Traceback (most recent call last): + ... + ValueError: linear expression must be non-constant to define a hyperplane + """ + if len(args) == 1: + arg = args[0] + if isinstance(arg, HyperplaneArrangementElement) and args[0].parent() is self: + # optimization if argument is already a hyperplane arrangement + return arg + if arg == 0 and not isinstance(arg, Hyperplane): + # zero = neutral element under addition = the empty hyperplane arrangement + args = [] + # process keyword arguments + not_char2 = (self.base_ring().characteristic() != 2) + signed = kwds.pop('signed', not_char2) + warn_duplicates = kwds.pop('warn_duplicates', False) + check = kwds.pop('check', True) + backend = kwds.pop('backend', None) + if len(kwds) > 0: + raise ValueError('unknown keyword argument') + # process positional arguments + AA = self.ambient_space() + try: + hyperplanes = [AA(_) for _ in args] + except (TypeError, ValueError, AttributeError): + if len(args) > 1: + raise + arg = args[0] + if hasattr(arg, 'Hrepresentation'): + hyperplanes = [AA(h) for h in arg.Hrepresentation()] + else: + hyperplanes = [AA(_) for _ in arg] + hyperplanes = [h.primitive(signed) for h in hyperplanes] + if warn_duplicates: + from warnings import warn + warn('Input contained repeated hyperplanes.') + # argument checking (optional but recommended) + if check: + if signed and not not_char2: + raise ValueError('cannot be signed in characteristic 2') + for h in hyperplanes: + if h.A() == 0: + raise ValueError('linear expression must be non-constant to define a hyperplane') + if not_char2 and -h in hyperplanes: + raise ValueError('arrangement cannot simultaneously have h and -h as hyperplane') + return self.element_class(self, tuple(hyperplanes), backend=backend) + + def _repr_(self): + """ + Return a string representation. + + OUTPUT: + + A string. + + EXAMPLES:: + + sage: L. = OrderedHyperplaneArrangements(QQ); L + Ordered hyperplane arrangements in 2-dimensional linear space over Rational Field with coordinates x, y + """ + return 'Ordered hyperplane arrangements in {0}'.format(self.ambient_space()) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 175830f2409..f5bf8149c23 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Affine and Projective Plane Curve Arrangements @@ -15,9 +14,9 @@ sage: C[0].parent() -The default base field is `\mathbb{Q}`, the rational numbers. +The default base field is `\QQ`, the rational numbers. Number fields are also possible (also with fixed embeddings in -`\overline{\mathbb{Q}}`):: +`\QQbar`):: sage: # needs sage.rings.number_field sage: x = polygen(QQ, 'x') @@ -442,7 +441,7 @@ def fundamental_group(self, simplified=True, vertical=True, projective=False): r""" The fundamental group of the complement of the union - of affine plane curves in `\mathbb{C}^2`. + of affine plane curves in `\CC^2`. INPUT: @@ -601,7 +600,7 @@ def meridians(self, simplified=True, vertical=True): def braid_monodromy(self, vertical=True): r""" It computes the braid monodromy of the complement of the union - of affine plane curves in `\mathbb{C}^2`. If there are vertical + of affine plane curves in `\CC^2`. If there are vertical asymptotes a change of variable is done. INPUT: @@ -774,7 +773,7 @@ def __init__(self, parent, curves, check=True): def fundamental_group(self, simplified=True): r""" The fundamental group of the complement of the union - of projective plane curves in `\mathbb{P}^2`. + of projective plane curves in the projective plane. INPUT: @@ -930,7 +929,7 @@ def meridians(self, simplified=True): return self._meridians_simplified -class AffinePlaneCurveArrangements(Parent, UniqueRepresentation): +class AffinePlaneCurveArrangements(UniqueRepresentation, Parent): """ Affine curve arrangements. @@ -1087,10 +1086,6 @@ def _element_constructor_(self, *args, **kwds): arg = tuple(args[0]) else: arg = tuple(args) - # process keyword arguments - if len(kwds) > 0: - raise ValueError('unknown keyword argument') - # process positional arguments ambient_space = self.ambient_space() R = ambient_space.coordinate_ring() curves = () diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 405c959b5ab..126d2c92638 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -394,7 +394,7 @@ def voronoi_cells(V, vertical_lines=[]): - ``DG`` -- the dual graph of ``V``, where the vertices are labelled by the compact regions of ``V`` and the edges by their dual edges. - ``vertical_regions`` -- dictionary for the regions associated - with vertical lines. + with vertical lines EXAMPLES:: @@ -1007,20 +1007,20 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): 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]] - Intern = G.subgraph(vertices=InternalVertices, edges=InternalEdges) + 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 Intern: + if ECi in Internal: EI = [v for v in E if v in - Intern.connected_component_containing_vertex(ECi, sort=True) + 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 Intern: + if EC[-i] in Internal: EI = [v for v in E if v in - Intern.connected_component_containing_vertex(EC[-i], - sort=True) + Internal.connected_component_containing_vertex(EC[-i], + sort=True) and v != EC[-i]] if len(EI) > 0: q = EC[-i] @@ -1028,10 +1028,10 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): break # Precompute distances from q in E and I E_dist_q = E.shortest_path_lengths(q) - I_dist_q = Intern.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] r = max(distancequotients)[1] - cutpath = Intern.shortest_path(q, r) + cutpath = Internal.shortest_path(q, r) for i, v in enumerate(cutpath): if i > 0 and v in EC: r = v @@ -1218,8 +1218,8 @@ def braid_monodromy(f, arrangement=(), vertical=False): - ``f`` -- a polynomial with two variables, over a number field with an embedding in the complex numbers - - ``arrangement`` -- tuple (default: ``[]``); an optional tuple - of polynomials whose product equals ``f``. + - ``arrangement`` -- tuple (default: ``()``); an optional tuple + of polynomials whose product equals ``f`` - ``vertical`` -- boolean (default: ``False`); if set to ``True``, ``arrangements`` contains more than one polynomial, some of them From 6f552186420d831ceb044821fff2b093212f29ac Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 28 Dec 2023 15:03:52 +0100 Subject: [PATCH 62/95] forgotten change --- .../geometry/hyperplane_arrangement/ordered_arrangement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 89cc5a1a9be..34bd6c5d5ad 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -250,7 +250,7 @@ def affine_fundamental_group(self): r""" Return the fundamental group of the complement of an affine hyperplane arrangement in `\CC^n` whose equations have - coefficients in a subfield of `\overline{\QQ}.` + coefficients in a subfield of `\QQbar`. OUTPUT: @@ -354,7 +354,7 @@ def projective_fundamental_group(self): OUTPUT: The finitely presented group of the complement - in `\PP^n` whose equations have + in the projective space whose equations have coefficients in a subfield of `\bar\QQ`. EXAMPLES:: From 2fa5790f570a3be4dc5c91e9555f7701df62b0b1 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 28 Dec 2023 15:10:09 +0100 Subject: [PATCH 63/95] tex issue --- src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 34bd6c5d5ad..994a051115e 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -355,7 +355,7 @@ def projective_fundamental_group(self): The finitely presented group of the complement in the projective space whose equations have - coefficients in a subfield of `\bar\QQ`. + coefficients in a subfield of `\QQbar`. EXAMPLES:: From c221ea559245892589c39a821e579610c16f0f20 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 28 Dec 2023 17:13:42 +0100 Subject: [PATCH 64/95] more review changes --- .../ordered_arrangement.py | 12 +++++-- src/sage/schemes/curves/zariski_vankampen.py | 31 ++++++++++--------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 994a051115e..76df62b0672 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -235,9 +235,17 @@ def hyperplane_section(self, proj=True): H1b = A1(mat_rows) return H1b 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] + center = P.maximal_elements()[0].linear_part() + n1 = center.dimension() + U = [] + for p in P: + if p.dimension() == n1 + 1: + B = [u for u in p.linear_part().basis() if u not in center] + U.append(B[0]) + # U = [p.linear_part().basis()[0] for p in P if p.dimension() == n1 + 1] U0 = sum(U) + # We look for a linear hyperplane with integer coefficients + # defining a transversal hyperplane for v in ZZ**n0: v1 = v + U0 if 0 not in [w * v1 for w in U]: diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 126d2c92638..aa57f9eb1c1 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -372,7 +372,7 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False): print(prec) -def voronoi_cells(V, vertical_lines=[]): +def voronoi_cells(V, vertical_lines=()): 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. @@ -381,7 +381,7 @@ def voronoi_cells(V, vertical_lines=[]): - ``V`` -- a corrected Voronoi diagram - - ``vertical_lines`` -- list (default: ``[]``); indices of the + - ``vertical_lines`` -- list (default: ``()``); indices of the vertical lines OUTPUT: @@ -460,7 +460,7 @@ def voronoi_cells(V, vertical_lines=[]): regions = V.regions() points = [p for p in V.regions().keys() if V.regions()[p].is_compact()] compact_regions = [regions[p] for p in points] - vertical_regions = dict() + vertical_regions = {} 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)], @@ -638,9 +638,9 @@ def fieldI(field): sage: a0 = p.roots(QQbar, multiplicities=False)[0] sage: F0. = NumberField(p, embedding=a0) sage: fieldI(F0) - Number Field in primitif_element with defining polynomial + Number Field in prim 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 primitif_element = 0.4863890359345430? + 1.000000000000000?*I + with prim = 0.4863890359345430? + 1.000000000000000?*I If ``I`` is already in the field, the result is the field itself:: @@ -656,7 +656,7 @@ def fieldI(field): if I0 in field: return field field_a = field[I0] - field_b = field_a.absolute_field('imaginary_unit') + field_b = field_a.absolute_field('b0') b0 = field_b.gen() q = b0.minpoly() qembd = field_b.embeddings(QQbar) @@ -664,7 +664,7 @@ def fieldI(field): b1 = h1(b0) b2 = h1(field_b(field_a.gen(0))) b3 = field.gen(0) - F1 = NumberField(q, 'primitif_element', embedding=b1) + F1 = NumberField(q, 'prim', embedding=b1) if b3 in F1 and b2.imag() > 0: return F1 @@ -933,7 +933,7 @@ def braid_in_segment(glist, x0, x1, precision={}): return initialbraid * centralbraid * finalbraid -def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): +def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}): r""" Return a geometric basis, based on a vertex. @@ -952,10 +952,11 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): ``E`` is the boundary of the non-bounded component of the complement. The edges are labelled as the dual edges and the vertices are labelled by a tuple whose first element is the an integer for the position and the - second one is the cyclic ordered list of vertices in the region. + second one is the cyclic ordered list of vertices in the region - - ``vertical_regions`` -- dictionary with keys the vertices of - ``dual_graph`` to fix regions associated with vertical lines + - ``vertical_regions`` -- dictionary (default: `{}`); its keys are + the vertices of ``dual_graph`` to fix regions associated with + vertical lines OUTPUT: A geometric basis and a dictionary. @@ -973,7 +974,7 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): sage: points = (0, -1, I, 1, -I) sage: V = corrected_voronoi_diagram(points) sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=[0, 1, 2, 3, 4]) - sage: gb, vd = geometric_basis(G, E, EC, p, DG, VR) + sage: gb, vd = geometric_basis(G, E, EC, p, DG, vertical_regions=VR) sage: gb [[A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (-5/2, 5/2), A vertex at (-1/2, 1/2), A vertex at (-1/2, -1/2), A vertex at (1/2, -1/2), @@ -1098,8 +1099,8 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions): EC1 = list(EC[qi:] + EC[1:ri]) + list(reversed(cutpath)) EC2 = cutpath + list(EC[ri + 1:qi + 1]) - gb1, vd1 = geometric_basis(G1, E1, EC1, q, Gd1, vertical_regions) - gb2, vd2 = geometric_basis(G2, E2, EC2, q, Gd2, vertical_regions) + gb1, vd1 = geometric_basis(G1, E1, EC1, q, Gd1, vertical_regions=vertical_regions) + gb2, vd2 = geometric_basis(G2, E2, EC2, q, Gd2, vertical_regions=vertical_regions) vd = {j: vd1[j] for j in vd1} m = len(gb1) @@ -1350,7 +1351,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): i = strands[j] k = arrangement1.index(arrangement_h[i]) strands1[j] = k - geombasis, vd = geometric_basis(G, E, EC, p, DG, VR) + geombasis, vd = geometric_basis(G, E, EC, p, DG, vertical_regions=VR) segs = set() for p in geombasis: for s in zip(p[:-1], p[1:]): From 5f664ae5dfd8dc5474278e572771848b97477381 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 28 Dec 2023 21:50:49 +0100 Subject: [PATCH 65/95] more relevant codecov warnings in ordered_arrangement --- .../ordered_arrangement.py | 28 ++++++++++++++++++- src/sage/schemes/curves/zariski_vankampen.py | 4 +-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 76df62b0672..68225622cfe 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -285,6 +285,26 @@ def affine_fundamental_group(self): sage: H.affine_fundamental_group() Finitely presented group < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-1 > + sage: H.affine_fundamental_group() # repeat to use the attribute + Finitely presented group + < x0, x1, x2 | x0*x1*x2*x1^-1*x0^-1*x2^-1, x1*x2*x0*x1^-1*x0^-1*x2^-1 > + sage: T. = QQ[] + sage: K. = NumberField(t^3 + t + 1) + sage: L. = OrderedHyperplaneArrangements(K) + sage: H = L(a*x + y -1, x + a*y + 1, x - 1, y - 1) + sage: H.affine_fundamental_group() + Traceback (most recent call last): + ... + TypeError: the base field is not in QQbar + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L([t - j for j in range(4)]).affine_fundamental_group() + Finitely presented group < x0, x1, x2, x3 | > + sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L(L.gens() + (x + y + z + 1,)).affine_fundamental_group() + Finitely presented group + < x0, x1, x2, x3 | x1*x0*x1^-1*x0^-1, x2*x0*x2^-1*x0^-1, + x2*x1*x2^-1*x1^-1, x3*x0*x3^-1*x0^-1, + x3*x1*x3^-1*x1^-1, x3*x2*x3^-1*x2^-1 > .. WARNING:: @@ -406,6 +426,10 @@ def projective_fundamental_group(self): sage: g = H.projective_fundamental_group() sage: g.is_abelian(), g.abelian_invariants() (True, (0, 0, 0, 0)) + sage: L(t0, t1, t2, t3, t4, t0 - 1).projective_fundamental_group() + Traceback (most recent call last): + ... + TypeError: the arrangement is not projective .. WARNING:: @@ -414,7 +438,7 @@ def projective_fundamental_group(self): K = self.base_ring() if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') - if not self.is_central(): + if not self.is_linear(): raise TypeError('the arrangement is not projective') if self._projective_fundamental_group: return self._projective_fundamental_group @@ -534,6 +558,8 @@ def _element_constructor_(self, *args, **kwds): EXAMPLES:: sage: L. = OrderedHyperplaneArrangements(QQ) + sage: L._element_constructor_(x) + Arrangement sage: L._element_constructor_(x, y) Arrangement sage: L._element_constructor_([x, y]) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index aa57f9eb1c1..015f3ec0011 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -656,14 +656,14 @@ def fieldI(field): if I0 in field: return field field_a = field[I0] - field_b = field_a.absolute_field('b0') + field_b = field_a.absolute_field('b_0') b0 = field_b.gen() q = b0.minpoly() qembd = field_b.embeddings(QQbar) for h1 in qembd: b1 = h1(b0) b2 = h1(field_b(field_a.gen(0))) - b3 = field.gen(0) + b3 = QQbar(field.gen(0)) F1 = NumberField(q, 'prim', embedding=b1) if b3 in F1 and b2.imag() > 0: return F1 From 42fd46c0fe5d85eb53065a61e281f0ca2203b85f Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 29 Dec 2023 11:02:28 +0100 Subject: [PATCH 66/95] relevant codecov in plane_curve_arrangement --- .../ordered_arrangement.py | 8 ++-- .../schemes/curves/plane_curve_arrangement.py | 37 +++++++++++++------ 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 68225622cfe..71efe317b2f 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -300,11 +300,11 @@ def affine_fundamental_group(self): sage: L([t - j for j in range(4)]).affine_fundamental_group() Finitely presented group < x0, x1, x2, x3 | > sage: L. = OrderedHyperplaneArrangements(QQ) - sage: L(L.gens() + (x + y + z + 1,)).affine_fundamental_group() + sage: L(L.gens() + (x + y + z + 1,)).affine_fundamental_group().sorted_presentation() Finitely presented group - < x0, x1, x2, x3 | x1*x0*x1^-1*x0^-1, x2*x0*x2^-1*x0^-1, - x2*x1*x2^-1*x1^-1, x3*x0*x3^-1*x0^-1, - x3*x1*x3^-1*x1^-1, x3*x2*x3^-1*x2^-1 > + < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1, + x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, + x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > .. WARNING:: diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index f5bf8149c23..431046baa9e 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -295,6 +295,10 @@ def deletion(self, curves): sage: h.deletion(C) Arrangement (x*y, x + y + 1, -y^5 + x^3, x^5 + y^5 + x^2*y^2) in Affine Space of dimension 2 over Rational Field + sage: h.deletion(x) + Traceback (most recent call last): + ... + ValueError: curve is not in the arrangement """ parent = self.parent() curves = parent(curves) @@ -386,6 +390,8 @@ def have_common_factors(self): sage: A = H(x * y, x^2 + x* y^3) sage: A.have_common_factors() True + sage: H(x * y, x + y^3).have_common_factors() + False """ L = [c.defining_polynomial() for c in self] C = Combinations(L, 2) @@ -440,7 +446,7 @@ def reduce(self, clean=False): def fundamental_group(self, simplified=True, vertical=True, projective=False): r""" - The fundamental group of the complement of the union + Return the fundamental group of the complement of the union of affine plane curves in `\CC^2`. INPUT: @@ -551,7 +557,7 @@ def fundamental_group(self, simplified=True, vertical=True, def meridians(self, simplified=True, vertical=True): r""" - Meridians of each irreducible component. + Return the meridians of each irreducible component. OUTPUT: @@ -599,7 +605,7 @@ def meridians(self, simplified=True, vertical=True): def braid_monodromy(self, vertical=True): r""" - It computes the braid monodromy of the complement of the union + Return the braid monodromy of the complement of the union of affine plane curves in `\CC^2`. If there are vertical asymptotes a change of variable is done. @@ -653,7 +659,7 @@ def braid_monodromy(self, vertical=True): def strands(self): r""" - Strands for each member of the arrangement. + Return the strands for each member of the arrangement. OUTPUT: @@ -680,7 +686,8 @@ def strands(self): def vertical_strands(self): r""" - Vertical strands for each member of the arrangement. + Return the strands if the braid monodromy has been computed with + the vertical option. OUTPUT: @@ -693,9 +700,10 @@ def vertical_strands(self): sage: # needs sirocco sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) - sage: bm = A.braid_monodromy(vertical=True) sage: A.vertical_strands() {0: 1, 1: 0, 2: 0} + sage: A.braid_monodromy(vertical=True) + [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] .. WARNING:: @@ -707,7 +715,7 @@ def vertical_strands(self): def vertical_lines_in_braid_mon(self): r""" - Vertical lines in the arrangement. + Return the vertical lines in the arrangement. OUTPUT: @@ -719,10 +727,10 @@ def vertical_lines_in_braid_mon(self): sage: # needs sirocco sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) - sage: A.braid_monodromy(vertical=True) - [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] sage: A.vertical_lines_in_braid_mon() {1: 2} + sage: A.braid_monodromy(vertical=True) + [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] .. WARNING:: @@ -772,8 +780,9 @@ def __init__(self, parent, curves, check=True): def fundamental_group(self, simplified=True): r""" - The fundamental group of the complement of the union - of projective plane curves in the projective plane. + Return the fundamental group of the complement of the union + of an arragnement of projective plane curves + in the projective plane. INPUT: @@ -788,6 +797,10 @@ def fundamental_group(self, simplified=True): sage: # needs sirocco sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: H(z).fundamental_group() + Finitely presented group < | > + sage: H(x*y).fundamental_group() + Finitely presented group < x | > sage: A = H(y^2 + x*z, y + x - z, x) sage: A.fundamental_group().sorted_presentation() Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0 > @@ -881,7 +894,7 @@ def fundamental_group(self, simplified=True): def meridians(self, simplified=True): r""" - Meridians of each irreducible component. + Return the meridians of each irreducible component. OUTPUT: From 45df0a5310cea1d8c5028a22379f8ee7adac0175 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 29 Dec 2023 13:57:52 +0100 Subject: [PATCH 67/95] more codecov --- .../ordered_arrangement.py | 33 ++++---- src/sage/schemes/curves/affine_curve.py | 73 ++++++++++------- .../schemes/curves/plane_curve_arrangement.py | 81 +++++++++---------- src/sage/schemes/curves/projective_curve.py | 60 ++++++++------ src/sage/schemes/curves/zariski_vankampen.py | 5 ++ 5 files changed, 140 insertions(+), 112 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 71efe317b2f..fe0e068b771 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -264,6 +264,10 @@ def affine_fundamental_group(self): A finitely presented fundamental group`. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -305,10 +309,6 @@ def affine_fundamental_group(self): < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1, x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ K = self.base_ring() if not K.is_subring(QQbar): @@ -350,6 +350,10 @@ def affine_meridians(self): A dictionary + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -365,10 +369,6 @@ def affine_meridians(self): sage: H = A(x, y, x + y) sage: H.affine_meridians() {0: [x2], 1: [x1], 2: [x0], 3: [x2^-1*x1^-1*x0^-1]} - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if not self._affine_meridians: self.affine_fundamental_group() @@ -385,6 +385,10 @@ def projective_fundamental_group(self): in the projective space whose equations have coefficients in a subfield of `\QQbar`. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -430,10 +434,6 @@ def projective_fundamental_group(self): Traceback (most recent call last): ... TypeError: the arrangement is not projective - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ K = self.base_ring() if not K.is_subring(QQbar): @@ -478,6 +478,11 @@ def projective_meridians(self): A dictionary + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + + EXAMPLES:: sage: # needs sirocco @@ -504,10 +509,6 @@ def projective_meridians(self): sage: H = L(H) sage: H.projective_meridians() {0: [x2], 1: [x3], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1], 4: [x1]} - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if not self._projective_meridians: self.projective_fundamental_group() diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 6d72ab7ef86..3f0ab862bce 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -174,10 +174,11 @@ class AffineCurve(Curve_generic, AlgebraicScheme_subscheme_affine): EXAMPLES:: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(v^2 + 3) # needs sage.rings.number_field - sage: A. = AffineSpace(K, 3) # needs sage.rings.number_field - sage: C = Curve([z - u*x^2, y^2], A); C # needs sage.rings.number_field + sage: K. = NumberField(v^2 + 3) + sage: A. = AffineSpace(K, 3) + sage: C = Curve([z - u*x^2, y^2], A); C Affine Curve over Number Field in u with defining polynomial v^2 + 3 defined by (-u)*x^2 + z, y^2 @@ -194,10 +195,11 @@ def __init__(self, A, X): EXAMPLES:: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(v^2 + 3) # needs sage.rings.number_field - sage: A. = AffineSpace(K, 3) # needs sage.rings.number_field - sage: C = Curve([z - u*x^2, y^2], A); C # needs sage.rings.number_field + sage: K. = NumberField(v^2 + 3) + sage: A. = AffineSpace(K, 3) + sage: C = Curve([z - u*x^2, y^2], A); C Affine Curve over Number Field in u with defining polynomial v^2 + 3 defined by (-u)*x^2 + z, y^2 @@ -454,25 +456,28 @@ def plot(self, *args, **kwds): A cuspidal curve:: + sage: # needs sage.plot sage: R. = QQ[] sage: C = Curve(x^3 - y^2) - sage: C.plot() # needs sage.plot + sage: C.plot() Graphics object consisting of 1 graphics primitive A 5-nodal curve of degree 11. This example also illustrates some of the optional arguments:: + sage: # needs sage.plot() sage: R. = ZZ[] sage: C = Curve(32*x^2 - 2097152*y^11 + 1441792*y^9 ....: - 360448*y^7 + 39424*y^5 - 1760*y^3 + 22*y - 1) - sage: C.plot((x, -1, 1), (y, -1, 1), plot_points=400) # needs sage.plot + sage: C.plot((x, -1, 1), (y, -1, 1), plot_points=400) Graphics object consisting of 1 graphics primitive A line over `\mathbf{RR}`:: + sage: # needs sage.symbolic sage.plot sage: R. = RR[] - sage: C = Curve(R(y - sqrt(2)*x)) # needs sage.symbolic - sage: C.plot() # needs sage.plot + sage: C = Curve(R(y - sqrt(2)*x)) + sage: C.plot() Graphics object consisting of 1 graphics primitive """ Id = self.defining_ideal() @@ -843,10 +848,11 @@ def __init__(self, A, X): EXAMPLES:: + sage: # needs sage.rings.number_field sage: R. = QQ[] - sage: K. = NumberField(v^2 + 3) # needs sage.rings.number_field - sage: A. = AffineSpace(K, 3) # needs sage.rings.number_field - sage: C = Curve([z - u*x^2, y^2], A); C # needs sage.rings.number_field + sage: K. = NumberField(v^2 + 3) + sage: A. = AffineSpace(K, 3) + sage: C = Curve([z - u*x^2, y^2], A); C Affine Curve over Number Field in u with defining polynomial v^2 + 3 defined by (-u)*x^2 + z, y^2 @@ -1293,9 +1299,10 @@ def blowup(self, P=None): :: - sage: A. = AffineSpace(QuadraticField(-1), 2) # needs sage.rings.number_field - sage: C = A.curve([y^2 + x^2]) # needs sage.rings.number_field - sage: C.blowup() # needs sage.rings.number_field + sage: # needs sage.rings.number_field + sage: A. = AffineSpace(QuadraticField(-1), 2) + sage: C = A.curve([y^2 + x^2]) + sage: C.blowup() Traceback (most recent call last): ... TypeError: this curve must be irreducible @@ -1820,11 +1827,12 @@ def fundamental_group(self, simplified=True, puiseux=True): .. NOTE:: The curve must be defined over the rationals or a number field - with an embedding over `\QQbar`. + with an embedding over `\QQbar`. This functionality requires + the ``sirocco`` package to be installed. EXAMPLES:: - sage: # optional - sirocco + sage: # needs sirocco sage: A. = AffineSpace(QQ, 2) sage: C = A.curve(y^2 - x^3 - x^2) sage: C.fundamental_group(puiseux=False) @@ -1852,10 +1860,6 @@ def fundamental_group(self, simplified=True, puiseux=True): sage: C = A.curve(x * (x - 1)) sage: C.fundamental_group() Finitely presented group < x0, x1 | > - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ from sage.schemes.curves.zariski_vankampen import fundamental_group_from_braid_mon bm = self.braid_monodromy() @@ -1885,20 +1889,30 @@ def braid_monodromy(self): each of this paths is the conjugated of a loop around one of the points in the discriminant of the projection of ``self``. - NOTE: + .. 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. + This functionality requires the ``sirocco`` package to be installed. - 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: # needs sirocco sage: A. = AffineSpace(QQ, 2) sage: C = A.curve((x^2-y^3)*(x+3*y-5)) - sage: C.braid_monodromy() # optional - sirocco + sage: C.braid_monodromy() [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: T. = QQ[] + sage: K. = NumberField(t^3 + 2, 'a') + sage: A. = AffineSpace(K, 2) + sage: Curve(y^2 + a * x).braid_monodromy() + Traceback (most recent call last): + ... + NotImplementedError: the base field must have an embedding to the algebraic field """ from sage.schemes.curves.zariski_vankampen import braid_monodromy @@ -2103,9 +2117,10 @@ def function_field(self): :: - sage: A. = AffineSpace(GF(8), 2) # needs sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1) # needs sage.rings.finite_rings - sage: C.function_field() # needs sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(8), 2) + sage: C = Curve(x^5 + y^5 + x*y + 1) + sage: C.function_field() Function field in y defined by y^5 + x*y + x^5 + 1 """ return self._function_field diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 431046baa9e..609ddf13e60 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -326,10 +326,11 @@ def change_ring(self, base_ring): EXAMPLES:: + sage: # needs sage.rings.number_field sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) - sage: K. = CyclotomicField(3) # optional - sage.rings.number_field - sage: A.change_ring(K) # optional - sage.rings.number_field + sage: K. = CyclotomicField(3) + sage: A.change_ring(K) Arrangement (-x^3 + y^2, x, y, x^2 + x*y + y^2) in Affine Space of dimension 2 over Cyclotomic Field of order 3 and degree 2 """ @@ -465,6 +466,10 @@ def fundamental_group(self, simplified=True, vertical=True, A finitely presented group. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -507,10 +512,6 @@ def fundamental_group(self, simplified=True, vertical=True, < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1, x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if self._fundamental_group and not vertical and not simplified: return self._fundamental_group @@ -564,6 +565,12 @@ def meridians(self, simplified=True, vertical=True): A dictionary which associates the index of each curve with its meridians, including the line at infinity if it can be omputed + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed + and :func:`AffinePlaneCurveArrangements.fundamental_group` with the same options, + where some examples are shown. + EXAMPLES:: sage: # needs sirocco @@ -575,14 +582,6 @@ def meridians(self, simplified=True, vertical=True): x3*x0*x3^-1*x0^-1, x3*x1*x3^-1*x1^-1 > sage: A.meridians() {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]} - - This function needs - :func:`AffinePlaneCurveArrangements.fundamental_group` with the same options, - where some examples are shown. - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if not vertical and not simplified: computed = bool(self._meridians) @@ -620,6 +619,10 @@ def braid_monodromy(self, vertical=True): A braid monodromy with dictionnaries identifying strands with components and braids with vertical lines. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -633,10 +636,6 @@ def braid_monodromy(self, vertical=True): s1*s0*s1*s2*s1*s2^-1*s1^-1*s2*s0^-1*s1^-1] sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if not vertical and self._braid_monodromy is not None: return self._braid_monodromy @@ -667,6 +666,10 @@ def strands(self): its associated component if the braid monodromy has been calculated with ``vertical=False``. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -675,10 +678,6 @@ def strands(self): sage: bm = A.braid_monodromy() sage: A.strands() {0: 2, 1: 1, 2: 0, 3: 0} - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if not self._strands: self._braid_monodromy = self.braid_monodromy(vertical=False) @@ -695,6 +694,10 @@ def vertical_strands(self): its associated component if the braid monodromy has been calculated with ``vertical=True``. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -704,10 +707,6 @@ def vertical_strands(self): {0: 1, 1: 0, 2: 0} sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if not self._strands_with_vertical: self.braid_monodromy(vertical=True) @@ -722,6 +721,10 @@ def vertical_lines_in_braid_mon(self): A dictionary which associates the index of a braid to the index of the vertical line associated to the braid. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -731,10 +734,6 @@ def vertical_lines_in_braid_mon(self): {1: 2} sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if not self._vertical_lines_in_braid_mon: self.braid_monodromy(vertical=True) @@ -793,6 +792,10 @@ def fundamental_group(self, simplified=True): A finitely presented group. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: sage: # needs sirocco @@ -823,10 +826,6 @@ def fundamental_group(self, simplified=True): Finitely presented group < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if self._fundamental_group and not simplified: return self._fundamental_group @@ -902,6 +901,12 @@ def meridians(self, simplified=True): its meridians, including the line at infinity if it can be computed + .. NOTE:: + + This function requires the ``sirocco`` package to be installed and + :func:`ProjectivePlaneCurveArrangements.fundamental_group` + with the same options, where some examples are shown. + EXAMPLES:: sage: # needs sirocco @@ -922,14 +927,6 @@ def meridians(self, simplified=True): x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > sage: A.meridians() {0: [x0, x2*x0*x2^-1], 1: [x2, x0^-1*x2^-1*x1^-1*x0^-1], 2: [x1]} - - This function needs - :func:`ProjectivePlaneCurveArrangements.fundamental_group` - with the same options, where some examples are shown. - - .. WARNING:: - - This functionality requires the sirocco package to be installed. """ if not simplified: computed = bool(self._meridians) diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 94e2e9637ae..0f0f3fae082 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -781,32 +781,35 @@ def plot(self, *args, **kwds): The other affine patches of the same curve:: - sage: C.plot(patch=0) # needs sage.plot + sage: # needs sage.plot + sage: C.plot(patch=0) Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=1) # needs sage.plot + sage: C.plot(patch=1) Graphics object consisting of 1 graphics primitive An elliptic curve:: + sage: # needs sage.plot sage: E = EllipticCurve('101a') sage: C = Curve(E) - sage: C.plot() # needs sage.plot + sage: C.plot() Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=0) # needs sage.plot + sage: C.plot(patch=0) Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=1) # needs sage.plot + sage: C.plot(patch=1) Graphics object consisting of 1 graphics primitive A hyperelliptic curve:: + sage: # needs sage.plot sage: P. = QQ[] sage: f = 4*x^5 - 30*x^3 + 45*x - 22 sage: C = HyperellipticCurve(f) - sage: C.plot() # needs sage.plot + sage: C.plot() Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=0) # needs sage.plot + sage: C.plot(patch=0) Graphics object consisting of 1 graphics primitive - sage: C.plot(patch=1) # needs sage.plot + sage: C.plot(patch=1) Graphics object consisting of 1 graphics primitive """ # if user has not specified a favorite affine patch, take the @@ -862,16 +865,17 @@ def is_singular(self, P=None): Over `\CC`:: + sage: # needs sage.rings.function_field sage: F = CC sage: P2. = ProjectiveSpace(F, 2) sage: C = Curve(X) - sage: C.is_singular() # needs sage.rings.function_field + sage: C.is_singular() False sage: D = Curve(Y^2*Z - X^3) - sage: D.is_singular() # needs sage.rings.function_field + sage: D.is_singular() True sage: E = Curve(Y^2*Z - X^3 + Z^3) - sage: E.is_singular() # needs sage.rings.function_field + sage: E.is_singular() False Showing that :trac:`12187` is fixed:: @@ -883,10 +887,11 @@ def is_singular(self, P=None): :: + sage: # needs sage.fings.function_field sage: P. = ProjectiveSpace(CC, 2) sage: C = Curve([y^4 - x^3*z], P) sage: Q = P([0,0,1]) - sage: C.is_singular() # needs sage.rings.function_field + sage: C.is_singular() True """ if P is None: @@ -1725,17 +1730,24 @@ def fundamental_group(self): The curve must be defined over the rationals or a number field with an embedding over `\QQbar`. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. + EXAMPLES:: + sage: # needs sirocco sage: P. = ProjectiveSpace(QQ, 2) sage: C = P.curve(x^2*z - y^3) - sage: C.fundamental_group() # optional - sirocco + sage: C.fundamental_group() Finitely presented group < x0 | x0^3 > + sage: P.curve(z*(x^2*z - y^3)).fundamental_group() + Finitely presented group < x0, x1 | x1*x0*x1*x0^-1*x1^-1*x0^-1 > In the case of number fields, they need to have an embedding into the algebraic field:: - - sage: # needs sage.rings.number_field +. + sage: # needs sage.rings.number_field sirocco sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] sage: a -2.236067977499790?*I @@ -1744,15 +1756,12 @@ def fundamental_group(self): sage: F.inject_variables() Defining a sage: C = P.curve(x^2 + a * y^2) - sage: C.fundamental_group() # optional - sirocco + sage: C.fundamental_group() Finitely presented group < x0 | > - .. WARNING:: - - This functionality requires the ``sirocco`` package to be installed. - TESTS:: + sage: # needs sirocco sage: F. = FreeGroup() sage: G = F / [x1^-1*(x1^-1*x0^-1*x1*x0^-1)^2, (x1^-1*x0^-1)^2*x1^-1*(x0*x1)^2*x0] sage: G.order() @@ -1761,8 +1770,8 @@ 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: G0 = C.fundamental_group() # optional - sirocco - sage: G.is_isomorphic(G0) # optional - sirocco + sage: G0 = C.fundamental_group() + sage: G.is_isomorphic(G0) #I Forcing finiteness test True sage: C = P.curve(z) @@ -2296,9 +2305,10 @@ def _genus(self): EXAMPLES:: - sage: P. = ProjectiveSpace(GF(4), 2) # needs sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) # needs sage.rings.finite_rings - sage: C.genus() # indirect doctest # needs sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(4), 2) + sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) + sage: C.genus() # indirect doctest 1 """ return self._open_affine.genus() diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 015f3ec0011..d031d87379c 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -128,6 +128,7 @@ def sgn(x, y): if x > y: return -1 return 0 + for i in range(len(totalpoints[0]) - 1): l1 = [totalpoints[j][i] for j in range(len(L))] l2 = [totalpoints[j][i + 1] for j in range(len(L))] @@ -1148,6 +1149,8 @@ def vertical_lines_in_braidmon(flist): sage: flist += [x * y - 1] sage: vertical_lines_in_braidmon(flist) [] + sage: vertical_lines_in_braidmon([]) + [] """ if not flist: return [] @@ -1613,6 +1616,8 @@ def fundamental_group_from_braid_mon(bm, degree=None, True sage: fundamental_group_from_braid_mon([], degree=2) Finitely presented group < x0, x1 | > + sage: fundamental_group_from_braid_mon([SymmetricGroup(1).one()]) + Finitely presented group < x | > """ vertical0 = sorted(vertical) v = len(vertical0) From 416df4979e92fd3939e0b9832caf04c50a418c8b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 29 Dec 2023 14:15:37 +0100 Subject: [PATCH 68/95] typo --- src/sage/schemes/curves/projective_curve.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 0f0f3fae082..99bef58fad1 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1746,7 +1746,7 @@ def fundamental_group(self): In the case of number fields, they need to have an embedding into the algebraic field:: -. + sage: # needs sage.rings.number_field sirocco sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] sage: a From 2b2857f432f63e78d6771bcadeceeea45ade926b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 29 Dec 2023 17:16:35 +0100 Subject: [PATCH 69/95] more changes from review --- .../hyperplane_arrangement/arrangement.py | 30 ++++------- .../ordered_arrangement.py | 15 +++--- src/sage/schemes/curves/affine_curve.py | 24 ++++----- src/sage/schemes/curves/all.py | 5 +- src/sage/schemes/curves/projective_curve.py | 16 +++--- src/sage/schemes/curves/zariski_vankampen.py | 53 +++++++++---------- 6 files changed, 64 insertions(+), 79 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 1e932b232b4..5fde76e5179 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -351,7 +351,6 @@ # - create ties with the Sage matroid methods # - hyperplane arrangements over other fields -# from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements from sage.geometry.hyperplane_arrangement.hyperplane import AmbientVectorSpace, Hyperplane from sage.matrix.constructor import matrix, vector from sage.misc.cachefunc import cached_method @@ -375,7 +374,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. @@ -501,10 +499,6 @@ def hyperplanes(self): A tuple - REMARK: - - It applies also to ordered arrangements.. - EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) @@ -714,7 +708,7 @@ def plot(self, **kwds): 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: @@ -735,7 +729,7 @@ def cone(self, variable='t'): ``self.hyperplanes()`` will match the order in which their counterparts in ``self.cone()`` will appear in ``self.cone().hyperplanes()``! This warning does not apply - to ordered hyperplane arrangements, + to ordered hyperplane arrangements. EXAMPLES:: @@ -769,8 +763,8 @@ def cone(self, variable='t'): hyperplanes.append([0, 1] + [0] * self.dimension()) P = self.parent() names = (variable,) + P._names - if 'Ordered' in str(type(self)): - from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements + from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements + if isinstance(P, OrderedHyperplaneArrangements): H = OrderedHyperplaneArrangements(self.parent().base_ring(), names=names) else: H = HyperplaneArrangements(self.parent().base_ring(), names=names) @@ -1308,8 +1302,8 @@ def restriction(self, hyperplane, repetitions=False): hyperplanes.append([A, b]) names = list(parent._names) names.pop(pivot) - if 'Ordered' in str(type(self)): - from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements + from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements + if isinstance(parent, OrderedHyperplaneArrangements): H = OrderedHyperplaneArrangements(parent.base_ring(), names=tuple(names)) if not repetitions: L = list(hyperplanes) @@ -2829,12 +2823,10 @@ def _bounded_region_indices(self): normal = Polyhedron(vertices=[[0]*self.dimension()], lines=[hyperplane.normal() for hyperplane in self], backend=self._backend) - - def transverse(poly): - if normal.dim() == 0: - return poly - return poly.intersection(normal) - + if normal.dim() == 0: + transverse = lambda poly: poly + else: + transverse = lambda poly: poly.intersection(normal) return tuple(i for i, region in enumerate(self.regions()) if transverse(region).is_compact()) @@ -2952,7 +2944,7 @@ def whitney_data(self): EXAMPLES:: - sage:# needs sage.combinat + sage: # needs sage.combinat sage: A = hyperplane_arrangements.Shi(3) sage: A.whitney_data() ( diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index fe0e068b771..049ae19140c 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -1,14 +1,13 @@ r""" Ordered Hyperplane Arrangements -The :class:`HyperplaneArrangements` ordered the arrangements in a hyperplane -independently of the way the hyperplanes are introduced. With the class -:class:`OrderedHyperplaneArrangements` it is allowed to fix an order by -the user, what is needed for some properties, e.g., topological one. -Besides this fact, it inherits the rest of the properties from :class:`HyperplaneArrangements.` - -Ordered arrangements --------------------- +The :class:`HyperplaneArrangements` orders the hyperplanes in a arrangement +independently of the way the hyperplanes are introduced. The class +:class:`OrderedHyperplaneArrangements` fixes an order specified by +the user. This can be needed for certain properties, e.g., fundamental group with +information about meridians, braid monodromy with information about the strands; +in the future, it may be useful for combinatorial properties. +There are no other differences with usual hyperplane arrangements. An ordered arrangement is an arrangement where the hyperplanes are sorted by the user:: diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 3f0ab862bce..d3ae36c4011 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1832,22 +1832,21 @@ def fundamental_group(self, simplified=True, puiseux=True): EXAMPLES:: - sage: # needs sirocco sage: A. = AffineSpace(QQ, 2) sage: C = A.curve(y^2 - x^3 - x^2) - sage: C.fundamental_group(puiseux=False) + sage: C.fundamental_group(puiseux=False) # needs sirocco Finitely presented group < x0 | > - sage: bm = C.braid_monodromy() - sage: g = C.fundamental_group(simplified=False) - sage: g.sorted_presentation() + sage: bm = C.braid_monodromy() # needs sirocco + sage: g = C.fundamental_group(simplified=False) # needs sirocco + sage: g.sorted_presentation() # needs sirocco Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0, x1^-1*x0 > - sage: g.simplified() + sage: g.simplified() # needs sirocco Finitely presented group < x0 | > In the case of number fields, they need to have an embedding to the algebraic field:: - sage: # needs sage.rings.number_field sirocco + sage: # needs sage.rings.number_field sage: T. = QQ[] sage: a = (t^2 + 5).roots(QQbar)[0][0] sage: F = NumberField(a.minpoly(), 'a', embedding=a) @@ -1855,15 +1854,15 @@ def fundamental_group(self, simplified=True, puiseux=True): Defining a sage: A. = AffineSpace(F, 2) sage: C = A.curve(y^2 - a*x^3 - x^2) - sage: C.fundamental_group() + sage: C.fundamental_group() # needs sirocco Finitely presented group < x0 | > sage: C = A.curve(x * (x - 1)) - sage: C.fundamental_group() + sage: C.fundamental_group() # needs sirocco Finitely presented group < x0, x1 | > """ from sage.schemes.curves.zariski_vankampen import fundamental_group_from_braid_mon bm = self.braid_monodromy() - if bm == []: + if not bm: f = self.defining_polynomial() x, y = f.parent().gens() d0 = f.degree(y) @@ -1898,10 +1897,9 @@ def braid_monodromy(self): EXAMPLES:: - sage: # needs sirocco sage: A. = AffineSpace(QQ, 2) sage: C = A.curve((x^2-y^3)*(x+3*y-5)) - sage: C.braid_monodromy() + sage: C.braid_monodromy() # needs 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, @@ -1909,7 +1907,7 @@ def braid_monodromy(self): sage: T. = QQ[] sage: K. = NumberField(t^3 + 2, 'a') sage: A. = AffineSpace(K, 2) - sage: Curve(y^2 + a * x).braid_monodromy() + sage: Curve(y^2 + a * x).braid_monodromy() # needs sirocco Traceback (most recent call last): ... NotImplementedError: the base field must have an embedding to the algebraic field diff --git a/src/sage/schemes/curves/all.py b/src/sage/schemes/curves/all.py index bdb29c72ee7..daa260b2581 100644 --- a/src/sage/schemes/curves/all.py +++ b/src/sage/schemes/curves/all.py @@ -19,11 +19,12 @@ # # http://www.gnu.org/licenses/ # ***************************************************************************** +from sage.misc.lazy_import import lazy_import from .constructor import Curve from .projective_curve import Hasse_bounds -from .plane_curve_arrangement import AffinePlaneCurveArrangements +lazy_import('sage.schemes.curves.plane_curve_arrangement', 'AffinePlaneCurveArrangements') -from .plane_curve_arrangement import ProjectivePlaneCurveArrangements +lazy_import('sage.schemes.curves.plane_curve_arrangement', 'ProjectivePlaneCurveArrangements') diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 99bef58fad1..b43550481c8 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -1736,18 +1736,17 @@ def fundamental_group(self): EXAMPLES:: - sage: # needs sirocco sage: P. = ProjectiveSpace(QQ, 2) sage: C = P.curve(x^2*z - y^3) - sage: C.fundamental_group() + sage: C.fundamental_group() # needs sirocco Finitely presented group < x0 | x0^3 > - sage: P.curve(z*(x^2*z - y^3)).fundamental_group() + sage: P.curve(z*(x^2*z - y^3)).fundamental_group() # needs sirocco Finitely presented group < x0, x1 | x1*x0*x1*x0^-1*x1^-1*x0^-1 > In the case of number fields, they need to have an embedding into the algebraic field:: - sage: # needs sage.rings.number_field sirocco + sage: # needs sage.rings.number_field sage: a = QQ[x](x^2 + 5).roots(QQbar)[0][0] sage: a -2.236067977499790?*I @@ -1756,12 +1755,11 @@ def fundamental_group(self): sage: F.inject_variables() Defining a sage: C = P.curve(x^2 + a * y^2) - sage: C.fundamental_group() + sage: C.fundamental_group() # needs sirocco Finitely presented group < x0 | > TESTS:: - sage: # needs sirocco sage: F. = FreeGroup() sage: G = F / [x1^-1*(x1^-1*x0^-1*x1*x0^-1)^2, (x1^-1*x0^-1)^2*x1^-1*(x0*x1)^2*x0] sage: G.order() @@ -1770,12 +1768,12 @@ 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: G0 = C.fundamental_group() - sage: G.is_isomorphic(G0) + sage: G0 = C.fundamental_group() # needs sirocco + sage: G.is_isomorphic(G0) # needs sirocco #I Forcing finiteness test True sage: C = P.curve(z) - sage: C.fundamental_group() + sage: C.fundamental_group() # needs sirocco Finitely presented group < | > """ from sage.schemes.curves.zariski_vankampen import fundamental_group diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index d031d87379c..0a6616ec485 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -8,7 +8,7 @@ The current implementation allows to compute a presentation of the fundamental group of curves over the rationals or number fields with -a fixed embedding on `\overline{\mathbb{Q}}`. +a fixed embedding on `\QQbar`. Instead of computing a representation of the braid monodromy, we choose several base points and a system of paths joining them that @@ -23,7 +23,7 @@ EXAMPLES:: - sage: # optional - sirocco + sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy sage: R. = QQ[] sage: f = y^3 + x^3 - 1 @@ -87,7 +87,7 @@ def braid_from_piecewise(strands): EXAMPLES:: - sage: # optional - sirocco + sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import braid_from_piecewise sage: paths = [[(0, 0, 1), (0.2, -1, -0.5), (0.8, -1, 0), (1, 0, -1)], ....: [(0, -1, 0), (0.5, 0, -1), (1, 1, 0)], @@ -177,11 +177,11 @@ def discrim(pols): INPUT: - ``pols`` -- a list or tuple of polynomials in two variables with - coefficients in a number field with a fixed embedding in `\overline{\mathbb{Q}}`. + coefficients in a number field with a fixed embedding in `QQbar`. OUTPUT: - A tuple with the roots of the discriminant in `\overline{\mathbb{Q}}`. + A tuple with the roots of the discriminant in `\QQbar`. EXAMPLES:: @@ -512,7 +512,7 @@ def followstrand(f, factors, x0, x1, y0a, prec=53): EXAMPLES:: - sage: # optional - sirocco + sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import followstrand sage: R. = QQ[] sage: f = x^2 + y^3 @@ -835,7 +835,6 @@ def braid_in_segment(glist, x0, x1, precision={}): EXAMPLES:: - sage: # optional - sirocco sage: from sage.schemes.curves.zariski_vankampen import braid_in_segment, fieldI sage: R. = QQ[] sage: K = fieldI(QQ) @@ -843,14 +842,14 @@ def braid_in_segment(glist, x0, x1, precision={}): 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) + sage: braid_in_segment(tuple(_[0] for _ in f.factor()), x0, x1) # needs sirocco s1 TESTS: Check that :trac:`26503` is fixed:: - sage: # needs sage.rings.real_mpfr sage.symbolic sirocco + sage: # needs sage.rings.real_mpfr sage.symbolic sage: wp = QQ['t']([1, 1, 1]).roots(QQbar)[0][0] sage: Kw. = NumberField(wp.minpoly(), embedding=wp) sage: R. = Kw[] @@ -867,7 +866,7 @@ def braid_in_segment(glist, x0, x1, precision={}): sage: p2a = CC(p2) sage: p2b = QQ(p2a.real()) + I*QQ(p2a.imag()) sage: glist = tuple([_[0] for _ in g.factor()]) - sage: B = braid_in_segment(glist, p1b, p2b); B + sage: B = braid_in_segment(glist, p1b, p2b); B # needs sirocco s5*s3^-1 """ precision1 = {_: precision[_] for _ in precision.keys()} @@ -1189,7 +1188,7 @@ def strand_components(f, flist, p1): EXAMPLES:: - sage: # optional - sirocco + sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import strand_components sage: R. = QQ[] sage: flist = [x^2 - y^3, x + 3 * y - 5] @@ -1238,7 +1237,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): OUTPUT: - A list of braids, images by the braid monodromy of a geometric - basis of the complement of the discriminant of `f` in `\mathbb{C}`. + basis of the complement of the discriminant of `f` in `\CC`. - A dictionary: ``i``, index of a strand is sent to the index of the corresponding factor in ``arrangement``. @@ -1260,7 +1259,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): EXAMPLES:: - sage: # optional - sirocco + sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy sage: R. = QQ[] sage: f = (x^2 - y^3) * (x + 3*y - 5) @@ -1411,7 +1410,7 @@ def conjugate_positive_form(braid): INPUT: - - ``braid`` -- a braid `\sigma`. + - ``braid`` -- a braid `\sigma` OUTPUT: @@ -1595,28 +1594,27 @@ def fundamental_group_from_braid_mon(bm, degree=None, EXAMPLES:: - sage: # optional - sirocco 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 + sage: g = fundamental_group_from_braid_mon(bm, projective=True); g # needs 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()) + sage: print (g.order(), g.abelian_invariants()) # needs sirocco 12 (4,) sage: B2 = BraidGroup(2) sage: bm = [B2(3 * [1])] - sage: g = fundamental_group_from_braid_mon(bm, vertical=[0]); g + sage: g = fundamental_group_from_braid_mon(bm, vertical=[0]); g # needs sirocco Finitely presented group < x0, x1, x2 | x2*x0*x1*x2^-1*x1^-1*x0^-1, x2*x0*x1*x0*x1^-1*x0^-1*x2^-1*x1^-1 > - sage: fundamental_group_from_braid_mon([]) is None + sage: fundamental_group_from_braid_mon([]) is None # needs sirocco True - sage: fundamental_group_from_braid_mon([], degree=2) + sage: fundamental_group_from_braid_mon([], degree=2) # needs sirocco Finitely presented group < x0, x1 | > - sage: fundamental_group_from_braid_mon([SymmetricGroup(1).one()]) + sage: fundamental_group_from_braid_mon([SymmetricGroup(1).one()]) # needs sirocco Finitely presented group < x | > """ vertical0 = sorted(vertical) @@ -1674,7 +1672,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): - ``f`` -- a polynomial in two variables, with coefficients in either the rationals or a number field with a fixed embedding in - `\overline{\mathbb{Q}}` + `\QQbar` - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the presentation will be simplified (see below) @@ -1699,7 +1697,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): EXAMPLES:: - sage: # optional - sirocco + sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import fundamental_group, braid_monodromy sage: R. = QQ[] sage: f = x^2 + y^3 @@ -1716,7 +1714,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): :: - sage: # optional - sirocco + sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import fundamental_group sage: R. = QQ[] sage: f = y^3 + x^3 @@ -1725,9 +1723,8 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): 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 `\overline{\mathbb{Q}}`:: + fixed embedding in `\QQbar`:: - sage: # optional - sirocco sage: from sage.schemes.curves.zariski_vankampen import fundamental_group sage: zeta = QQbar['x']('x^2 + x+ 1').roots(multiplicities=False)[0] sage: zeta @@ -1737,7 +1734,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): Defining zeta sage: R. = F[] sage: f = y^3 + x^3 + zeta * x + 1 - sage: fundamental_group(f) + sage: fundamental_group(f) # needs sirocco Finitely presented group < x0 | > We compute the fundamental group of the complement of a @@ -1831,7 +1828,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, EXAMPLES:: - sage: # optional - sirocco + sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import braid_monodromy sage: from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement sage: R. = QQ[] From 830a0069cc77ba7b9c79728d74d1d64491b48706 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 29 Dec 2023 17:49:12 +0100 Subject: [PATCH 70/95] forgot some changes --- .../ordered_arrangement.py | 23 +++++++++---------- src/sage/schemes/curves/affine_curve.py | 4 +++- .../schemes/curves/plane_curve_arrangement.py | 8 +++---- src/sage/schemes/curves/zariski_vankampen.py | 4 ++-- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 049ae19140c..ffbd27eb28a 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -19,8 +19,9 @@ sage: H(t0 - t1, t1 - t2, t0 - t2) Arrangement -Some methods are adapted and some new ones are created, regarding -hyperplane sections and fundamental groups, e.g., ``hyperplanes``:: +Some methods are adapted, e.g., :meth: `arrangement.hyperplanes``, +and some new ones are created, regarding +hyperplane sections and fundamental groups:: sage: H. = HyperplaneArrangements(QQ) sage: H1. = OrderedHyperplaneArrangements(QQ) @@ -30,7 +31,7 @@ sage: A1.hyperplanes() (Hyperplane x + 0*y + 0, Hyperplane 0*x + y + 0) -We see the differences in ``union``:: +We see the differences in :meth: `arrangment.union`:: sage: H. = HyperplaneArrangements(QQ) sage: H1. = OrderedHyperplaneArrangements(QQ) @@ -41,7 +42,7 @@ sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()] [0, 5, 6, 1, 2, 3, 7, 4] -Also in ``cone``:: +Also in meth: `arrangement.cone`:: sage: # needs sage.combinat sage: a. = hyperplane_arrangements.semiorder(3) @@ -51,7 +52,7 @@ sage: [b1.hyperplanes().index(h) for h in b.hyperplanes()] [0, 2, 4, 6, 1, 3, 5] -And in ``restriction``:: +And in :meth: `arrangement.restriction`:: sage: # needs sage.graphs sage: A. = hyperplane_arrangements.braid(4) @@ -106,7 +107,6 @@ class OrderedHyperplaneArrangementElement(HyperplaneArrangementElement): :class:`OrderedHyperplaneArrangementElement` instances directly, always use the parent. """ - def __init__(self, parent, hyperplanes, check=True, backend=None): """ Construct an ordered hyperplane arrangement. @@ -117,10 +117,10 @@ def __init__(self, parent, hyperplanes, check=True, backend=None): - ``hyperplanes`` -- a tuple of hyperplanes - - ``check`` -- boolean (optional; default ``True``); whether + - ``check`` -- boolean (default ``True``); whether to check input - - ``backend`` -- string (optional; default: ``None``); the backend to + - ``backend`` -- string (default: ``None``); the backend to use for the related polyhedral objects EXAMPLES:: @@ -343,7 +343,7 @@ def affine_fundamental_group(self): def affine_meridians(self): r""" - Return the meridians of each hyperplane (including the one at infinity) + Return the meridians of each hyperplane (including the one at infinity). OUTPUT: @@ -481,7 +481,6 @@ def projective_meridians(self): This functionality requires the ``sirocco`` package to be installed. - EXAMPLES:: sage: # needs sirocco @@ -549,10 +548,10 @@ def _element_constructor_(self, *args, **kwds): hyperplane; alternatively, a single polytope or a single hyperplane arrangement - - ``signed`` -- boolean (optional, default: ``True``); whether to + - ``signed`` -- boolean (default: ``True``); whether to preserve signs of hyperplane equations - - ``check`` -- boolean (optional, default: ``True``); whether to + - ``check`` -- boolean (default: ``True``); whether to perform argument checking. EXAMPLES:: diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index d3ae36c4011..80090133691 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1892,8 +1892,10 @@ def braid_monodromy(self): 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. - This functionality requires the ``sirocco`` package to be installed. + .. NOTE:: + + This functionality requires the ``sirocco`` package to be installed. EXAMPLES:: diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 609ddf13e60..0a24fe6ff04 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -452,14 +452,14 @@ def fundamental_group(self, simplified=True, vertical=True, INPUT: - - ``vertical`` -- boolean (default: True); if it is ``True``, there + - ``vertical`` -- boolean (default: ``True``); if it is ``True``, there are no vertical asymptotes, and there are vertical lines, then a simplified braid braid_monodromy is used. - - ``simplified`` -- boolean (default: True); if it is ``True``, the + - ``simplified`` -- boolean (default: ``True``); if it is ``True``, the group is simplified. - - ``projective`` -- boolean (default: False); to be used in the + - ``projective`` -- boolean (default: ``False``); to be used in the method for projective curves. OUTPUT: @@ -568,7 +568,7 @@ def meridians(self, simplified=True, vertical=True): .. NOTE:: This functionality requires the ``sirocco`` package to be installed - and :func:`AffinePlaneCurveArrangements.fundamental_group` with the same options, + and :meth:`AffinePlaneCurveArrangements.fundamental_group` with the same options, where some examples are shown. EXAMPLES:: diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 0a6616ec485..8b0b2921451 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -382,7 +382,7 @@ def voronoi_cells(V, vertical_lines=()): - ``V`` -- a corrected Voronoi diagram - - ``vertical_lines`` -- list (default: ``()``); indices of the + - ``vertical_lines`` -- tuple (default: ``()``); indices of the vertical lines OUTPUT: @@ -657,7 +657,7 @@ def fieldI(field): if I0 in field: return field field_a = field[I0] - field_b = field_a.absolute_field('b_0') + field_b = field_a.absolute_field('b0') b0 = field_b.gen() q = b0.minpoly() qembd = field_b.embeddings(QQbar) From 580bdc033ae8501d8f2732de10fe3b2a5b116471 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 29 Dec 2023 17:57:11 +0100 Subject: [PATCH 71/95] errors in :meth: --- .../hyperplane_arrangement/ordered_arrangement.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index ffbd27eb28a..0c89b62db29 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -19,7 +19,7 @@ sage: H(t0 - t1, t1 - t2, t0 - t2) Arrangement -Some methods are adapted, e.g., :meth: `arrangement.hyperplanes``, +Some methods are adapted, e.g., :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.hyperplanes``, and some new ones are created, regarding hyperplane sections and fundamental groups:: @@ -31,7 +31,7 @@ sage: A1.hyperplanes() (Hyperplane x + 0*y + 0, Hyperplane 0*x + y + 0) -We see the differences in :meth: `arrangment.union`:: +We see the differences in :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.union`:: sage: H. = HyperplaneArrangements(QQ) sage: H1. = OrderedHyperplaneArrangements(QQ) @@ -42,7 +42,7 @@ sage: [C1.hyperplanes().index(h) for h in C.hyperplanes()] [0, 5, 6, 1, 2, 3, 7, 4] -Also in meth: `arrangement.cone`:: +Also in meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.cone`:: sage: # needs sage.combinat sage: a. = hyperplane_arrangements.semiorder(3) @@ -52,7 +52,7 @@ sage: [b1.hyperplanes().index(h) for h in b.hyperplanes()] [0, 2, 4, 6, 1, 3, 5] -And in :meth: `arrangement.restriction`:: +And in :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.restriction`:: sage: # needs sage.graphs sage: A. = hyperplane_arrangements.braid(4) From 7e76fd075efcd9d700e55a0a228405b315235c53 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 29 Dec 2023 18:18:33 +0100 Subject: [PATCH 72/95] typo --- src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 0c89b62db29..bb8c6992c3b 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -19,7 +19,7 @@ sage: H(t0 - t1, t1 - t2, t0 - t2) Arrangement -Some methods are adapted, e.g., :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.hyperplanes``, +Some methods are adapted, e.g., :meth:`~sage.geometry.hyperplane_arrangement.arrangement.HyperplaneArrangementElement.hyperplanes`, and some new ones are created, regarding hyperplane sections and fundamental groups:: From 3140834f32aaf77981094e374b8c30b3fd68bb59 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 1 Jan 2024 16:08:14 +0100 Subject: [PATCH 73/95] more reviewer changes --- .../schemes/curves/plane_curve_arrangement.py | 43 +++++++------------ src/sage/schemes/curves/zariski_vankampen.py | 5 +++ 2 files changed, 21 insertions(+), 27 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 0a24fe6ff04..5ff0ad1a9d7 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -61,6 +61,7 @@ from sage.schemes.curves.projective_curve import ProjectivePlaneCurve from sage.schemes.curves.zariski_vankampen import braid_monodromy from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement +from sage.structure.category_object import normalize_names from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import richcmp @@ -958,6 +959,11 @@ class AffinePlaneCurveArrangements(UniqueRepresentation, Parent): """ Element = AffinePlaneCurveArrangementsElement + @staticmethod + def __classcall__(cls, base, names=()): + names = normalize_names(len(names), names) + return super().__classcall__(cls, base, names) + def __init__(self, base_ring, names=tuple()): """ Initialize ``self``. @@ -978,25 +984,7 @@ def __init__(self, base_ring, names=tuple()): """ if base_ring not in _Fields: raise ValueError('base ring must be a field') - super().__init__(category=Sets()) - self._base_ring = base_ring - self._names = names - - def base_ring(self): - """ - Return the base ring. - - OUTPUT: - - The base ring of the curve arrangement. - - EXAMPLES:: - - sage: L. = AffinePlaneCurveArrangements(QQ) - sage: L.base_ring() - Rational Field - """ - return self._base_ring + super().__init__(base_ring, names=names, category=Sets()) def coordinate_ring(self): """ @@ -1012,7 +1000,7 @@ def coordinate_ring(self): sage: L.coordinate_ring() Multivariate Polynomial Ring in x, y over Rational Field """ - return PolynomialRing(self._base_ring, self._names) + return PolynomialRing(self.base_ring(), self.variable_names()) def change_ring(self, base_ring): """ @@ -1040,7 +1028,7 @@ def change_ring(self, base_ring): sage: L.change_ring(QQ) is L True """ - return AffinePlaneCurveArrangements(base_ring, names=self._names) + return AffinePlaneCurveArrangements(base_ring, names=self.variable_names()) @cached_method def ambient_space(self): @@ -1053,7 +1041,7 @@ def ambient_space(self): sage: L.ambient_space() Affine Space of dimension 2 over Rational Field """ - return AffineSpace(self.base_ring(), 2, self._names) + return AffineSpace(self.base_ring(), 2, self.variable_names()) def _repr_(self): """ @@ -1090,10 +1078,11 @@ def _element_constructor_(self, *args, **kwds): sage: L._element_constructor_(y, x) == A False """ - if len(args) == 1 and not (isinstance(args[0], (tuple, list))): - arg = (args[0], ) - elif len(args) == 1: - arg = tuple(args[0]) + if len(args) == 1: + if not isinstance(args[0], (tuple, list)): + arg = (args[0], ) + else: + arg = tuple(args[0]) else: arg = tuple(args) ambient_space = self.ambient_space() @@ -1148,7 +1137,7 @@ def ngens(self): sage: L.ngens() 3 """ - return len(self._names) + return len(self.variable_names()) @cached_method def gens(self): diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 8b0b2921451..92fb28aa332 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -652,6 +652,11 @@ def fieldI(field): sage: F1 = fieldI(F0) sage: F0 == F1 True + sage: F0 = CyclotomicField(5) + sage: fieldI(F0) + Number Field in prim with defining polynomial + x^8 - 2*x^7 + 7*x^6 - 10*x^5 + 16*x^4 - 10*x^3 - 2*x^2 + 4*x + 1 + with prim = -0.3090169943749474? + 0.04894348370484643?*I """ I0 = QQbar.gen() if I0 in field: From d32de826d2150d9abd66b7c2b6ac1cae159a97b4 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 6 Jan 2024 15:19:21 +0100 Subject: [PATCH 74/95] more reviewer changes --- .../hyperplane_arrangement/arrangement.py | 41 ++++++++----------- .../ordered_arrangement.py | 7 ++-- .../schemes/curves/plane_curve_arrangement.py | 5 ++- src/sage/schemes/curves/zariski_vankampen.py | 10 ++--- 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 5fde76e5179..baaca550b29 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -2027,9 +2027,8 @@ def regions(self): EXAMPLES:: - sage: # needs sage.graphs - sage: a = hyperplane_arrangements.braid(2) - sage: a.regions() + sage: a = hyperplane_arrangements.braid(2) # needs sage.graphs + sage: a.regions() # needs 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 @@ -2206,10 +2205,9 @@ def poset_of_regions(self, B=None, numbered_labels=True): EXAMPLES:: - sage: # needs sage.combinat sage: H. = HyperplaneArrangements(QQ) sage: A = H([[0,1,1,1], [0,1,2,3]]) - sage: A.poset_of_regions() + sage: A.poset_of_regions() # needs sage.combinat Finite poset containing 4 elements sage: # needs sage.combinat sage.graphs @@ -2227,7 +2225,7 @@ def poset_of_regions(self, B=None, numbered_labels=True): 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.poset_of_regions(B=base_region) # needs sage.combinat Finite poset containing 14 elements """ from sage.combinat.posets.posets import Poset @@ -2420,17 +2418,16 @@ def closed_faces(self, labelled=True): dimension computed using ``self.closed_faces()`` equals the one computed using :meth:`face_vector`:: - sage: # needs sage.combinat sage: def test_number(a): ....: Qx = PolynomialRing(QQ, 'x'); x = Qx.gen() ....: RHS = Qx.sum(vi * x ** i for i, vi in enumerate(a.face_vector())) ....: 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) # needs sage.combinat True sage: a = hyperplane_arrangements.Shi(3) - sage: test_number(a) # long time + sage: test_number(a) # long time # needs sage.combinat True TESTS: @@ -2703,8 +2700,7 @@ def face_semigroup_algebra(self, field=None, names='e'): sage: (e3 + 2*e4) * (e1 - e7) e4 - e6 - sage: # needs sage.graphs sage.rings.finite_rings - sage: U3 = a.face_semigroup_algebra(field=GF(3)); U3 + sage: U3 = a.face_semigroup_algebra(field=GF(3)); U3 # needs sage.graphs sage.rings.finite_rings Finite-dimensional algebra of degree 13 over Finite Field of size 3 TESTS: @@ -2944,8 +2940,7 @@ def whitney_data(self): EXAMPLES:: - sage: # needs sage.combinat - sage: A = hyperplane_arrangements.Shi(3) + sage: A = hyperplane_arrangements.Shi(3) # needs sage.combinat sage: A.whitney_data() ( [ 1 -6 9] [ 1 6 6] @@ -3254,10 +3249,9 @@ def matroid(self): We check the lattice of flats is isomorphic to the intersection lattice:: - sage: # needs sage.combinat sage: f = sum([list(M.flats(i)) for i in range(M.rank() + 1)], []) - sage: PF = Poset([f, lambda x, y: x < y]) - sage: PF.is_isomorphic(A.intersection_poset()) + sage: PF = Poset([f, lambda x, y: x < y]) # needs sage.combinat + sage: PF.is_isomorphic(A.intersection_poset()) # needs sage.combinat True """ if not self.is_central(): @@ -3513,9 +3507,8 @@ def is_free(self, algorithm="singular"): For type `A` arrangements, chordality is equivalent to freeness. We verify that in type `A_3`:: - sage: # needs sage.combinat sage.groups - sage: W = WeylGroup(['A', 3], prefix='s') - sage: for x in W: + sage: W = WeylGroup(['A', 3], prefix='s') # needs sage.combinat sage.groups + sage: for x in W: # needs sage.combinat sage.groups ....: A = x.inversion_arrangement() ....: assert A.matroid().is_chordal() == A.is_free() @@ -3523,9 +3516,8 @@ def is_free(self, algorithm="singular"): We check that the algorithms agree:: - sage: # needs sage.combinat sage.groups - sage: W = WeylGroup(['B', 3], prefix='s') - sage: for x in W: # long time + sage: W = WeylGroup(['B', 3], prefix='s') # needs sage.combinat sage.groups + sage: for x in W: # long time # needs sage.combinat sage.groups ....: A = x.inversion_arrangement() ....: assert (A.is_free(algorithm="BC") ....: == A.is_free(algorithm="singular")) @@ -3594,11 +3586,10 @@ def derivation_module_basis(self, algorithm="singular"): We check the algorithms produce a basis with the same exponents:: - sage: # needs sage.combinat sage.groups - sage: W = WeylGroup(['A', 2], prefix='s') + sage: W = WeylGroup(['A', 2], prefix='s') # needs sage.combinat sage.groups sage: def exponents(B): ....: return sorted([max(x.degree() for x in b) for b in B]) - sage: for x in W: # long time + sage: for x in W: # long time # needs sage.combinat sage.groups ....: A = x.inversion_arrangement() ....: B = A.derivation_module_basis(algorithm="singular") ....: Bp = A.derivation_module_basis(algorithm="BC") diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index bb8c6992c3b..e6995d9036a 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -317,7 +317,7 @@ def affine_fundamental_group(self): n = self.dimension() r = len(self) if n == 0: - raise TypeError('Only empty arrangements for dimension ', n) + return FreeGroup(0) / [] if n == 1: G = FreeGroup(r) / [] dic = {j: G.gen(j) for j in range(r)} @@ -444,8 +444,7 @@ def projective_fundamental_group(self): n = self.dimension() r = len(self) if n == 1: - print("Only empty arrangements for dimension ", n - 1) - return None + return FreeGroup(0) / [] if n == 2: G = FreeGroup(r - 1) / [] dic = {j: G.gen(j) for j in range(r - 1)} @@ -588,7 +587,7 @@ def _element_constructor_(self, *args, **kwds): Traceback (most recent call last): ... ValueError: linear expression must be non-constant to define a hyperplane - """ + """ if len(args) == 1: arg = args[0] if isinstance(arg, HyperplaneArrangementElement) and args[0].parent() is self: diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 5ff0ad1a9d7..e0ace764482 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -1222,9 +1222,10 @@ def __init__(self, base_ring, names=tuple()): """ if base_ring not in _Fields: raise ValueError('base ring must be a field') + # super().__init__(base_ring, names=names) + # self._base_ring = base_ring + # self._names = names super().__init__(base_ring, names=names) - self._base_ring = base_ring - self._names = names @cached_method def ambient_space(self): diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 92fb28aa332..72cecea14fb 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -642,6 +642,11 @@ def fieldI(field): Number Field in prim 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 prim = 0.4863890359345430? + 1.000000000000000?*I + sage: F0 = CyclotomicField(5) + sage: fieldI(F0) + Number Field in prim with defining polynomial + x^8 - 2*x^7 + 7*x^6 - 10*x^5 + 16*x^4 - 10*x^3 - 2*x^2 + 4*x + 1 + with prim = -0.3090169943749474? + 0.04894348370484643?*I If ``I`` is already in the field, the result is the field itself:: @@ -652,11 +657,6 @@ def fieldI(field): sage: F1 = fieldI(F0) sage: F0 == F1 True - sage: F0 = CyclotomicField(5) - sage: fieldI(F0) - Number Field in prim with defining polynomial - x^8 - 2*x^7 + 7*x^6 - 10*x^5 + 16*x^4 - 10*x^3 - 2*x^2 + 4*x + 1 - with prim = -0.3090169943749474? + 0.04894348370484643?*I """ I0 = QQbar.gen() if I0 in field: From 2431c6bc618963b1857890d324b4db9fc8d9ef35 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 6 Jan 2024 15:43:09 +0100 Subject: [PATCH 75/95] change tuple to frozenset --- src/sage/schemes/curves/zariski_vankampen.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 72cecea14fb..b084c5fb1ee 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -373,7 +373,7 @@ def orient_circuit(circuit, convex=False, precision=53, verbose=False): print(prec) -def voronoi_cells(V, vertical_lines=()): +def voronoi_cells(V, vertical_lines=frozenset()): 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. @@ -382,7 +382,7 @@ def voronoi_cells(V, vertical_lines=()): - ``V`` -- a corrected Voronoi diagram - - ``vertical_lines`` -- tuple (default: ``()``); indices of the + - ``vertical_lines`` -- frozenset (default: ``frozenset()``); indices of the vertical lines OUTPUT: @@ -402,7 +402,7 @@ def voronoi_cells(V, vertical_lines=()): 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, VR = voronoi_cells(V, vertical_lines=(1,)) + sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=frozenset((1,))) sage: Gv = G.vertices(sort=True) sage: Ge = G.edges(sort=True) sage: len(Gv), len(Ge) @@ -978,7 +978,7 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}): sage: from sage.schemes.curves.zariski_vankampen import geometric_basis, corrected_voronoi_diagram, voronoi_cells sage: points = (0, -1, I, 1, -I) sage: V = corrected_voronoi_diagram(points) - sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=[0, 1, 2, 3, 4]) + sage: G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=frozenset((0 .. 4))) sage: gb, vd = geometric_basis(G, E, EC, p, DG, vertical_regions=VR) sage: gb [[A vertex at (5/2, -5/2), A vertex at (5/2, 5/2), A vertex at (-5/2, 5/2), @@ -1329,6 +1329,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): else: transversal[f0] = arrangement1.index(f0) vl.sort() + vl = frozenset(vl) if not disc: vertical_braids = {i: transversal[f0] for i, f0 in enumerate(transversal)} From cd9ecd76a2a0e9d1b6c4cf653257a1ffae95323b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sat, 6 Jan 2024 16:15:01 +0100 Subject: [PATCH 76/95] set puiseux True and do not change simplified to False --- src/sage/schemes/curves/zariski_vankampen.py | 56 +++++++++----------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index b084c5fb1ee..4c6fbe8e1e9 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1561,7 +1561,7 @@ def relation(x, b): def fundamental_group_from_braid_mon(bm, degree=None, simplified=True, projective=False, - puiseux=False, vertical=[]): + puiseux=True, vertical=[]): r""" Return a presentation of the fundamental group computed from a braid monodromy. @@ -1581,8 +1581,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, 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 + - ``puiseux`` -- boolean (default: ``True``); 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``. @@ -1590,7 +1589,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, - ``vertical`` -- list of integers (default: ``[]``); the indices in ``[0 .. r - 1]`` of the braids that surround a vertical line - If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is + If ``projective``` is ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. OUTPUT: @@ -1606,8 +1605,8 @@ def fundamental_group_from_braid_mon(bm, degree=None, ....: 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 # needs sirocco - Finitely presented group < x0, x1 | x1*x0^2*x1, - x0^-1*x1^-1*x0^-1*x1*x0^-1*x1^-1 > + Finitely presented group + < x1, x3 | x3^2*x1^2, x1^-1*x3^-1*x1*x3^-1*x1^-1*x3^-1 > sage: print (g.order(), g.abelian_invariants()) # needs sirocco 12 (4,) sage: B2 = BraidGroup(2) @@ -1643,9 +1642,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, if not puiseux: relations_h = (relation([(x, b) for x in F.gens() for b in bmh])) rel_h = [r[1] for r in relations_h] - simplified0 = simplified else: - simplified0 = False conjugate_desc = conjugate_positive_form_p(bmh) trenzas_desc = [b1[-1] for b1 in conjugate_desc] trenzas_desc_1 = flatten(trenzas_desc, max_level=1) @@ -1664,12 +1661,12 @@ def fundamental_group_from_braid_mon(bm, degree=None, if projective: rel.append(prod(Fv.gens()).Tietze()) G = Fv / rel - if simplified0: + if simplified: return G.simplified() return G -def fundamental_group(f, simplified=True, projective=False, puiseux=False): +def fundamental_group(f, simplified=True, projective=False, puiseux=True): r""" Return a presentation of the fundamental group of the complement of the algebraic set defined by the polynomial ``f``. @@ -1688,12 +1685,14 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): 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``, + - ``puiseux`` -- boolean (default: ``True``); if set to ``True``, a presentation of the fundamental group with the homotopy type - of the complement of the affine curve is computed, ``simplified`` is - ignored. One relation is added if ``projective`` is set to ``True``. + of the complement of the affine curve is computed. If the Euler + characteristic does not match, the homotopy type is obtained + with a wedge of 2-spheres. One relation is added if ``projective`` + is set to ``True``. - If ``simplified` and ``projective``` are ``False`` and ``puiseux`` is + If ``projective``` is ``False`` and ``puiseux`` is ``True``, a Zariski-VanKampen presentation is returned. OUTPUT: @@ -1708,8 +1707,8 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): sage: R. = QQ[] sage: f = x^2 + y^3 sage: fundamental_group(f) - Finitely presented group < x1, x2 | x1*x2*x1^-1*x2^-1*x1^-1*x2 > - sage: fundamental_group(f, simplified=False).sorted_presentation() + Finitely presented group < x0, x1 | x0*x1^-1*x0^-1*x1^-1*x0*x1 > + sage: fundamental_group(f, simplified=False, puiseux=False).sorted_presentation() Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x0*x1, x2^-1*x0*x1*x0^-1, x1^-1*x0^-1*x1^-1*x0*x1*x0 > @@ -1724,9 +1723,9 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): sage: from sage.schemes.curves.zariski_vankampen import fundamental_group sage: R. = QQ[] sage: f = y^3 + x^3 - sage: fundamental_group(f) - Finitely presented group < x0, x1, x2 | x0*x1*x2*x0^-1*x2^-1*x1^-1, - x2*x0*x1*x2^-1*x1^-1*x0^-1 > + sage: fundamental_group(f).sorted_presentation() + Finitely presented group < x0, x1, x2 | x2^-1*x1^-1*x0^-1*x2*x0*x1, + x2^-1*x1^-1*x2*x0*x1*x0^-1 > It is also possible to have coefficients in a number field with a fixed embedding in `\QQbar`:: @@ -1750,14 +1749,9 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): sage: from sage.schemes.curves.zariski_vankampen import fundamental_group sage: R. = QQ[] sage: f = x^2 * y^2 + x^2 + y^2 - 2 * x * y * (x + y + 1) - sage: g = fundamental_group(f, puiseux=True); g.sorted_presentation() - Finitely presented group - < x0, x1, x2, x3 | x3^-1*x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1*x2, - x3^-1*x2^-1*x1*x2, x2^-1*x1^-1*x0^-1*x1*x2*x1, - x2^-1*x0 > - sage: g.simplified().sorted_presentation() + sage: g = fundamental_group(f); g.sorted_presentation() Finitely presented group < x0, x1 | x1^-2*x0^2, (x1^-1*x0)^3 > - sage: g = fundamental_group(f, puiseux=True, projective=True) + sage: g = fundamental_group(f, projective=True) sage: g.order(), g.abelian_invariants() (12, (4,)) sage: fundamental_group(y * (y - 1)) @@ -1785,7 +1779,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=False): def fundamental_group_arrangement(flist, simplified=True, projective=False, - puiseux=False, vertical=False, + puiseux=True, vertical=False, braid_data=None): r""" Compute the fundamental group of the complement of a curve @@ -1806,8 +1800,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, 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 + - ``puiseux`` -- boolean (default: ``True``); 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``. @@ -1846,7 +1839,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, x1^-1*x0^-1*x1*x0 > sage: dic {0: [x0, x2], 1: [x1], 2: [x0^-1*x2^-1*x1^-1*x0^-1]} - sage: g, dic = fundamental_group_arrangement(flist, simplified=False) + sage: g, dic = fundamental_group_arrangement(flist, simplified=False, puiseux=False) sage: g.sorted_presentation(), dic (Finitely presented group < x0, x1, x2, x3 | 1, 1, 1, 1, 1, 1, 1, @@ -1875,8 +1868,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, sage: G.sorted_presentation() Finitely presented group < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x0^-1*x1*x3*x0, - x3^-1*x1^-1*x0^-1*x3*x0*x1, - x2^-1*x1^-1*x0^-1*x1*x2*x1^-1*x0*x1 > + x3^-1*x1^-1*x3*x0*x1*x0^-1, x2^-1*x0^-1*x2*x0 > sage: dic {0: [x1], 1: [x3], 2: [x2], 3: [x0], 4: [x3^-1*x2^-1*x1^-1*x0^-1]} sage: fundamental_group_arrangement(L, vertical=True) From 630e0cf01e33e79589b55706a07ee0d9e53bb824 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 8 Jan 2024 17:49:16 +0100 Subject: [PATCH 77/95] attributes as dictionaries --- src/sage/schemes/curves/affine_curve.py | 13 +- .../schemes/curves/plane_curve_arrangement.py | 242 +++++++++++------- src/sage/schemes/curves/zariski_vankampen.py | 8 + 3 files changed, 160 insertions(+), 103 deletions(-) diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 80090133691..040f9ece0c5 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1810,7 +1810,7 @@ def fundamental_group(self, simplified=True, puiseux=True): - ``puiseux`` -- (default: ``True``) boolean to decide if the presentation is constructed in the classical way or using Puiseux - shortcut. If ``True``, ``simplified`` is set to ``False``. + shortcut. OUTPUT: @@ -1832,15 +1832,16 @@ def fundamental_group(self, simplified=True, puiseux=True): EXAMPLES:: + sage: # needs sirocco sage: A. = AffineSpace(QQ, 2) sage: C = A.curve(y^2 - x^3 - x^2) - sage: C.fundamental_group(puiseux=False) # needs sirocco + sage: C.fundamental_group(puiseux=False) Finitely presented group < x0 | > - sage: bm = C.braid_monodromy() # needs sirocco - sage: g = C.fundamental_group(simplified=False) # needs sirocco - sage: g.sorted_presentation() # needs sirocco + sage: bm = C.braid_monodromy() + sage: g = C.fundamental_group(simplified=False) + sage: g.sorted_presentation() Finitely presented group < x0, x1 | x1^-1*x0^-1*x1*x0, x1^-1*x0 > - sage: g.simplified() # needs sirocco + sage: g.simplified() Finitely presented group < x0 | > In the case of number fields, they need to have an embedding diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index e0ace764482..a256826331c 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -100,19 +100,27 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - self._braid_monodromy = None - self._braid_monodromy_with_vertical = None - self._strands = dict() - self._strands_with_vertical = dict() + self._braid_monodromy = {('vertical', bool_v): None + for bool_v in (True, False)} + self._strands = {('vertical', bool_v): None + for bool_v in (True, False)} + self._fundamental_group = {('simpl', bool_s, 'vertical', bool_p): None + for bool_s in (True, False) for bool_p in (True, False)} + self._meridians = {('simpl', bool_s, 'vertical', bool_p): None + for bool_s in (True, False) for bool_p in (True, False)} + # self._braid_monodromy = None + # self._braid_monodromy_with_vertical = None + # self._strands = dict() + # self._strands_with_vertical = dict() self._vertical_lines_in_braid_mon = None - self._fundamental_group_vertical_simplified = None - self._meridians_vertical_simplified = dict() - self._fundamental_group_vertical = None - self._meridians_vertical = dict() - self._fundamental_group_simplified = None - self._meridians_simplified = dict() - self._fundamental_group = None - self._meridians = dict() + # self._fundamental_group_vertical_simplified = None + # self._meridians_vertical_simplified = dict() + # self._fundamental_group_vertical = None + # self._meridians_vertical = dict() + # self._fundamental_group_simplified = None + # self._meridians_simplified = dict() + # self._fundamental_group = None + # self._meridians = dict() def __getitem__(self, i): """ @@ -514,28 +522,40 @@ def fundamental_group(self, simplified=True, vertical=True, x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > """ - if self._fundamental_group and not vertical and not simplified: - return self._fundamental_group - if self._fundamental_group_simplified and simplified and not vertical: - return self._fundamental_group_simplified - if self._fundamental_group_vertical and not simplified and vertical: - return self._fundamental_group_vertical - if self._fundamental_group_vertical_simplified and simplified \ - and vertical: - return self._fundamental_group_vertical_simplified + computed = self._fundamental_group['simpl', simplified, 'vertical', vertical] + if computed: + return computed + # if self._fundamental_group and not vertical and not simplified: + # return self._fundamental_group + # if self._fundamental_group_simplified and simplified and not vertical: + # return self._fundamental_group_simplified + # if self._fundamental_group_vertical and not simplified and vertical: + # return self._fundamental_group_vertical + # if self._fundamental_group_vertical_simplified and simplified \ + # and vertical: + # return self._fundamental_group_vertical_simplified K = self.base_ring() R = self.coordinate_ring() if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') C = self.reduce() L = C.defining_polynomials() - if not vertical and self._braid_monodromy is not None: - d1 = prod(L).degree() - bd = (self._braid_monodromy, self._strands, dict(), d1) - elif vertical and self._braid_monodromy_with_vertical is not None: - d1 = prod(L).degree(R.gen(1)) - bd = (self._braid_monodromy_with_vertical, self._strands_with_vertical, - self._vertical_lines_in_braid_mon, d1) + bm = self._braid_monodromy['vertical', vertical] + if bm is not None: + st = self._strands['vertical', vertical] + if not vertical: + d1 = prod(L).degree() + bd = (bm, st, dict(), d1) + else: + d1 = prod(L).degree(R.gen(1)) + bd = (bm, st, self._vertical_lines_in_braid_mon, d1) + # if not vertical and self._braid_monodromy is not None: + # d1 = prod(L).degree() + # bd = (self._braid_monodromy, self._strands, dict(), d1) + # elif vertical and self._braid_monodromy_with_vertical is not None: + # d1 = prod(L).degree(R.gen(1)) + # bd = (self._braid_monodromy_with_vertical, self._strands_with_vertical, + # self._vertical_lines_in_braid_mon, d1) else: bd = None G, dic = fundamental_group_arrangement(L, simplified=simplified, @@ -543,18 +563,20 @@ def fundamental_group(self, simplified=True, vertical=True, projective=projective, vertical=vertical, braid_data=bd) - if not vertical and not simplified: - self._fundamental_group = G - self._meridians = dic - elif not vertical: - self._fundamental_group_simplified = G - self._meridians_simplified = dic - elif not simplified: - self._fundamental_group_vertical = G - self._meridians_vertical = dic - else: - self._fundamental_group_vertical_simplified = G - self._meridians_vertical_simplified = dic + self._fundamental_group['simpl', simplified, 'vertical', vertical] = G + self._meridians['simpl', simplified, 'vertical', vertical] = dic + # if not vertical and not simplified: + # self._fundamental_group = G + # self._meridians = dic + # elif not vertical: + # self._fundamental_group_simplified = G + # self._meridians_simplified = dic + # elif not simplified: + # self._fundamental_group_vertical = G + # self._meridians_vertical = dic + # else: + # self._fundamental_group_vertical_simplified = G + # self._meridians_vertical_simplified = dic return G def meridians(self, simplified=True, vertical=True): @@ -584,24 +606,29 @@ def meridians(self, simplified=True, vertical=True): sage: A.meridians() {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]} """ - if not vertical and not simplified: - computed = bool(self._meridians) - elif not vertical: - computed = bool(self._meridians_simplified) - elif not simplified: - computed = bool(self._meridians_vertical) - else: - computed = bool(self._meridians_vertical_simplified) - if not computed: - self.fundamental_group(simplified=simplified, vertical=vertical) - if not vertical and not simplified: - return self._meridians - if simplified and not vertical: - return self._meridians_simplified - if not simplified and vertical: - return self._meridians_vertical - if simplified and vertical: - return self._meridians_vertical_simplified + computed = self._meridians['simpl', simplified, 'vertical', vertical] + if computed: + return computed + _ = self.fundamental_group(simplified=simplified, vertical=vertical) + return self._meridians['simpl', simplified, 'vertical', vertical] + # if not vertical and not simplified: + # computed = bool(self._meridians) + # elif not vertical: + # computed = bool(self._meridians_simplified) + # elif not simplified: + # computed = bool(self._meridians_vertical) + # else: + # computed = bool(self._meridians_vertical_simplified) + # if not computed: + # self.fundamental_group(simplified=simplified, vertical=vertical) + # if not vertical and not simplified: + # return self._meridians + # if simplified and not vertical: + # return self._meridians_simplified + # if not simplified and vertical: + # return self._meridians_vertical + # if simplified and vertical: + # return self._meridians_vertical_simplified def braid_monodromy(self, vertical=True): r""" @@ -638,23 +665,28 @@ def braid_monodromy(self, vertical=True): sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] """ - if not vertical and self._braid_monodromy is not None: - return self._braid_monodromy - if vertical and self._braid_monodromy_with_vertical is not None: - return self._braid_monodromy_with_vertical + computed = self._braid_monodromy['vertical', vertical] + if computed is not None: + return computed + # if not vertical and self._braid_monodromy is not None: + # return self._braid_monodromy + # if vertical and self._braid_monodromy_with_vertical is not None: + # return self._braid_monodromy_with_vertical K = self.base_ring() if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') L = self.defining_polynomials() bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical) + self._braid_monodromy['vertical', vertical] = bm + self._strands['vertical', vertical] = dic if vertical: - self._braid_monodromy_with_vertical = bm - self._strands_with_vertical = dic + # self._braid_monodromy_with_vertical = bm + # self._strands_with_vertical = dic self._vertical_lines_in_braid_mon = dv - else: - self._braid_monodromy = bm - self._strands = dic + # else: + # self._braid_monodromy = bm + # self._strands = dic return bm def strands(self): @@ -680,9 +712,9 @@ def strands(self): sage: A.strands() {0: 2, 1: 1, 2: 0, 3: 0} """ - if not self._strands: + if not self._strands['vertical', False]: self._braid_monodromy = self.braid_monodromy(vertical=False) - return self._strands + return self._strands['vertical', False] def vertical_strands(self): r""" @@ -709,9 +741,9 @@ def vertical_strands(self): sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] """ - if not self._strands_with_vertical: + if not self._strands['vertical', True]: self.braid_monodromy(vertical=True) - return self._strands_with_vertical + return self._strands['vertical', True] def vertical_lines_in_braid_mon(self): r""" @@ -773,10 +805,12 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - self._fundamental_group_simplified = None - self._meridians_simplified = dict() - self._fundamental_group = None - self._meridians = dict() + self._fundamental_group = {('simpl', bool_s): None for bool_s in (True, False)} + self._meridians = {('simpl', bool_s): None for bool_s in (True, False)} + # self._fundamental_group_simplified = None + # self._meridians_simplified = dict() + # self._fundamental_group = None + # self._meridians = dict() def fundamental_group(self, simplified=True): r""" @@ -828,10 +862,13 @@ def fundamental_group(self, simplified=True): < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > """ - if self._fundamental_group and not simplified: - return self._fundamental_group - if self._fundamental_group_simplified and simplified: - return self._fundamental_group_simplified + computed = self._fundamental_group['simpl', simplified] + if computed: + return computed + # if self._fundamental_group and not simplified: + # return self._fundamental_group + # if self._fundamental_group_simplified and simplified: + # return self._fundamental_group_simplified H = self.parent() K = self.base_ring() R = self.coordinate_ring() @@ -844,11 +881,15 @@ def fundamental_group(self, simplified=True): infinity_in_C = infinity in C if infinity_in_C and n == 1: G = FreeGroup(0) / [] - self._fundamental_group = G - self._meridians = {0: 0} - self._fundamental_group_simplified = G - self._meridians_simplified = {0: [G.one()]} + for bool_s in (True, False): + self._fundamental_group['simpl', bool_s] = G + self._meridians['simpl', bool_s] = {0: [G.one()]} return G + # self._fundamental_group = G + # self._meridians = {0: 0} + # self._fundamental_group_simplified = G + # self._meridians_simplified = {0: [G.one()]} + # return G if infinity_in_C: j = C.curves().index(infinity) C = H(C.curves()[:j] + C.curves()[j + 1:]) @@ -884,12 +925,14 @@ def fundamental_group(self, simplified=True): dic1[j] += dic[n] else: dic1 = dic - if not simplified: - self._fundamental_group = G - self._meridians = dic1 - else: - self._fundamental_group_simplified = G - self._meridians_simplified = dic1 + self._fundamental_group['simpl', simplified] = G + self._meridians['simpl', simplified] = dic1 + # if not simplified: + # self._fundamental_group = G + # self._meridians = dic1 + # else: + # self._fundamental_group_simplified = G + # self._meridians_simplified = dic1 return G def meridians(self, simplified=True): @@ -929,15 +972,20 @@ def meridians(self, simplified=True): sage: A.meridians() {0: [x0, x2*x0*x2^-1], 1: [x2, x0^-1*x2^-1*x1^-1*x0^-1], 2: [x1]} """ - if not simplified: - computed = bool(self._meridians) - else: - computed = bool(self._meridians_simplified) - if not computed: - _ = self._fundamental_group(simplified=simplified) - if not simplified: - return self._meridians + computed = self._meridians['simpl', simplified] + if computed: + return computed + _ = self._fundamental_group(simplified=simplified) return self._meridians_simplified + # if not simplified: + # computed = bool(self._meridians) + # else: + # computed = bool(self._meridians_simplified) + # if not computed: + # _ = self._fundamental_group(simplified=simplified) + # if not simplified: + # return self._meridians + # return self._meridians_simplified class AffinePlaneCurveArrangements(UniqueRepresentation, Parent): diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 4c6fbe8e1e9..c2912f517da 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -647,6 +647,12 @@ def fieldI(field): Number Field in prim with defining polynomial x^8 - 2*x^7 + 7*x^6 - 10*x^5 + 16*x^4 - 10*x^3 - 2*x^2 + 4*x + 1 with prim = -0.3090169943749474? + 0.04894348370484643?*I + sage: fieldI(QuadraticField(3)) + Number Field in prim with defining polynomial x^4 - 4*x^2 + 16 + with prim = -1.732050807568878? + 1.000000000000000?*I + sage: fieldI(QuadraticField(-3)) + Number Field in prim with defining polynomial x^4 + 8*x^2 + 4 + with prim = 0.?e-18 - 0.732050807568878?*I If ``I`` is already in the field, the result is the field itself:: @@ -657,6 +663,8 @@ def fieldI(field): sage: F1 = fieldI(F0) sage: F0 == F1 True + sage: QuadraticField(-1) == fieldI(QuadraticField(-1)) + True """ I0 = QQbar.gen() if I0 in field: From 1c1c30c3dd3936f6bc29c0a648fde3a2dfa40aeb Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 8 Jan 2024 21:08:03 +0100 Subject: [PATCH 78/95] delete commented lines --- .../ordered_arrangement.py | 9 -- .../schemes/curves/plane_curve_arrangement.py | 95 ------------------- 2 files changed, 104 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index e6995d9036a..85885b31da6 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -131,15 +131,6 @@ def __init__(self, parent, hyperplanes, check=True, backend=None): sage: TestSuite(elt).run() """ super().__init__(parent, hyperplanes, check=check, backend=backend) - # self._hyperplanes = hyperplanes - # self._backend = backend - # if check: - # if not isinstance(hyperplanes, tuple): - # raise ValueError("the hyperplanes must be given as a tuple") - # if not all(isinstance(h, Hyperplane) for h in hyperplanes): - # raise ValueError("not all elements are hyperplanes") - # if not all(h.parent() is self.parent().ambient_space() for h in hyperplanes): - # raise ValueError("not all hyperplanes are in the ambient space") self._affine_fundamental_group = None self._affine_meridians = None self._projective_fundamental_group = None diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index a256826331c..00da9c187dd 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -108,19 +108,7 @@ def __init__(self, parent, curves, check=True): for bool_s in (True, False) for bool_p in (True, False)} self._meridians = {('simpl', bool_s, 'vertical', bool_p): None for bool_s in (True, False) for bool_p in (True, False)} - # self._braid_monodromy = None - # self._braid_monodromy_with_vertical = None - # self._strands = dict() - # self._strands_with_vertical = dict() self._vertical_lines_in_braid_mon = None - # self._fundamental_group_vertical_simplified = None - # self._meridians_vertical_simplified = dict() - # self._fundamental_group_vertical = None - # self._meridians_vertical = dict() - # self._fundamental_group_simplified = None - # self._meridians_simplified = dict() - # self._fundamental_group = None - # self._meridians = dict() def __getitem__(self, i): """ @@ -525,15 +513,6 @@ def fundamental_group(self, simplified=True, vertical=True, computed = self._fundamental_group['simpl', simplified, 'vertical', vertical] if computed: return computed - # if self._fundamental_group and not vertical and not simplified: - # return self._fundamental_group - # if self._fundamental_group_simplified and simplified and not vertical: - # return self._fundamental_group_simplified - # if self._fundamental_group_vertical and not simplified and vertical: - # return self._fundamental_group_vertical - # if self._fundamental_group_vertical_simplified and simplified \ - # and vertical: - # return self._fundamental_group_vertical_simplified K = self.base_ring() R = self.coordinate_ring() if not K.is_subring(QQbar): @@ -549,13 +528,6 @@ def fundamental_group(self, simplified=True, vertical=True, else: d1 = prod(L).degree(R.gen(1)) bd = (bm, st, self._vertical_lines_in_braid_mon, d1) - # if not vertical and self._braid_monodromy is not None: - # d1 = prod(L).degree() - # bd = (self._braid_monodromy, self._strands, dict(), d1) - # elif vertical and self._braid_monodromy_with_vertical is not None: - # d1 = prod(L).degree(R.gen(1)) - # bd = (self._braid_monodromy_with_vertical, self._strands_with_vertical, - # self._vertical_lines_in_braid_mon, d1) else: bd = None G, dic = fundamental_group_arrangement(L, simplified=simplified, @@ -565,18 +537,6 @@ def fundamental_group(self, simplified=True, vertical=True, braid_data=bd) self._fundamental_group['simpl', simplified, 'vertical', vertical] = G self._meridians['simpl', simplified, 'vertical', vertical] = dic - # if not vertical and not simplified: - # self._fundamental_group = G - # self._meridians = dic - # elif not vertical: - # self._fundamental_group_simplified = G - # self._meridians_simplified = dic - # elif not simplified: - # self._fundamental_group_vertical = G - # self._meridians_vertical = dic - # else: - # self._fundamental_group_vertical_simplified = G - # self._meridians_vertical_simplified = dic return G def meridians(self, simplified=True, vertical=True): @@ -611,24 +571,6 @@ def meridians(self, simplified=True, vertical=True): return computed _ = self.fundamental_group(simplified=simplified, vertical=vertical) return self._meridians['simpl', simplified, 'vertical', vertical] - # if not vertical and not simplified: - # computed = bool(self._meridians) - # elif not vertical: - # computed = bool(self._meridians_simplified) - # elif not simplified: - # computed = bool(self._meridians_vertical) - # else: - # computed = bool(self._meridians_vertical_simplified) - # if not computed: - # self.fundamental_group(simplified=simplified, vertical=vertical) - # if not vertical and not simplified: - # return self._meridians - # if simplified and not vertical: - # return self._meridians_simplified - # if not simplified and vertical: - # return self._meridians_vertical - # if simplified and vertical: - # return self._meridians_vertical_simplified def braid_monodromy(self, vertical=True): r""" @@ -668,10 +610,6 @@ def braid_monodromy(self, vertical=True): computed = self._braid_monodromy['vertical', vertical] if computed is not None: return computed - # if not vertical and self._braid_monodromy is not None: - # return self._braid_monodromy - # if vertical and self._braid_monodromy_with_vertical is not None: - # return self._braid_monodromy_with_vertical K = self.base_ring() if not K.is_subring(QQbar): raise TypeError('the base field is not in QQbar') @@ -681,12 +619,7 @@ def braid_monodromy(self, vertical=True): self._braid_monodromy['vertical', vertical] = bm self._strands['vertical', vertical] = dic if vertical: - # self._braid_monodromy_with_vertical = bm - # self._strands_with_vertical = dic self._vertical_lines_in_braid_mon = dv - # else: - # self._braid_monodromy = bm - # self._strands = dic return bm def strands(self): @@ -807,10 +740,6 @@ def __init__(self, parent, curves, check=True): raise ValueError("not all curves are in the same ambient space") self._fundamental_group = {('simpl', bool_s): None for bool_s in (True, False)} self._meridians = {('simpl', bool_s): None for bool_s in (True, False)} - # self._fundamental_group_simplified = None - # self._meridians_simplified = dict() - # self._fundamental_group = None - # self._meridians = dict() def fundamental_group(self, simplified=True): r""" @@ -865,10 +794,6 @@ def fundamental_group(self, simplified=True): computed = self._fundamental_group['simpl', simplified] if computed: return computed - # if self._fundamental_group and not simplified: - # return self._fundamental_group - # if self._fundamental_group_simplified and simplified: - # return self._fundamental_group_simplified H = self.parent() K = self.base_ring() R = self.coordinate_ring() @@ -885,11 +810,6 @@ def fundamental_group(self, simplified=True): self._fundamental_group['simpl', bool_s] = G self._meridians['simpl', bool_s] = {0: [G.one()]} return G - # self._fundamental_group = G - # self._meridians = {0: 0} - # self._fundamental_group_simplified = G - # self._meridians_simplified = {0: [G.one()]} - # return G if infinity_in_C: j = C.curves().index(infinity) C = H(C.curves()[:j] + C.curves()[j + 1:]) @@ -927,12 +847,6 @@ def fundamental_group(self, simplified=True): dic1 = dic self._fundamental_group['simpl', simplified] = G self._meridians['simpl', simplified] = dic1 - # if not simplified: - # self._fundamental_group = G - # self._meridians = dic1 - # else: - # self._fundamental_group_simplified = G - # self._meridians_simplified = dic1 return G def meridians(self, simplified=True): @@ -977,15 +891,6 @@ def meridians(self, simplified=True): return computed _ = self._fundamental_group(simplified=simplified) return self._meridians_simplified - # if not simplified: - # computed = bool(self._meridians) - # else: - # computed = bool(self._meridians_simplified) - # if not computed: - # _ = self._fundamental_group(simplified=simplified) - # if not simplified: - # return self._meridians - # return self._meridians_simplified class AffinePlaneCurveArrangements(UniqueRepresentation, Parent): From a896c65bc99b2376552ac98116251ce040acb292 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 9 Jan 2024 07:47:38 +0100 Subject: [PATCH 79/95] adapt attributes in ordered_arrangement --- .../geometry/hyperplane_arrangement/ordered_arrangement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 85885b31da6..c986f72b34c 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -324,7 +324,7 @@ def affine_fundamental_group(self): L = Af([vector(line.coefficients()) * coord for line in self]) G = L.fundamental_group() self._affine_fundamental_group = G - self._affine_meridians = L._meridians_vertical_simplified + self._affine_meridians = L._meridians['simpl', True, 'vertical', True] return G H1 = self.hyperplane_section(proj=False) G = H1.affine_fundamental_group() @@ -451,7 +451,7 @@ def projective_fundamental_group(self): L = Proj([vector(line.coefficients()[1:]) * coord for line in self]) G = L.fundamental_group() self._projective_fundamental_group = G - self._projective_meridians = L._meridians_simplified + self._projective_meridians = L._meridians['simpl', True] return G H1 = self.hyperplane_section() G = H1.projective_fundamental_group() From 2e472d4dfa4d247b29ef9ea911c7ccc6c7f5357b Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 8 Feb 2024 13:27:58 +0100 Subject: [PATCH 80/95] attributes are not dictionaries --- .../schemes/curves/plane_curve_arrangement.py | 129 +++++++++++++----- 1 file changed, 97 insertions(+), 32 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 00da9c187dd..718c0bbadfc 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -100,14 +100,18 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - self._braid_monodromy = {('vertical', bool_v): None - for bool_v in (True, False)} - self._strands = {('vertical', bool_v): None - for bool_v in (True, False)} - self._fundamental_group = {('simpl', bool_s, 'vertical', bool_p): None - for bool_s in (True, False) for bool_p in (True, False)} - self._meridians = {('simpl', bool_s, 'vertical', bool_p): None - for bool_s in (True, False) for bool_p in (True, False)} + self._braid_monodromy_non_vertical = None + self._braid_monodromy_vertical = None + self._strands_nonvertical = None + self._strands_vertical = None + self._fundamental_group_nonsimpl_nonvertical = None + self._fundamental_group_nonsimpl_vertical = None + self._fundamental_group_simpl_nonvertical = None + self._fundamental_group_simpl_vertical = None + self._meridians_nonsimpl_nonvertical = None + self._meridians_nonsimpl_vertical = None + self._meridians_simpl_nonvertical = None + self._meridians_simpl_vertical = None self._vertical_lines_in_braid_mon = None def __getitem__(self, i): @@ -510,7 +514,14 @@ def fundamental_group(self, simplified=True, vertical=True, x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > """ - computed = self._fundamental_group['simpl', simplified, 'vertical', vertical] + if simplified and vertical: + computed = self._fundamental_group_simpl_vertical + elif simplified and not vertical: + computed = self._fundamental_group_simpl_nonvertical + elif not simplified and vertical: + computed = self._fundamental_group_nonsimpl_vertical + else: + computed = self._fundamental_group_nonsimpl_nonvertical if computed: return computed K = self.base_ring() @@ -519,13 +530,17 @@ def fundamental_group(self, simplified=True, vertical=True, raise TypeError('the base field is not in QQbar') C = self.reduce() L = C.defining_polynomials() - bm = self._braid_monodromy['vertical', vertical] + if vertical: + bm = self._braid_monodromy_vertical + else: + bm = self._braid_monodromy_non_vertical if bm is not None: - st = self._strands['vertical', vertical] if not vertical: + st = self._strands_nonvertical d1 = prod(L).degree() bd = (bm, st, dict(), d1) else: + st = self._strands_vertical d1 = prod(L).degree(R.gen(1)) bd = (bm, st, self._vertical_lines_in_braid_mon, d1) else: @@ -535,8 +550,18 @@ def fundamental_group(self, simplified=True, vertical=True, projective=projective, vertical=vertical, braid_data=bd) - self._fundamental_group['simpl', simplified, 'vertical', vertical] = G - self._meridians['simpl', simplified, 'vertical', vertical] = dic + if simplified and vertical: + self._fundamental_group_simpl_vertical = G + self._meridians_simpl_vertical = dic + elif simplified and not vertical: + self._fundamental_group_simpl_nonvertical = G + self._meridians_simpl_nonvertical = dic + elif not simplified and vertical: + self._fundamental_group_nonsimpl_vertical = G + self._meridians_nonsimpl_vertical = dic + else: + self._fundamental_group_nonsimpl_nonvertical = G + self._meridians_nonsimpl_nonvertical = dic return G def meridians(self, simplified=True, vertical=True): @@ -566,11 +591,25 @@ def meridians(self, simplified=True, vertical=True): sage: A.meridians() {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]} """ - computed = self._meridians['simpl', simplified, 'vertical', vertical] + if simplified and vertical: + computed = self._meridians_simpl_vertical + elif simplified and not vertical: + computed = self._meridians_simpl_nonvertical + elif not simplified and vertical: + computed = self._meridians_nonsimpl_vertical + else: + computed = self._meridians_nonsimpl_nonvertical if computed: return computed _ = self.fundamental_group(simplified=simplified, vertical=vertical) - return self._meridians['simpl', simplified, 'vertical', vertical] + if simplified and vertical: + return self._meridians_simpl_vertical + elif simplified and not vertical: + return self._meridians_group_simpl_nonvertical + elif not simplified and vertical: + return self._meridians_nonsimpl_vertical + else: + return self._meridians_nonsimpl_nonvertical def braid_monodromy(self, vertical=True): r""" @@ -607,7 +646,10 @@ def braid_monodromy(self, vertical=True): sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] """ - computed = self._braid_monodromy['vertical', vertical] + if vertical: + computed = self._braid_monodromy_vertical + else: + computed = self._braid_monodromy_non_vertical if computed is not None: return computed K = self.base_ring() @@ -616,10 +658,13 @@ def braid_monodromy(self, vertical=True): L = self.defining_polynomials() bm, dic, dv, d1 = braid_monodromy(prod(L), arrangement=L, vertical=vertical) - self._braid_monodromy['vertical', vertical] = bm - self._strands['vertical', vertical] = dic if vertical: + self._braid_monodromy_vertical = bm + self._strands_vertical = dic self._vertical_lines_in_braid_mon = dv + else: + self._braid_monodromy_non_vertical = bm + self._strands_nonvertical = dic return bm def strands(self): @@ -645,9 +690,9 @@ def strands(self): sage: A.strands() {0: 2, 1: 1, 2: 0, 3: 0} """ - if not self._strands['vertical', False]: + if not self._strands_nonvertical: self._braid_monodromy = self.braid_monodromy(vertical=False) - return self._strands['vertical', False] + return self._strands_nonvertical def vertical_strands(self): r""" @@ -674,9 +719,9 @@ def vertical_strands(self): sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] """ - if not self._strands['vertical', True]: + if not self._strands_vertical: self.braid_monodromy(vertical=True) - return self._strands['vertical', True] + return self._strands_vertical def vertical_lines_in_braid_mon(self): r""" @@ -738,8 +783,10 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - self._fundamental_group = {('simpl', bool_s): None for bool_s in (True, False)} - self._meridians = {('simpl', bool_s): None for bool_s in (True, False)} + self._fundamental_group_nonsimpl = None + self._fundamental_group_simpl = None + self._meridians_nonsimpl = None + self._meridians_simpl = None def fundamental_group(self, simplified=True): r""" @@ -791,7 +838,10 @@ def fundamental_group(self, simplified=True): < x0, x1, x2 | x2*x0*x1*x0^-1*x2^-1*x1^-1, x1*(x2*x0)^2*x2^-1*x1^-1*x0^-1*x2^-1*x0^-1 > """ - computed = self._fundamental_group['simpl', simplified] + if simplified: + computed = self._fundamental_group_simpl + else: + computed = self._fundamental_group_nonsimpl if computed: return computed H = self.parent() @@ -806,9 +856,12 @@ def fundamental_group(self, simplified=True): infinity_in_C = infinity in C if infinity_in_C and n == 1: G = FreeGroup(0) / [] - for bool_s in (True, False): - self._fundamental_group['simpl', bool_s] = G - self._meridians['simpl', bool_s] = {0: [G.one()]} + if simplified: + self._fundamental_group_simpl = G + self._meridians_simpl = {0: [G.one()]} + else: + self._fundamental_group_nonsimpl = G + self._meridians_nonsimpl = {0: [G.one()]} return G if infinity_in_C: j = C.curves().index(infinity) @@ -845,8 +898,12 @@ def fundamental_group(self, simplified=True): dic1[j] += dic[n] else: dic1 = dic - self._fundamental_group['simpl', simplified] = G - self._meridians['simpl', simplified] = dic1 + if simplified: + self._fundamental_group_simpl = G + self._meridians_simpl = dic1 + else: + self._fundamental_group_nonsimpl = G + self._meridians_nonsimpl = dic1 return G def meridians(self, simplified=True): @@ -886,11 +943,19 @@ def meridians(self, simplified=True): sage: A.meridians() {0: [x0, x2*x0*x2^-1], 1: [x2, x0^-1*x2^-1*x1^-1*x0^-1], 2: [x1]} """ - computed = self._meridians['simpl', simplified] + if simplified: + computed = self._meridians_simpl + else: + computed = self._meridians_nonsimpl + if computed: + return computed if computed: return computed _ = self._fundamental_group(simplified=simplified) - return self._meridians_simplified + if simplified: + return self._meridians_simpl + else: + return self._meridians_nonsimpl class AffinePlaneCurveArrangements(UniqueRepresentation, Parent): From 73a728d50216d76c94cefedca2127cb0f0f084b2 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 8 Feb 2024 16:13:44 +0100 Subject: [PATCH 81/95] change attributes in arrangement code --- .../geometry/hyperplane_arrangement/ordered_arrangement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index c986f72b34c..686211fd2bb 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -324,7 +324,7 @@ def affine_fundamental_group(self): L = Af([vector(line.coefficients()) * coord for line in self]) G = L.fundamental_group() self._affine_fundamental_group = G - self._affine_meridians = L._meridians['simpl', True, 'vertical', True] + self._affine_meridians = L._meridians_simpl_vertical return G H1 = self.hyperplane_section(proj=False) G = H1.affine_fundamental_group() @@ -451,7 +451,7 @@ def projective_fundamental_group(self): L = Proj([vector(line.coefficients()[1:]) * coord for line in self]) G = L.fundamental_group() self._projective_fundamental_group = G - self._projective_meridians = L._meridians['simpl', True] + self._projective_meridians = L._meridians_simpl return G H1 = self.hyperplane_section() G = H1.projective_fundamental_group() From 3cbafec85ef2aeaf7557d2e67ced4aaddc2c13a8 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 11 Feb 2024 22:07:55 +0100 Subject: [PATCH 82/95] changes following #37281 --- .../schemes/curves/plane_curve_arrangement.py | 4 +- src/sage/schemes/curves/zariski_vankampen.py | 91 +++++++++---------- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 718c0bbadfc..724f7f607d4 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -534,7 +534,7 @@ def fundamental_group(self, simplified=True, vertical=True, bm = self._braid_monodromy_vertical else: bm = self._braid_monodromy_non_vertical - if bm is not None: + if bm is not None: # bm could be [] if not vertical: st = self._strands_nonvertical d1 = prod(L).degree() @@ -601,7 +601,7 @@ def meridians(self, simplified=True, vertical=True): computed = self._meridians_nonsimpl_nonvertical if computed: return computed - _ = self.fundamental_group(simplified=simplified, vertical=vertical) + self.fundamental_group(simplified=simplified, vertical=vertical) if simplified and vertical: return self._meridians_simpl_vertical elif simplified and not vertical: diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index c2912f517da..265209dec40 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -166,7 +166,7 @@ def sgn(x, y): return B(braid) -def discrim(pols): +def discrim(pols) -> tuple: r""" Return the points in the discriminant of the product of the polynomials of a list or tuple ``pols``. @@ -198,9 +198,8 @@ def discrim(pols): 0.2613789792873551? + 0.4527216721561923?*I, 1] """ - flist = tuple(pols) - x, y = flist[0].parent().gens() - field = flist[0].base_ring() + x, y = pols[0].parent().gens() + field = pols[0].base_ring() pol_ring = PolynomialRing(field, (x,)) @parallel @@ -209,8 +208,8 @@ def discrim_pairs(f, g): return pol_ring(f.discriminant(y)) return pol_ring(f.resultant(g, y)) - pairs = [(f, None) for f in flist] + [(f, g) for f, g - in Combinations(flist, 2)] + pairs = [(f, None) for f in pols] + [tuple(t) for t + in Combinations(pols, 2)] fdiscrim = discrim_pairs(pairs) rts = () poly = 1 @@ -484,7 +483,7 @@ def voronoi_cells(V, vertical_lines=frozenset()): return (G, E, p, EC, DG, vertical_regions) -def followstrand(f, factors, x0, x1, y0a, prec=53): +def followstrand(f, factors, x0, x1, y0a, prec=53) -> list: r""" Return a piecewise linear approximation of the homotopy continuation of the root ``y0a`` from ``x0`` to ``x1``. @@ -751,7 +750,7 @@ def roots_interval(f, x0): envelop = IF(diam) * IF((-1, 1), (-1, 1)) qapr = QQ(CF(r).real()) + QQbar.gen() * QQ(CF(r).imag()) if qapr not in r + envelop: - raise ValueError("Could not approximate roots with exact values") + raise ValueError("could not approximate roots with exact values") result[qapr] = r + envelop return result @@ -839,7 +838,7 @@ def braid_in_segment(glist, x0, x1, precision={}): - ``glist`` -- a tuple of polynomials in two variables - ``x0`` -- a Gauss rational - ``x1`` -- a Gauss rational - - ``precision`` -- a dictionary (default: `dict()`) which assigns a number + - ``precision`` -- a dictionary (default: `{}`) which assigns a number precision bits to each element of ``glist`` OUTPUT: @@ -882,7 +881,7 @@ def braid_in_segment(glist, x0, x1, precision={}): sage: B = braid_in_segment(glist, p1b, p2b); B # needs sirocco s5*s3^-1 """ - precision1 = {_: precision[_] for _ in precision.keys()} + precision1 = precision.copy() g = prod(glist) F1 = g.base_ring() x, y = g.parent().gens() @@ -946,7 +945,7 @@ def braid_in_segment(glist, x0, x1, precision={}): return initialbraid * centralbraid * finalbraid -def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}): +def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}) -> list: r""" Return a geometric basis, based on a vertex. @@ -1016,10 +1015,10 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}): if j in vertical_regions: vd = {j: 0} else: - vd = dict() + vd = {} return [EC], vd - InternalEdges = [_ for _ in G.edges(sort=True) if _ not in - E.edges(sort=True)] + edges_E = E.edges(sort=True) + InternalEdges = [e for e in G.edges(sort=True) if e not in edges_E] InternalVertices = [v for e in InternalEdges for v in e[:2]] Internal = G.subgraph(vertices=InternalVertices, edges=InternalEdges) for i, ECi in enumerate(EC): # q and r are the points we will cut through @@ -1027,7 +1026,7 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}): EI = [v for v in E if v in Internal.connected_component_containing_vertex(ECi, sort=True) and v != ECi] - if len(EI) > 0: + if EI: q = ECi connecting_path = list(EC[:i]) break @@ -1036,7 +1035,7 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}): Internal.connected_component_containing_vertex(EC[-i], sort=True) and v != EC[-i]] - if len(EI) > 0: + if EI: q = EC[-i] connecting_path = list(reversed(EC[-i:])) break @@ -1135,14 +1134,14 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}): return (resul, vd) -def vertical_lines_in_braidmon(flist): +def vertical_lines_in_braidmon(pols) -> list: r""" - Returns the vertical lines in ``flist``, unless + Returns the vertical lines in ``pols``, unless one of the other components has a vertical asymptote. INPUT: - - ``flist`` -- a list of polynomials with two variables whose + - ``pols`` -- a list of polynomials with two variables whose product equals ``f`` OUTPUT: @@ -1164,10 +1163,10 @@ def vertical_lines_in_braidmon(flist): sage: vertical_lines_in_braidmon([]) [] """ - if not flist: + if not pols: return [] res = [] - for j, f in enumerate(flist): + for j, f in enumerate(pols): C = Curve(f) vertical_asymptote = C.has_vertical_asymptote() if vertical_asymptote: @@ -1177,7 +1176,7 @@ def vertical_lines_in_braidmon(flist): return res -def strand_components(f, flist, p1): +def strand_components(f, pols, p1): r""" Compute only the assignment from strands to elements of ``flist``. @@ -1186,7 +1185,7 @@ def strand_components(f, flist, p1): - ``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 + - ``pols`` -- a list of polynomials with two variables whose product equals ``f`` - ``p1`` -- a Gauss rational @@ -1211,14 +1210,14 @@ def strand_components(f, flist, p1): (1, 0), (1.333333333333334?, 1)], {0: 0, 1: 0, 2: 0, 3: 1}) """ x, y = f.parent().gens() - F = flist[0].base_ring() + F = pols[0].base_ring() strands = {} roots_base = [] - for i, h in enumerate(flist): + for i, h in enumerate(pols): h0 = h.subs({x: p1}) h1 = F[y](h0) rt = h1.roots(QQbar, multiplicities=False) - roots_base += [(_, i) for _ in rt] + roots_base += [(r, i) for r in rt] roots_base.sort() strands = {i: par[1] for i, par in enumerate(roots_base)} return (roots_base, strands) @@ -1299,10 +1298,10 @@ def braid_monodromy(f, arrangement=(), vertical=False): F = fieldI(f.base_ring()) I1 = F(QQbar.gen()) f = f.change_ring(F) - if arrangement == (): + if not arrangement: arrangement1 = (f,) else: - arrangement1 = tuple(_.change_ring(F) for _ in arrangement) + arrangement1 = tuple(g.change_ring(F) for g in arrangement) x, y = f.parent().gens() if vertical: indices_v = vertical_lines_in_braidmon(arrangement1) @@ -1312,7 +1311,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): if j not in indices_v) arrangement_v = tuple(f0 for j, f0 in enumerate(arrangement1) if j in indices_v) - glist = tuple(_[0] for f0 in arrangement_h for _ in f0.factor()) + glist = tuple(fc[0] for f0 in arrangement_h for fc in f0.factor()) g = f.parent()(prod(glist)) d = g.degree(y) if not arrangement_v: # change of coordinates only if indices_v is empty @@ -1326,8 +1325,8 @@ def braid_monodromy(f, arrangement=(), vertical=False): disc = discrim(glist) else: disc = [] - vertical_braid = dict() - transversal = dict() + vertical_braid = {} + transversal = {} vl = [] for f0 in arrangement_v: pt = [j for j, t in enumerate(disc) if f0.subs({x: t}) == 0] @@ -1349,20 +1348,20 @@ def braid_monodromy(f, arrangement=(), vertical=False): p1 = F(0) if d > 0: roots_base, strands = strand_components(g, arrangement_h, p1) - strands1 = dict() + strands1 = {} for j in range(d): i = strands[j] k = arrangement1.index(arrangement_h[i]) strands1[j] = k else: - strands1 = dict() + strands1 = {} return (result, strands1, vertical_braids, d) V = corrected_voronoi_diagram(tuple(disc)) G, E, p, EC, DG, VR = voronoi_cells(V, vertical_lines=vl) p0 = (p[0], p[1]) p1 = p0[0] + I1 * p0[1] roots_base, strands = strand_components(g, arrangement_h, p1) - strands1 = dict() + strands1 = {} for j in range(d): i = strands[j] k = arrangement1.index(arrangement_h[i]) @@ -1403,7 +1402,7 @@ def braid_monodromy(f, arrangement=(), vertical=False): x1 = tuple(path[i + 1].vector()) braidpath = braidpath * segsbraids[(x0, x1)] result.append(braidpath) - vertical_braids = dict() + vertical_braids = {} r = len(result) t = 0 for f0 in arrangement_v: @@ -1465,8 +1464,8 @@ 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): - block = [j for j in L1 if j > cuts[i] and j < cuts[i + 1]] - if len(block) > 0: + block = [j for j in L1 if cuts[i] < j < cuts[i + 1]] + if block: blocks.append(block) shorts = [] for a in blocks: @@ -1477,7 +1476,7 @@ def conjugate_positive_form(braid): A1 = rightnormalform(sg) par = A1[-1][0] % 2 A1 = [B(a) for a in A1[:-1]] - if len(A1) == 0: + if not A1: b = B.one() else: b = prod(A1) @@ -1540,8 +1539,8 @@ 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]] - U = [tuple(sign(k1)*(abs(k1) + k) for k1 in _.Tietze()) for _ in U0] - pasos = [B.one()] + [_ for _ in reversed(L1)] + U = [tuple(sign(k1) * (abs(k1) + k) for k1 in _.Tietze()) for _ in U0] + pasos = [B.one()] + list(reversed(L1)) for C in pasos: U = [(F(a) * C.inverse()).Tietze() for a in U] ga = F / U @@ -1632,7 +1631,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, """ vertical0 = sorted(vertical) v = len(vertical0) - if bm == []: + if not bm: d = degree elif bm[0].parent().order() == 1: d = 1 @@ -1776,7 +1775,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=True): while g.degree(y) < g.degree(): g = g.subs({x: x + y}) bm = braid_monodromy(g)[0] - if bm == []: + if not bm: d = g.degree(y) else: d = bm[0].parent().strands() @@ -1886,14 +1885,14 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, x1*x2*x0*x1^-1*x0^-1*x2^-1 >, {0: [x2], 1: [x0], 2: [x3], 3: [x1], 4: [x3^-1*x2^-1*x1^-1*x0^-1]}) """ - if len(flist) > 0: + if flist: f = prod(flist) R = f.parent() else: R = PolynomialRing(QQ, ('x', 'y')) f = R(1) x, y = R.gens() - flist1 = [_ for _ in flist] + flist1 = tuple(list(flist).copy()) if vertical and vertical_lines_in_braidmon(flist1): infinity = all([Curve(g).is_vertical_line() or g.degree(y) == g.degree() for g in flist1]) @@ -1904,7 +1903,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, infinity = all([g.degree(y) == g.degree() for g in flist1]) if braid_data: bm, dic, dv, d1 = braid_data - elif len(flist1) == 0: + elif not flist: bm = [] dic = {} dv = {j: j for j, f in flist1} @@ -1924,7 +1923,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, else: hom = g.hom(codomain=g, im_gens=list(g.gens()), check=False) g1 = hom.codomain() - if len(flist) == 0: + if not flist: return (g1, {}) dic1 = {} for i in range(len(flist1)): From 97ee13524dff37e1e53465c96a9b81abc49287bc Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 13 Feb 2024 23:28:45 +0100 Subject: [PATCH 83/95] underscores and other details --- src/sage/schemes/curves/zariski_vankampen.py | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 265209dec40..a56fbb15a44 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -461,8 +461,8 @@ def voronoi_cells(V, vertical_lines=frozenset()): points = [p for p in V.regions().keys() if V.regions()[p].is_compact()] compact_regions = [regions[p] for p in points] vertical_regions = {} - non_compact_regions = [_ for _ in V.regions().values() - if not _.is_compact()] + non_compact_regions = [reg for reg in V.regions().values() + if not reg.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) @@ -1476,10 +1476,7 @@ def conjugate_positive_form(braid): A1 = rightnormalform(sg) par = A1[-1][0] % 2 A1 = [B(a) for a in A1[:-1]] - if not A1: - b = B.one() - else: - b = prod(A1) + b = prod(A1, B.one()) b1 = len(b.Tietze()) / (len(A1) + 1) if res is None or b1 < res[3]: res = [tau, A1, par, b1] @@ -1533,13 +1530,13 @@ def braid2rels(L): k = min(T1) - 1 B0 = BraidGroup(m) F0 = FreeGroup(m) - br0 = B0([_-k for _ in T]) + br0 = B0([j-k for j 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:])) + br1 = B0.delta()**r * prod(map(B0, br0_left[1:]), B0.one()) cox = prod(F0.gens()) U0 = [cox**q * (f0 * br1) / cox**q / f0 for f0 in F0.gens()[:-1]] - U = [tuple(sign(k1) * (abs(k1) + k) for k1 in _.Tietze()) for _ in U0] + U = [tuple(sign(k1) * (abs(k1) + k) for k1 in br.Tietze()) for br in U0] pasos = [B.one()] + list(reversed(L1)) for C in pasos: U = [(F(a) * C.inverse()).Tietze() for a in U] @@ -1552,7 +1549,7 @@ def braid2rels(L): P.TzGoGo() P.TzGoGo() gb = wrap_FpGroup(P.FpGroupPresentation()) - U = [_.Tietze() for _ in gb.relations()] + U = [rel.Tietze() for rel in gb.relations()] return U @@ -1933,7 +1930,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, t = prod(hom(a) for a in g.gens()).inverse() dic1[len(flist1)] = [t] n = g1.ngens() - rels = [_.Tietze() for _ in g1.relations()] + rels = [rel.Tietze() for rel in g1.relations()] g1 = FreeGroup(n) / rels dic1 = {i: list(set([g1(el.Tietze()) for el in dic1[i]])) for i in dic1} return (g1, dic1) From 5688d6824d129b1a795fe11bdca81f579097e00e Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Fri, 8 Mar 2024 19:14:55 +0100 Subject: [PATCH 84/95] reviewer's comments --- .../hyperplane_arrangement/arrangement.py | 16 +++----------- src/sage/schemes/curves/affine_curve.py | 2 +- .../schemes/curves/plane_curve_arrangement.py | 21 ++++++++++--------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 18f3c37b26c..1d3686468e5 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -763,13 +763,8 @@ def cone(self, variable='t'): hyperplanes.append([0, 1] + [0] * self.dimension()) P = self.parent() names = (variable,) + P._names - from sage.geometry.hyperplane_arrangement.ordered_arrangement import OrderedHyperplaneArrangements - if isinstance(P, OrderedHyperplaneArrangements): - H = OrderedHyperplaneArrangements(self.parent().base_ring(), names=names) - else: - H = HyperplaneArrangements(self.parent().base_ring(), names=names) - result = H(*hyperplanes, backend=self._backend) - return result + H = type(P).__base__(P.base_ring(), names=names) + return H(*hyperplanes, backend=self._backend) @cached_method def intersection_poset(self, element_label="int"): @@ -1242,10 +1237,6 @@ def restriction(self, hyperplane, repetitions=False): The restriction `\mathcal{A}_H` of the hyperplane arrangement `\mathcal{A}` to the given ``hyperplane`` `H`. - REMARK: - - It applies also to ordered arrangements. - EXAMPLES:: sage: # needs sage.graphs @@ -1781,8 +1772,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) - return result + return restricted_parent(*restricted, signed=False, backend=self._backend) def sign_vector(self, p): r""" diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 1eeaf1f3875..7d46b58823d 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -1910,7 +1910,7 @@ def braid_monodromy(self): sage: T. = QQ[] sage: K. = NumberField(t^3 + 2, 'a') sage: A. = AffineSpace(K, 2) - sage: Curve(y^2 + a * x).braid_monodromy() # needs sirocco + sage: Curve(y^2 + a * x).braid_monodromy() Traceback (most recent call last): ... NotImplementedError: the base field must have an embedding to the algebraic field diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 724f7f607d4..722890d8385 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -619,9 +619,9 @@ def braid_monodromy(self, vertical=True): INPUT: - - ``vertical`` -- boolean (default: True). If it is ``True``, there + - ``vertical`` -- boolean (default: ``True``); if it is ``True``, there are no vertical asymptotes, and there are vertical lines, then a - simplified braid_monodromy is computed. + simplified :func:`braid_monodromy` is computed. OUTPUT: @@ -979,21 +979,22 @@ class AffinePlaneCurveArrangements(UniqueRepresentation, Parent): @staticmethod def __classcall__(cls, base, names=()): - names = normalize_names(len(names), names) - return super().__classcall__(cls, base, names) - - def __init__(self, base_ring, names=tuple()): """ - Initialize ``self``. - + Normalize names TESTS:: sage: H. = AffinePlaneCurveArrangements(QQ) sage: K = AffinePlaneCurveArrangements(QQ, names=('x', 'y')) sage: H is K True - sage: type(K) - + sage: TestSuite(K).run() + """ + names = normalize_names(len(names), names) + return super().__classcall__(cls, base, names) + + def __init__(self, base_ring, names=tuple()): + """ + Initialize ``self``. TESTS:: From f83cf48efe02b2ac626577014928c788bee3bf98 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 11 Mar 2024 23:21:35 +0100 Subject: [PATCH 85/95] first merge of plane curve arrangement classes --- src/sage/schemes/curves/all.py | 2 + .../schemes/curves/plane_curve_arrangement.py | 148 ++++++++++++------ 2 files changed, 106 insertions(+), 44 deletions(-) diff --git a/src/sage/schemes/curves/all.py b/src/sage/schemes/curves/all.py index daa260b2581..f788cf9f1c9 100644 --- a/src/sage/schemes/curves/all.py +++ b/src/sage/schemes/curves/all.py @@ -25,6 +25,8 @@ from .projective_curve import Hasse_bounds +lazy_import('sage.schemes.curves.plane_curve_arrangement', 'PlaneCurveArrangements') + lazy_import('sage.schemes.curves.plane_curve_arrangement', 'AffinePlaneCurveArrangements') lazy_import('sage.schemes.curves.plane_curve_arrangement', 'ProjectivePlaneCurveArrangements') diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 722890d8385..5c32b80f4c4 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -68,18 +68,18 @@ from sage.structure.unique_representation import UniqueRepresentation -class AffinePlaneCurveArrangementsElement(Element): +class PlaneCurveArrangementElement(Element): """ - An ordered affine plane curve arrangement. + An ordered plane curve arrangement. """ def __init__(self, parent, curves, check=True): """ - Construct an ordered affine plane curve arrangement. + Construct a plane curve arrangement. INPUT: - - ``parent`` -- the parent :class:`AffinePlaneCurveArrangements` + - ``parent`` -- the parent :class:`PlaneCurveArrangements` - ``curves`` -- a tuple of curves @@ -95,24 +95,13 @@ def __init__(self, parent, curves, check=True): if check: if not isinstance(curves, tuple): raise ValueError("the curves must be given as a tuple") - if not all(isinstance(h, AffinePlaneCurve) for h in curves): + affine = all(isinstance(h, AffinePlaneCurve) for h in curves) + projective = all(isinstance(h, ProjectivePlaneCurve) for h in curves) + if not (affine or projective): raise ValueError("not all elements are curves") if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - self._braid_monodromy_non_vertical = None - self._braid_monodromy_vertical = None - self._strands_nonvertical = None - self._strands_vertical = None - self._fundamental_group_nonsimpl_nonvertical = None - self._fundamental_group_nonsimpl_vertical = None - self._fundamental_group_simpl_nonvertical = None - self._fundamental_group_simpl_vertical = None - self._meridians_nonsimpl_nonvertical = None - self._meridians_nonsimpl_vertical = None - self._meridians_simpl_nonvertical = None - self._meridians_simpl_vertical = None - self._vertical_lines_in_braid_mon = None def __getitem__(self, i): """ @@ -445,6 +434,53 @@ def reduce(self, clean=False): L.append(g) return P(*L) + +class AffinePlaneCurveArrangementElement(PlaneCurveArrangementElement): + """ + An ordered affine plane curve arrangement. + """ + + def __init__(self, parent, curves, check=True): + """ + Construct an ordered affine plane curve arrangement. + + INPUT: + + - ``parent`` -- the parent :class:`AffinePlaneCurveArrangements` + + - ``curves`` -- a tuple of curves + + EXAMPLES:: + + sage: H. = AffinePlaneCurveArrangements(QQ) + sage: elt = H(x, y); elt + Arrangement (x, y) in Affine Space of dimension 2 over Rational Field + sage: TestSuite(elt).run() + """ + Element.__init__(self, parent) + self._curves = curves + if check: + if not isinstance(curves, tuple): + raise ValueError("the curves must be given as a tuple") + if not all(isinstance(h, AffinePlaneCurve) for h in curves): + raise ValueError("not all elements are curves") + if not all(h.ambient_space() is self.parent().ambient_space() + for h in curves): + raise ValueError("not all curves are in the same ambient space") + self._braid_monodromy_non_vertical = None + self._braid_monodromy_vertical = None + self._strands_nonvertical = None + self._strands_vertical = None + self._fundamental_group_nonsimpl_nonvertical = None + self._fundamental_group_nonsimpl_vertical = None + self._fundamental_group_simpl_nonvertical = None + self._fundamental_group_simpl_vertical = None + self._meridians_nonsimpl_nonvertical = None + self._meridians_nonsimpl_vertical = None + self._meridians_simpl_nonvertical = None + self._meridians_simpl_vertical = None + self._vertical_lines_in_braid_mon = None + def fundamental_group(self, simplified=True, vertical=True, projective=False): r""" @@ -751,7 +787,7 @@ def vertical_lines_in_braid_mon(self): return self._vertical_lines_in_braid_mon -class ProjectivePlaneCurveArrangementsElement(AffinePlaneCurveArrangementsElement): +class ProjectivePlaneCurveArrangementElement(PlaneCurveArrangementElement): """ An ordered projective plane curve arrangement. """ @@ -958,9 +994,9 @@ def meridians(self, simplified=True): return self._meridians_nonsimpl -class AffinePlaneCurveArrangements(UniqueRepresentation, Parent): +class PlaneCurveArrangements(UniqueRepresentation, Parent): """ - Affine curve arrangements. + Plane curve arrangements. INPUT: @@ -970,12 +1006,12 @@ class AffinePlaneCurveArrangements(UniqueRepresentation, Parent): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: H(x, y^2, x-1, y-1) Arrangement (x, y^2, x - 1, y - 1) in Affine Space of dimension 2 over Rational Field """ - Element = AffinePlaneCurveArrangementsElement + Element = PlaneCurveArrangementElement @staticmethod def __classcall__(cls, base, names=()): @@ -1036,7 +1072,7 @@ def change_ring(self, base_ring): EXAMPLES:: - sage: L. = AffinePlaneCurveArrangements(QQ) + sage: L. = PlaneCurveArrangements(QQ) sage: L.gen(0) x sage: L.change_ring(RR).base_ring() @@ -1047,7 +1083,7 @@ def change_ring(self, base_ring): sage: L.change_ring(QQ) is L True """ - return AffinePlaneCurveArrangements(base_ring, names=self.variable_names()) + return PlaneCurveArrangements(base_ring, names=self.variable_names()) @cached_method def ambient_space(self): @@ -1059,8 +1095,15 @@ def ambient_space(self): sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.ambient_space() Affine Space of dimension 2 over Rational Field + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Projective Space of dimension 2 over Rational Field """ - return AffineSpace(self.base_ring(), 2, self.variable_names()) + n = len(self._names) + if n == 2: + return AffineSpace(self.base_ring(), 2, self._names) + if n == 3: + return ProjectiveSpace(self.base_ring(), 2, self._names) def _repr_(self): """ @@ -1202,7 +1245,40 @@ def gen(self, i): return self.gens()[i] -class ProjectivePlaneCurveArrangements(AffinePlaneCurveArrangements): +class AffinePlaneCurveArrangements(PlaneCurveArrangements): + """ + Affine curve arrangements. + + INPUT: + + - ``base_ring`` -- ring; the base ring + + - ``names`` -- tuple of strings; the variable names + + EXAMPLES:: + + sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H(x, y^2, x-1, y-1) + Arrangement (x, y^2, x - 1, y - 1) in Affine Space + of dimension 2 over Rational Field + """ + Element = AffinePlaneCurveArrangementElement + + def __init__(self, base_ring, names=tuple()): + """ + Initialize ``self``. + + TESTS:: + + sage: H. = AffinePlaneCurveArrangements(QQ) + sage: TestSuite(H).run() + """ + if base_ring not in _Fields: + raise ValueError('base ring must be a field') + super().__init__(base_ring, names=names) + + +class ProjectivePlaneCurveArrangements(PlaneCurveArrangements): """ Projective curve arrangements. @@ -1219,7 +1295,7 @@ class ProjectivePlaneCurveArrangements(AffinePlaneCurveArrangements): Arrangement (x, y^2, x - z, y - z) in Projective Space of dimension 2 over Rational Field """ - Element = ProjectivePlaneCurveArrangementsElement + Element = ProjectivePlaneCurveArrangementElement def __init__(self, base_ring, names=tuple()): """ @@ -1241,20 +1317,4 @@ def __init__(self, base_ring, names=tuple()): """ if base_ring not in _Fields: raise ValueError('base ring must be a field') - # super().__init__(base_ring, names=names) - # self._base_ring = base_ring - # self._names = names super().__init__(base_ring, names=names) - - @cached_method - def ambient_space(self): - """ - Return the ambient space. - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.ambient_space() - Projective Space of dimension 2 over Rational Field - """ - return ProjectiveSpace(self.base_ring(), 2, self._names) From 50cfa19dfb6ddf512258d4089600017af6fd31e5 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 12 Mar 2024 14:04:23 +0100 Subject: [PATCH 86/95] fixing doctests for the new class --- .../schemes/curves/plane_curve_arrangement.py | 62 +++++++++++++------ 1 file changed, 42 insertions(+), 20 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 5c32b80f4c4..1693f89eee1 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -85,10 +85,14 @@ def __init__(self, parent, curves, check=True): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: elt = H(x, y); elt Arrangement (x, y) in Affine Space of dimension 2 over Rational Field sage: TestSuite(elt).run() + sage: H. = PlaneCurveArrangements(QQ) + sage: elt = H(x, y); elt + Arrangement (x, y) in Projective Space of dimension 2 over Rational Field + sage: TestSuite(elt).run() """ super().__init__(parent) self._curves = curves @@ -102,6 +106,10 @@ def __init__(self, parent, curves, check=True): if not all(h.ambient_space() is self.parent().ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") + if projective: + homogeneous = all(h.defining_polynomial().is_homogeneous() for h in curves) + if not homogeneous: + raise ValueError("the defining polynomials of plane projective curves must be homogeneous") def __getitem__(self, i): """ @@ -117,8 +125,8 @@ def __getitem__(self, i): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) - sage: h = H(y^2 - x, y^3 + 2 * x^2, x^4 + y^4 + 1); h + sage: H. = PlaneCurveArrangements(QQ) + sage: H(y^2 - x, y^3 + 2 * x^2, x^4 + y^4 + 1) Arrangement (y^2 - x, y^3 + 2*x^2, x^4 + y^4 + 1) in Affine Space of dimension 2 over Rational Field """ @@ -128,9 +136,9 @@ def __hash__(self): r""" TESTS:: - sage: H. = AffinePlaneCurveArrangements(QQ) - sage: h = H((x * y, x + y +1)) - sage: len_dict = {h: len(h)} + sage: H. = PlaneCurveArrangements(QQ) + sage: H((x * y, x + y +1)).__hash__() # random + -4938643871296220686 """ return hash(self.curves()) @@ -144,8 +152,8 @@ def n_curves(self): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) - sage: h = H((x * y, x + y +1)) + sage: H. = PlaneCurveArrangements(QQ) + sage: h = H((x * y, x + y + z)) sage: h.n_curves() 2 sage: len(h) # equivalent @@ -165,7 +173,7 @@ def curves(self): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: h = H((x * y, x + y + 1)) sage: h.curves() (Affine Plane Curve over Rational Field defined by x*y, @@ -188,12 +196,17 @@ def _repr_(self): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: h Arrangement of 5 curves in Affine Space of dimension 2 over Rational Field sage: H(()) Empty curve arrangement in Affine Space of dimension 2 over Rational Field + sage: H. = PlaneCurveArrangements(QQ) + sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) + sage: h + Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field + """ if len(self) == 0: return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) @@ -211,7 +224,7 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: H(x) == H(y) False sage: H(x) == H(2 * x) @@ -239,7 +252,7 @@ def union(self, other): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: C = Curve(x^8 - y^8 -x^4 * y^4) sage: h1 = h.union(C); h1 @@ -279,7 +292,7 @@ def deletion(self, curves): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: C = h[-1] sage: h.deletion(C) @@ -317,7 +330,7 @@ def change_ring(self, base_ring): EXAMPLES:: sage: # needs sage.rings.number_field - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) sage: K. = CyclotomicField(3) sage: A.change_ring(K) @@ -338,10 +351,14 @@ def coordinate_ring(self): EXAMPLES:: - sage: L. = AffinePlaneCurveArrangements(QQ) + sage: L. = PlaneCurveArrangements(QQ) sage: C = L(x, y) sage: C.coordinate_ring() Multivariate Polynomial Ring in x, y over Rational Field + sage: P. = PlaneCurveArrangements(QQ) + sage: C = P(x, y) + sage: C.coordinate_ring() + Multivariate Polynomial Ring in x, y, z over Rational Field """ return self.curves()[0].defining_polynomial().parent() @@ -351,7 +368,7 @@ def defining_polynomials(self): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) sage: A.defining_polynomials() (-x^3 + y^2, x, y, x^2 + x*y + y^2) @@ -364,7 +381,7 @@ def defining_polynomial(self, simplified=True): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: A = H(y ** 2 + x ** 2, x, y) sage: prod(A.defining_polynomials()) == A.defining_polynomial() True @@ -404,7 +421,7 @@ def reduce(self, clean=False): EXAMPLES:: - sage: H. = AffinePlaneCurveArrangements(QQ) + sage: H. = PlaneCurveArrangements(QQ) sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2)) sage: A.reduce() Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Affine Space @@ -1040,6 +1057,11 @@ def __init__(self, base_ring, names=tuple()): if base_ring not in _Fields: raise ValueError('base ring must be a field') super().__init__(base_ring, names=names, category=Sets()) + self._embedded = None + if len(names) == 2: + self._embedded = 'affine' + elif len(names) == 3: + self._embedded = 'projective' def coordinate_ring(self): """ @@ -1156,13 +1178,13 @@ def _element_constructor_(self, *args, **kwds): if ambient == ambient_space: curves += (h, ) else: - return None + raise TypeError('the curves do not have the same ambient space') except AttributeError: try: h = R(h) curves += (Curve(h), ) except TypeError: - return None + raise TypeError('elements are not curves') return self.element_class(self, curves) def _an_element_(self): From d724267fcf4c0ff4d2667b83bc1c64be88176efe Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 21 Mar 2024 01:15:32 +0100 Subject: [PATCH 87/95] more reviewer changes --- .../hyperplane_arrangement/arrangement.py | 6 +- .../ordered_arrangement.py | 44 ++++--- .../schemes/curves/plane_curve_arrangement.py | 109 +++++------------- src/sage/schemes/curves/projective_curve.py | 10 +- src/sage/schemes/curves/zariski_vankampen.py | 34 +++--- 5 files changed, 84 insertions(+), 119 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 3b0e9d03390..545e28f9c77 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -2613,9 +2613,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""" diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 686211fd2bb..cc8efa06837 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -299,6 +299,11 @@ def affine_fundamental_group(self): < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1, x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > + sage: A = OrderedHyperplaneArrangements(QQ, names=()) + sage: H = A(); H + Empty hyperplane arrangement of dimension 0 + sage: H.affine_fundamental_group() + Finitely presented group < | > """ K = self.base_ring() if not K.is_subring(QQbar): @@ -424,6 +429,19 @@ def projective_fundamental_group(self): Traceback (most recent call last): ... TypeError: the arrangement is not projective + sage: T. = QQ[] + sage: K. = NumberField(t^3 + t + 1) + sage: L. = OrderedHyperplaneArrangements(K) + sage: H = L(a*x + y - z, x + a*y + z, x - z, y - z) + sage: H.projective_fundamental_group() + Traceback (most recent call last): + ... + TypeError: the base field is not in QQbar + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(); H + Empty hyperplane arrangement of dimension 1 + sage: H.projective_fundamental_group() + Finitely presented group < | > """ K = self.base_ring() if not K.is_subring(QQbar): @@ -461,7 +479,7 @@ def projective_fundamental_group(self): def projective_meridians(self): r""" - Return the meridian of each hyperplane + Return the meridian of each hyperplane. OUTPUT: @@ -530,7 +548,7 @@ class OrderedHyperplaneArrangements(HyperplaneArrangements): def _element_constructor_(self, *args, **kwds): """ - Repetition of the corresponding function for hyperplane arrangements without reordering. + Construct an element of ``self``. INPUT: @@ -547,18 +565,18 @@ def _element_constructor_(self, *args, **kwds): EXAMPLES:: sage: L. = OrderedHyperplaneArrangements(QQ) - sage: L._element_constructor_(x) + sage: L(x) Arrangement - sage: L._element_constructor_(x, y) + sage: L(x, y) Arrangement - sage: L._element_constructor_([x, y]) + sage: L([x, y]) Arrangement - sage: L._element_constructor_([0, 1, 0], [0, 0, 1]) + sage: L([0, 1, 0], [0, 0, 1]) Arrangement - sage: L._element_constructor_([[0, 0, 1], [0, 1, 0]]) + sage: L([[0, 0, 1], [0, 1, 0]]) Arrangement - sage: L._element_constructor_(polytopes.hypercube(2)) + sage: L(polytopes.hypercube(2)) Arrangement <-x + 1 | -y + 1 | x + 1 | y + 1> sage: L(-x, x + y - 1, signed=False) @@ -581,7 +599,7 @@ def _element_constructor_(self, *args, **kwds): """ if len(args) == 1: arg = args[0] - if isinstance(arg, HyperplaneArrangementElement) and args[0].parent() is self: + if isinstance(arg, HyperplaneArrangementElement) and arg.parent() is self: # optimization if argument is already a hyperplane arrangement return arg if arg == 0 and not isinstance(arg, Hyperplane): @@ -590,7 +608,6 @@ def _element_constructor_(self, *args, **kwds): # process keyword arguments not_char2 = (self.base_ring().characteristic() != 2) signed = kwds.pop('signed', not_char2) - warn_duplicates = kwds.pop('warn_duplicates', False) check = kwds.pop('check', True) backend = kwds.pop('backend', None) if len(kwds) > 0: @@ -608,10 +625,6 @@ def _element_constructor_(self, *args, **kwds): else: hyperplanes = [AA(_) for _ in arg] hyperplanes = [h.primitive(signed) for h in hyperplanes] - if warn_duplicates: - from warnings import warn - warn('Input contained repeated hyperplanes.') - # argument checking (optional but recommended) if check: if signed and not not_char2: raise ValueError('cannot be signed in characteristic 2') @@ -633,6 +646,7 @@ def _repr_(self): EXAMPLES:: sage: L. = OrderedHyperplaneArrangements(QQ); L - Ordered hyperplane arrangements in 2-dimensional linear space over Rational Field with coordinates x, y + Ordered hyperplane arrangements in 2-dimensional linear space + over Rational Field with coordinates x, y """ return 'Ordered hyperplane arrangements in {0}'.format(self.ambient_space()) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 1693f89eee1..840ef884eab 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -46,8 +46,8 @@ # http://www.gnu.org/licenses/ # ***************************************************************************** +from itertools import combinations from sage.categories.sets_cat import Sets -from sage.combinat.combination import Combinations from sage.groups.free_group import FreeGroup from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod @@ -72,7 +72,6 @@ class PlaneCurveArrangementElement(Element): """ An ordered plane curve arrangement. """ - def __init__(self, parent, curves, check=True): """ Construct a plane curve arrangement. @@ -95,21 +94,15 @@ def __init__(self, parent, curves, check=True): sage: TestSuite(elt).run() """ super().__init__(parent) - self._curves = curves + self._curves = tuple(curves) if check: - if not isinstance(curves, tuple): - raise ValueError("the curves must be given as a tuple") affine = all(isinstance(h, AffinePlaneCurve) for h in curves) projective = all(isinstance(h, ProjectivePlaneCurve) for h in curves) if not (affine or projective): raise ValueError("not all elements are curves") - if not all(h.ambient_space() is self.parent().ambient_space() + if not all(h.ambient_space() is parent.ambient_space() for h in curves): raise ValueError("not all curves are in the same ambient space") - if projective: - homogeneous = all(h.defining_polynomial().is_homogeneous() for h in curves) - if not homogeneous: - raise ValueError("the defining polynomials of plane projective curves must be homogeneous") def __getitem__(self, i): """ @@ -142,7 +135,7 @@ def __hash__(self): """ return hash(self.curves()) - def n_curves(self): + def ncurves(self): r""" Return the number of curves in the arrangement. @@ -154,14 +147,14 @@ def n_curves(self): sage: H. = PlaneCurveArrangements(QQ) sage: h = H((x * y, x + y + z)) - sage: h.n_curves() + sage: h.ncurves() 2 sage: len(h) # equivalent 2 """ return len(self._curves) - __len__ = n_curves + __len__ = ncurves def curves(self): r""" @@ -341,6 +334,7 @@ def change_ring(self, base_ring): curves = tuple(c.change_ring(base_ring) for c in self) return parent(curves) + @cached_method def coordinate_ring(self): """ Return the coordinate ring of ``self``. @@ -360,7 +354,7 @@ def coordinate_ring(self): sage: C.coordinate_ring() Multivariate Polynomial Ring in x, y, z over Rational Field """ - return self.curves()[0].defining_polynomial().parent() + return self._curves[0].defining_polynomial().parent() def defining_polynomials(self): r""" @@ -373,7 +367,7 @@ def defining_polynomials(self): sage: A.defining_polynomials() (-x^3 + y^2, x, y, x^2 + x*y + y^2) """ - return tuple(h.defining_polynomial() for h in self) + return tuple([h.defining_polynomial() for h in self._curves]) def defining_polynomial(self, simplified=True): r""" @@ -401,12 +395,9 @@ def have_common_factors(self): sage: H(x * y, x + y^3).have_common_factors() False """ - L = [c.defining_polynomial() for c in self] - C = Combinations(L, 2) - for f1, f2 in C: - if f1.gcd(f2).degree() > 0: - return True - return False + L = [c.defining_polynomial() for c in self._curves] + C = combinations(L, 2) + return any(f1.gcd(f2).degree() > 0 for f1, f2 in C) def reduce(self, clean=False): r""" @@ -438,15 +429,15 @@ def reduce(self, clean=False): """ P = self.parent() R = self.coordinate_ring() - L = [self[0].defining_polynomial().radical()] - for c in self[1:]: + L = [self._curves[0].defining_polynomial().radical()] + for c in self._curves[1:]: g = c.defining_polynomial().radical() for f in L: d = g.gcd(f) if d.degree() > 0 and not clean: print("Some curves have common components") return None - g = R(g / d) + g //= d if g.degree() > 0: L.append(g) return P(*L) @@ -456,7 +447,6 @@ class AffinePlaneCurveArrangementElement(PlaneCurveArrangementElement): """ An ordered affine plane curve arrangement. """ - def __init__(self, parent, curves, check=True): """ Construct an ordered affine plane curve arrangement. @@ -475,10 +465,8 @@ def __init__(self, parent, curves, check=True): sage: TestSuite(elt).run() """ Element.__init__(self, parent) - self._curves = curves + self._curves = tuple(curves) if check: - if not isinstance(curves, tuple): - raise ValueError("the curves must be given as a tuple") if not all(isinstance(h, AffinePlaneCurve) for h in curves): raise ValueError("not all elements are curves") if not all(h.ambient_space() is self.parent().ambient_space() @@ -506,15 +494,15 @@ def fundamental_group(self, simplified=True, vertical=True, INPUT: - - ``vertical`` -- boolean (default: ``True``); if it is ``True``, there + - ``vertical`` -- boolean (default: ``True``); if ``True``, there are no vertical asymptotes, and there are vertical lines, then a - simplified braid braid_monodromy is used. + simplified braid :func:`braid_monodromy` is used - ``simplified`` -- boolean (default: ``True``); if it is ``True``, the - group is simplified. + group is simplified - ``projective`` -- boolean (default: ``False``); to be used in the - method for projective curves. + method for projective curves OUTPUT: @@ -827,10 +815,8 @@ def __init__(self, parent, curves, check=True): sage: TestSuite(elt).run() """ Element.__init__(self, parent) - self._curves = curves + self._curves = tuple(curves) if check: - if not isinstance(curves, tuple): - raise ValueError("the curves must be given as a tuple") if not all(isinstance(h, ProjectivePlaneCurve) for h in curves): raise ValueError("not all elements are curves") if not all(h.ambient_space() is self.parent().ambient_space() @@ -1053,6 +1039,12 @@ def __init__(self, base_ring, names=tuple()): sage: H. = AffinePlaneCurveArrangements(QQ) sage: TestSuite(H).run() + sage: H. = ProjectivePlaneCurveArrangements(QQ) + sage: K = ProjectivePlaneCurveArrangements(QQ, names=('x', 'y', 'z')) + sage: H is K + True + sage: type(K) + """ if base_ring not in _Fields: raise ValueError('base ring must be a field') @@ -1107,7 +1099,6 @@ def change_ring(self, base_ring): """ return PlaneCurveArrangements(base_ring, names=self.variable_names()) - @cached_method def ambient_space(self): """ Return the ambient space. @@ -1153,13 +1144,13 @@ def _element_constructor_(self, *args, **kwds): EXAMPLES:: sage: L. = AffinePlaneCurveArrangements(QQ) - sage: A = L._element_constructor_(x, y); A + sage: A = L(x, y); A Arrangement (x, y) in Affine Space of dimension 2 over Rational Field - sage: L._element_constructor_([x, y]) == A + sage: L([x, y]) == A True - sage: L._element_constructor_(Curve(x), Curve(y)) == A + sage: L(Curve(x), Curve(y)) == A True - sage: L._element_constructor_(y, x) == A + sage: L(y, x) == A False """ if len(args) == 1: @@ -1200,8 +1191,7 @@ def _an_element_(self): sage: H._an_element_() Arrangement (t) in Projective Space of dimension 2 over Rational Field """ - x = self.gen(0) - return self(x) + return self(self.gen(0)) @cached_method def ngens(self): @@ -1286,19 +1276,6 @@ class AffinePlaneCurveArrangements(PlaneCurveArrangements): """ Element = AffinePlaneCurveArrangementElement - def __init__(self, base_ring, names=tuple()): - """ - Initialize ``self``. - - TESTS:: - - sage: H. = AffinePlaneCurveArrangements(QQ) - sage: TestSuite(H).run() - """ - if base_ring not in _Fields: - raise ValueError('base ring must be a field') - super().__init__(base_ring, names=names) - class ProjectivePlaneCurveArrangements(PlaneCurveArrangements): """ @@ -1318,25 +1295,3 @@ class ProjectivePlaneCurveArrangements(PlaneCurveArrangements): of dimension 2 over Rational Field """ Element = ProjectivePlaneCurveArrangementElement - - def __init__(self, base_ring, names=tuple()): - """ - Initialize ``self``. - - TESTS:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: K = ProjectivePlaneCurveArrangements(QQ, names=('x', 'y', 'z')) - sage: H is K - True - sage: type(K) - - - TESTS:: - - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: TestSuite(H).run() - """ - if base_ring not in _Fields: - raise ValueError('base ring must be a field') - super().__init__(base_ring, names=names) diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 9f48e974567..35860b9d65f 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -437,12 +437,12 @@ def projection(self, P=None, PS=None): if self.defining_polynomials()[i] != 0: F = self.defining_polynomials()[i] # find a point on which it doesn't vanish - ll = list(PP.gens()) + l = list(PP.gens()) for i in range(n + 1): - ll[i] = 0 - while F(ll) == 0: - ll[i] += 1 - Q = PP(ll) # will be a point not on the curve + l[i] = 0 + while F(l) == 0: + l[i] += 1 + Q = PP(l) # will be a point not on the curve else: # if the base ring is a finite field, iterate over all points in the ambient space and check which # are on this curve diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 66576ea3a6b..f09c7dae74f 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -44,7 +44,7 @@ import itertools from copy import copy -from sage.combinat.combination import Combinations +from itertools import combinations from sage.combinat.permutation import Permutation from sage.functions.generalized import sign from sage.geometry.voronoi_diagram import VoronoiDiagram @@ -142,7 +142,7 @@ def sgn(x, y): l1j = l1[j] for k in range(j): if l2j < l2[k]: - t = (l1j[0]-l1[k][0])/((l2[k][0] - l2j[0]) + (l1j[0]-l1[k][0])) + t = (l1j[0] - l1[k][0]) / ((l2[k][0] - l2j[0]) + (l1j[0] - l1[k][0])) s = sgn(l1[k][1] * (1 - t) + t * l2[k][1], l1j[1] * (1 - t) + t * l2j[1]) cruces.append([t, k, j, s]) @@ -178,7 +178,7 @@ def discrim(pols) -> tuple: INPUT: - ``pols`` -- a list or tuple of polynomials in two variables with - coefficients in a number field with a fixed embedding in `QQbar`. + coefficients in a number field with a fixed embedding in `\QQbar`. OUTPUT: @@ -189,9 +189,7 @@ def discrim(pols) -> tuple: sage: from sage.schemes.curves.zariski_vankampen import discrim sage: R. = QQ[] sage: flist = (y^3 + x^3 - 1, 2 * x + y) - sage: L = list(discrim(flist)) - sage: L.sort() - sage: L + sage: sorted((discrim(flist))) [-0.522757958574711?, -0.500000000000000? - 0.866025403784439?*I, -0.500000000000000? + 0.866025403784439?*I, @@ -210,7 +208,7 @@ def discrim_pairs(f, g): return pol_ring(f.resultant(g, y)) pairs = [(f, None) for f in pols] + [tuple(t) for t - in Combinations(pols, 2)] + in combinations(pols, 2)] fdiscrim = discrim_pairs(pairs) rts = () poly = 1 @@ -270,8 +268,8 @@ def corrected_voronoi_diagram(points): V = VoronoiDiagram(configuration) valid = True for r in V.regions().items(): - if not r[1].rays() and \ - not r[1].interior_contains(apprpoints[r[0].affine()]): + if (not r[1].rays() and + not r[1].interior_contains(apprpoints[r[0].affine()])): prec += 53 valid = False break @@ -568,8 +566,8 @@ def followstrand(f, factors, x0, x1, y0a, prec=53) -> list: ci = c.imag() coefsfactors += list(cr.endpoints()) coefsfactors += list(ci.endpoints()) - from sage.libs.sirocco import contpath, contpath_mp, contpath_comps - from sage.libs.sirocco import contpath_mp_comps + from sage.libs.sirocco import (contpath, contpath_mp, contpath_comps, + contpath_mp_comps) try: if prec == 53: if factors: @@ -626,11 +624,11 @@ def fieldI(field): INPUT: - - ``field`` -- a number field with an embedding in ``QQbar``. + - ``field`` -- a number field with an embedding in `\QQbar`. OUTPUT: - The extension ``F`` of ``field`` containing ``I`` with an embedding in ``QQbar``. + The extension ``F`` of ``field`` containing ``I`` with an embedding in `\QQbar`. EXAMPLES:: @@ -1137,7 +1135,7 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}) -> list: def vertical_lines_in_braidmon(pols) -> list: r""" - Returns the vertical lines in ``pols``, unless + Return the vertical lines in ``pols``, unless one of the other components has a vertical asymptote. INPUT: @@ -1201,7 +1199,6 @@ def strand_components(f, pols, p1): EXAMPLES:: - sage: # needs sirocco sage: from sage.schemes.curves.zariski_vankampen import strand_components sage: R. = QQ[] sage: flist = [x^2 - y^3, x + 3 * y - 5] @@ -1679,8 +1676,7 @@ def fundamental_group(f, simplified=True, projective=False, puiseux=True): INPUT: - ``f`` -- a polynomial in two variables, with coefficients in either - the rationals or a number field with a fixed embedding in - `\QQbar` + the rationals or a number field with a fixed embedding in `\QQbar` - ``simplified`` -- boolean (default: ``True``); if set to ``True`` the presentation will be simplified (see below) @@ -1812,11 +1808,11 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, - ``vertical`` -- boolean (default: ``False``); if set to ``True``, whenever no curve has vertical asymptotes the computation of braid - monodromy is simpler if some lines are vertical. + monodromy is simpler if some lines are vertical - ``braid_data`` -- tuple (default: ``None``); if it is not the default it is the output of ``fundamental_group_from_braid_mon`` previously - computed. + computed OUTPUT: From 74ae1c962b7eccae2d82e6935803f6a3a1c6af4a Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Thu, 21 Mar 2024 01:25:04 +0100 Subject: [PATCH 88/95] indentation problem --- .../ordered_arrangement.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index cc8efa06837..353c5cdb6f8 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -299,11 +299,11 @@ def affine_fundamental_group(self): < x0, x1, x2, x3 | x3^-1*x2^-1*x3*x2, x3^-1*x1^-1*x3*x1, x3^-1*x0^-1*x3*x0, x2^-1*x1^-1*x2*x1, x2^-1*x0^-1*x2*x0, x1^-1*x0^-1*x1*x0 > - sage: A = OrderedHyperplaneArrangements(QQ, names=()) - sage: H = A(); H - Empty hyperplane arrangement of dimension 0 - sage: H.affine_fundamental_group() - Finitely presented group < | > + sage: A = OrderedHyperplaneArrangements(QQ, names=()) + sage: H = A(); H + Empty hyperplane arrangement of dimension 0 + sage: H.affine_fundamental_group() + Finitely presented group < | > """ K = self.base_ring() if not K.is_subring(QQbar): @@ -437,11 +437,11 @@ def projective_fundamental_group(self): Traceback (most recent call last): ... TypeError: the base field is not in QQbar - sage: A. = OrderedHyperplaneArrangements(QQ) - sage: H = A(); H - Empty hyperplane arrangement of dimension 1 - sage: H.projective_fundamental_group() - Finitely presented group < | > + sage: A. = OrderedHyperplaneArrangements(QQ) + sage: H = A(); H + Empty hyperplane arrangement of dimension 1 + sage: H.projective_fundamental_group() + Finitely presented group < | > """ K = self.base_ring() if not K.is_subring(QQbar): From 5b047bfa5f0ea781aa40c55423d9edd7e9815ba6 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 26 Mar 2024 18:25:03 +0100 Subject: [PATCH 89/95] check abstract_method --- .../schemes/curves/plane_curve_arrangement.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 840ef884eab..ffea361bd78 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -49,6 +49,7 @@ from itertools import combinations from sage.categories.sets_cat import Sets from sage.groups.free_group import FreeGroup +from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method from sage.misc.misc_c import prod from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -1099,6 +1100,7 @@ def change_ring(self, base_ring): """ return PlaneCurveArrangements(base_ring, names=self.variable_names()) + @abstract_method def ambient_space(self): """ Return the ambient space. @@ -1276,6 +1278,21 @@ class AffinePlaneCurveArrangements(PlaneCurveArrangements): """ Element = AffinePlaneCurveArrangementElement + def ambient_space(self): + """ + Return the ambient space. + + EXAMPLES:: + + sage: L. = AffinePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Affine Space of dimension 2 over Rational Field + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Projective Space of dimension 2 over Rational Field + """ + return AffineSpace(self.base_ring(), 2, self._names) + class ProjectivePlaneCurveArrangements(PlaneCurveArrangements): """ @@ -1295,3 +1312,19 @@ class ProjectivePlaneCurveArrangements(PlaneCurveArrangements): of dimension 2 over Rational Field """ Element = ProjectivePlaneCurveArrangementElement + + def ambient_space(self): + """ + Return the ambient space. + + EXAMPLES:: + + sage: L. = AffinePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Affine Space of dimension 2 over Rational Field + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Projective Space of dimension 2 over Rational Field + """ + return ProjectiveSpace(self.base_ring(), 2, self._names) + From 61a4f66948b60fce55a826ea2f6627b254afde53 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 26 Mar 2024 18:25:39 +0100 Subject: [PATCH 90/95] check abstract_method 2 --- src/sage/schemes/curves/plane_curve_arrangement.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index ffea361bd78..b75e6e61e30 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -1114,11 +1114,6 @@ def ambient_space(self): sage: L.ambient_space() Projective Space of dimension 2 over Rational Field """ - n = len(self._names) - if n == 2: - return AffineSpace(self.base_ring(), 2, self._names) - if n == 3: - return ProjectiveSpace(self.base_ring(), 2, self._names) def _repr_(self): """ From 5b6ba8c09b449828130e16d2b78c58b2b931dddd Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Wed, 27 Mar 2024 00:02:41 +0100 Subject: [PATCH 91/95] tests and one more abstract method: change_ring --- .../schemes/curves/plane_curve_arrangement.py | 118 ++++++++++++------ 1 file changed, 82 insertions(+), 36 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index b75e6e61e30..41cdc1ac215 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -85,11 +85,11 @@ def __init__(self, parent, curves, check=True): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: elt = H(x, y); elt Arrangement (x, y) in Affine Space of dimension 2 over Rational Field sage: TestSuite(elt).run() - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = ProjectivePlaneCurveArrangements(QQ) sage: elt = H(x, y); elt Arrangement (x, y) in Projective Space of dimension 2 over Rational Field sage: TestSuite(elt).run() @@ -119,7 +119,7 @@ def __getitem__(self, i): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: H(y^2 - x, y^3 + 2 * x^2, x^4 + y^4 + 1) Arrangement (y^2 - x, y^3 + 2*x^2, x^4 + y^4 + 1) in Affine Space of dimension 2 over Rational Field @@ -130,7 +130,7 @@ def __hash__(self): r""" TESTS:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: H((x * y, x + y +1)).__hash__() # random -4938643871296220686 """ @@ -146,7 +146,7 @@ def ncurves(self): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = ProjectivePlaneCurveArrangements(QQ) sage: h = H((x * y, x + y + z)) sage: h.ncurves() 2 @@ -167,7 +167,7 @@ def curves(self): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H((x * y, x + y + 1)) sage: h.curves() (Affine Plane Curve over Rational Field defined by x*y, @@ -190,13 +190,13 @@ def _repr_(self): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: h Arrangement of 5 curves in Affine Space of dimension 2 over Rational Field sage: H(()) Empty curve arrangement in Affine Space of dimension 2 over Rational Field - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = ProjectivePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) sage: h Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field @@ -218,7 +218,7 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: H(x) == H(y) False sage: H(x) == H(2 * x) @@ -246,7 +246,7 @@ def union(self, other): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: C = Curve(x^8 - y^8 -x^4 * y^4) sage: h1 = h.union(C); h1 @@ -286,7 +286,7 @@ def deletion(self, curves): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + 1, x^3 - y^5, x^2 * y^2 + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - 1)^2]) sage: C = h[-1] sage: h.deletion(C) @@ -324,7 +324,7 @@ def change_ring(self, base_ring): EXAMPLES:: sage: # needs sage.rings.number_field - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) sage: K. = CyclotomicField(3) sage: A.change_ring(K) @@ -346,11 +346,11 @@ def coordinate_ring(self): EXAMPLES:: - sage: L. = PlaneCurveArrangements(QQ) + sage: L. = AffinePlaneCurveArrangements(QQ) sage: C = L(x, y) sage: C.coordinate_ring() Multivariate Polynomial Ring in x, y over Rational Field - sage: P. = PlaneCurveArrangements(QQ) + sage: P. = ProjectivePlaneCurveArrangements(QQ) sage: C = P(x, y) sage: C.coordinate_ring() Multivariate Polynomial Ring in x, y, z over Rational Field @@ -363,7 +363,7 @@ def defining_polynomials(self): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 - x^3, x, y, y^2 + x * y + x^2) sage: A.defining_polynomials() (-x^3 + y^2, x, y, x^2 + x*y + y^2) @@ -376,7 +376,7 @@ def defining_polynomial(self, simplified=True): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y ** 2 + x ** 2, x, y) sage: prod(A.defining_polynomials()) == A.defining_polynomial() True @@ -413,7 +413,7 @@ def reduce(self, clean=False): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2, (x + y)^3 * (x^2 + x * y + y^2)) sage: A.reduce() Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Affine Space @@ -1010,7 +1010,7 @@ class PlaneCurveArrangements(UniqueRepresentation, Parent): EXAMPLES:: - sage: H. = PlaneCurveArrangements(QQ) + sage: H. = AffinePlaneCurveArrangements(QQ) sage: H(x, y^2, x-1, y-1) Arrangement (x, y^2, x - 1, y - 1) in Affine Space of dimension 2 over Rational Field @@ -1072,6 +1072,7 @@ def coordinate_ring(self): """ return PolynomialRing(self.base_ring(), self.variable_names()) + @abstract_method def change_ring(self, base_ring): """ Return curve arrangements over a different base ring. @@ -1082,23 +1083,16 @@ def change_ring(self, base_ring): OUTPUT: - A new :class:`AffinePlaneCurveArrangements` instance over the new + A new :class:`PlaneCurveArrangements` instance over the new base ring. EXAMPLES:: sage: L. = PlaneCurveArrangements(QQ) - sage: L.gen(0) - x - sage: L.change_ring(RR).base_ring() - Real Field with 53 bits of precision - - TESTS:: - - sage: L.change_ring(QQ) is L - True + Traceback (most recent call last): + ... + NotImplementedError: """ - return PlaneCurveArrangements(base_ring, names=self.variable_names()) @abstract_method def ambient_space(self): @@ -1107,12 +1101,10 @@ def ambient_space(self): EXAMPLES:: - sage: L. = AffinePlaneCurveArrangements(QQ) - sage: L.ambient_space() - Affine Space of dimension 2 over Rational Field - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.ambient_space() - Projective Space of dimension 2 over Rational Field + sage: L. = PlaneCurveArrangements(QQ) + Traceback (most recent call last): + ... + NotImplementedError: """ def _repr_(self): @@ -1210,7 +1202,6 @@ def ngens(self): """ return len(self.variable_names()) - @cached_method def gens(self): """ Return the coordinates. @@ -1288,6 +1279,34 @@ def ambient_space(self): """ return AffineSpace(self.base_ring(), 2, self._names) + def change_ring(self, base_ring): + """ + Return curve arrangements over a different base ring. + + INPUT: + + - ``base_ring`` -- a ring; the new base ring. + + OUTPUT: + + A new :class:`AffinePlaneCurveArrangements` instance over the new + base ring. + + EXAMPLES:: + + sage: L. = AffinePlaneCurveArrangements(QQ) + sage: L.gen(0) + x + sage: L.change_ring(RR).base_ring() + Real Field with 53 bits of precision + + TESTS:: + + sage: L.change_ring(QQ) is L + True + """ + return AffinePlaneCurveArrangements(base_ring, names=self.variable_names()) + class ProjectivePlaneCurveArrangements(PlaneCurveArrangements): """ @@ -1323,3 +1342,30 @@ def ambient_space(self): """ return ProjectiveSpace(self.base_ring(), 2, self._names) + def change_ring(self, base_ring): + """ + Return curve arrangements over a different base ring. + + INPUT: + + - ``base_ring`` -- a ring; the new base ring. + + OUTPUT: + + A new :class:`ProjectivePlaneCurveArrangements` instance over the new + base ring. + + EXAMPLES:: + + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.gen(0) + x + sage: L.change_ring(RR).base_ring() + Real Field with 53 bits of precision + + TESTS:: + + sage: L.change_ring(QQ) is L + True + """ + return ProjectivePlaneCurveArrangements(base_ring, names=self.variable_names()) From 816cf873a30d380aae3a2b46d4dd243e0471ab89 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Sun, 31 Mar 2024 23:54:53 +0200 Subject: [PATCH 92/95] reviewer changes --- .../hyperplane_arrangement/arrangement.py | 22 ++++---- .../ordered_arrangement.py | 42 ++++++++------- src/sage/schemes/curves/affine_curve.py | 3 +- .../schemes/curves/plane_curve_arrangement.py | 54 ++++++++----------- src/sage/schemes/curves/zariski_vankampen.py | 9 ++-- 5 files changed, 61 insertions(+), 69 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/arrangement.py b/src/sage/geometry/hyperplane_arrangement/arrangement.py index 545e28f9c77..19ae8698f8c 100644 --- a/src/sage/geometry/hyperplane_arrangement/arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/arrangement.py @@ -652,10 +652,6 @@ def union(self, other): A new hyperplane arrangement. - REMARK: - - It applies also to ordered arrangements. - EXAMPLES:: sage: H. = HyperplaneArrangements(QQ) @@ -1229,7 +1225,7 @@ def restriction(self, hyperplane, repetitions=False): - ``hyperplane`` -- a hyperplane of the hyperplane arrangement - - ``repetitions`` -- (boolean, default: ``False``) eliminate + - ``repetitions`` -- boolean (default: ``False``); eliminate repetitions for ordered arrangements OUTPUT: @@ -2210,7 +2206,6 @@ def poset_of_regions(self, B=None, numbered_labels=True): sage: A.poset_of_regions() Finite poset containing 24 elements - sage: # needs sage.combinat 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() @@ -2476,8 +2471,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: @@ -2727,8 +2722,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 @@ -2930,7 +2925,8 @@ def whitney_data(self): EXAMPLES:: - sage: A = hyperplane_arrangements.Shi(3) # needs sage.combinat + sage: # needs sage.combinat + sage: A = hyperplane_arrangements.Shi(3) sage: A.whitney_data() ( [ 1 -6 9] [ 1 6 6] @@ -3593,8 +3589,8 @@ def derivation_module_basis(self, algorithm="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) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index 353c5cdb6f8..ea6845b183a 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -71,7 +71,7 @@ - Enrique Artal (2023-12): initial version -This module add some features to the *unordered* one for some +This module adds some features to the *unordered* one for some properties which depend on the order. """ @@ -252,7 +252,7 @@ def affine_fundamental_group(self): OUTPUT: - A finitely presented fundamental group`. + A finitely presented fundamental group. .. NOTE:: @@ -324,8 +324,7 @@ def affine_fundamental_group(self): if n == 2: S = self.parent().ambient_space().symmetric_space() coord = vector((1,) + S.gens()) - Af = AffinePlaneCurveArrangements(K, - names=self.parent().variable_names()) + Af = AffinePlaneCurveArrangements(K, names=self.parent().variable_names()) L = Af([vector(line.coefficients()) * coord for line in self]) G = L.fundamental_group() self._affine_fundamental_group = G @@ -355,17 +354,24 @@ def affine_meridians(self): sage: A. = OrderedHyperplaneArrangements(QQ) sage: L = [y + x, y + x - 1] sage: H = A(L) - sage: H.affine_meridians() + sage: H._affine_meridians is None + True + sage: g = H.affine_fundamental_group() + sage: g + Finitely presented group < x0, x1 | > + sage: H._affine_meridians {0: [x0], 1: [x1], 2: [x1^-1*x0^-1]} - sage: L = [x, y, x + 1, y + 1, x - y] - sage: H = A(L) - sage: H.affine_meridians() - {0: [x4], 1: [x1], 2: [x3], 3: [x0], 4: [x2], 5: [x4^-1*x3^-1*x2^-1*x1^-1*x0^-1]} - sage: H = A(x, y, x + y) - sage: H.affine_meridians() - {0: [x2], 1: [x1], 2: [x0], 3: [x2^-1*x1^-1*x0^-1]} + sage: H.affine_meridians() == H._affine_meridians + True + sage: H1 = H.add_hyperplane(y - x) + sage: H1._affine_meridians is None + True + sage: dic = H1.affine_meridians(); dic + {0: [x0], 1: [x1], 2: [x2], 3: [x2^-1*x1^-1*x0^-1]} + sage: dic == H1._affine_meridians + True """ - if not self._affine_meridians: + if self._affine_meridians is None: self.affine_fundamental_group() return self._affine_meridians @@ -516,9 +522,9 @@ def projective_meridians(self): sage: H.projective_meridians() {0: [x2], 1: [x3], 2: [x0], 3: [x3^-1*x2^-1*x1^-1*x0^-1], 4: [x1]} """ - if not self._projective_meridians: + if self._projective_meridians is None: self.projective_fundamental_group() - return self._projective_meridians + return dict(self._projective_meridians) class OrderedHyperplaneArrangements(HyperplaneArrangements): @@ -610,12 +616,12 @@ def _element_constructor_(self, *args, **kwds): signed = kwds.pop('signed', not_char2) check = kwds.pop('check', True) backend = kwds.pop('backend', None) - if len(kwds) > 0: + if kwds: raise ValueError('unknown keyword argument') # process positional arguments AA = self.ambient_space() try: - hyperplanes = [AA(_) for _ in args] + hyperplanes = [AA(a) for a in args] except (TypeError, ValueError, AttributeError): if len(args) > 1: raise @@ -623,7 +629,7 @@ def _element_constructor_(self, *args, **kwds): if hasattr(arg, 'Hrepresentation'): hyperplanes = [AA(h) for h in arg.Hrepresentation()] else: - hyperplanes = [AA(_) for _ in arg] + hyperplanes = [AA(a) for a in arg] hyperplanes = [h.primitive(signed) for h in hyperplanes] if check: if signed and not not_char2: diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index 7d46b58823d..58bd9f986e3 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -456,10 +456,9 @@ def plot(self, *args, **kwds): A cuspidal curve:: - sage: # needs sage.plot sage: R. = QQ[] sage: C = Curve(x^3 - y^2) - sage: C.plot() + sage: C.plot() # needs sage.plot Graphics object consisting of 1 graphics primitive A 5-nodal curve of degree 11. This example also illustrates diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 41cdc1ac215..6583d220df9 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -58,10 +58,8 @@ from sage.schemes.affine.affine_space import AffineSpace from sage.schemes.curves.affine_curve import AffinePlaneCurve from sage.schemes.curves.constructor import Curve -from sage.schemes.curves.projective_curve import ProjectiveSpace -from sage.schemes.curves.projective_curve import ProjectivePlaneCurve -from sage.schemes.curves.zariski_vankampen import braid_monodromy -from sage.schemes.curves.zariski_vankampen import fundamental_group_arrangement +from sage.schemes.curves.projective_curve import ProjectiveSpace, ProjectivePlaneCurve +from sage.schemes.curves.zariski_vankampen import braid_monodromy, fundamental_group_arrangement from sage.structure.category_object import normalize_names from sage.structure.parent import Parent from sage.structure.element import Element @@ -195,14 +193,13 @@ def _repr_(self): sage: h Arrangement of 5 curves in Affine Space of dimension 2 over Rational Field sage: H(()) - Empty curve arrangement in Affine Space of dimension 2 over Rational Field + Arrangement () in Affine Space of dimension 2 over Rational Field sage: H. = ProjectivePlaneCurveArrangements(QQ) sage: h = H([x * y, x + y + z, x^3 * z^2 - y^5, x^2 * y^2 * z + x^5 + y^5, (x^2 + y^2)^3 + (x^3 + y^3 - z^3)^2]) sage: h Arrangement of 5 curves in Projective Space of dimension 2 over Rational Field - """ - if len(self) == 0: + if not self: return 'Empty curve arrangement in {0}'.format(self.parent().ambient_space()) elif len(self) < 5: curves = ', '.join(h.defining_polynomial()._repr_() @@ -252,18 +249,15 @@ def union(self, other): sage: h1 = h.union(C); h1 Arrangement of 6 curves in Affine Space of dimension 2 over Rational Field sage: h1 == h1.union(C) - Repeated curve True """ P = self.parent() other_h = P(other) curves0 = self._curves + other_h._curves - curves = () + curves = [] for h in curves0: if h not in curves: - curves += (h, ) - else: - print("Repeated curve") + curves.append(h) result = P(*curves) return result @@ -400,13 +394,13 @@ def have_common_factors(self): C = combinations(L, 2) return any(f1.gcd(f2).degree() > 0 for f1, f2 in C) - def reduce(self, clean=False): + def reduce(self, clean=False, verbose=False): r""" Replace the curves by their reduction. INPUT: - - ``clean`` -- boolean (default: False); if ``False`` + - ``clean`` -- boolean (default: ``False``); if ``False`` and there are common factors it returns ``None`` and a warning message. If ``True``, the common factors are kept only in the first occurance. @@ -419,7 +413,7 @@ def reduce(self, clean=False): Arrangement (y, x^3 + 2*x^2*y + 2*x*y^2 + y^3) in Affine Space of dimension 2 over Rational Field sage: C = H(x*y, x*(y + 1)) - sage: C.reduce() + sage: C.reduce(verbose=True) Some curves have common components sage: C.reduce(clean=True) Arrangement (x*y, y + 1) in Affine Space of dimension 2 @@ -429,14 +423,14 @@ def reduce(self, clean=False): Arrangement (x*y) in Affine Space of dimension 2 over Rational Field """ P = self.parent() - R = self.coordinate_ring() L = [self._curves[0].defining_polynomial().radical()] for c in self._curves[1:]: g = c.defining_polynomial().radical() for f in L: d = g.gcd(f) if d.degree() > 0 and not clean: - print("Some curves have common components") + if verbose: + print("Some curves have common components") return None g //= d if g.degree() > 0: @@ -645,13 +639,13 @@ def meridians(self, simplified=True, vertical=True): return computed self.fundamental_group(simplified=simplified, vertical=vertical) if simplified and vertical: - return self._meridians_simpl_vertical + return dict(self._meridians_simpl_vertical) elif simplified and not vertical: - return self._meridians_group_simpl_nonvertical + return dict(self._meridians_group_simpl_nonvertical) elif not simplified and vertical: - return self._meridians_nonsimpl_vertical + return dict(self._meridians_nonsimpl_vertical) else: - return self._meridians_nonsimpl_nonvertical + return dict(self._meridians_nonsimpl_nonvertical) def braid_monodromy(self, vertical=True): r""" @@ -765,7 +759,7 @@ def vertical_strands(self): self.braid_monodromy(vertical=True) return self._strands_vertical - def vertical_lines_in_braid_mon(self): + def vertical_lines_in_braid_monodromy(self): r""" Return the vertical lines in the arrangement. @@ -783,7 +777,7 @@ def vertical_lines_in_braid_mon(self): sage: # needs sirocco sage: H. = AffinePlaneCurveArrangements(QQ) sage: A = H(y^2 + x, y + x - 1, x) - sage: A.vertical_lines_in_braid_mon() + sage: A.vertical_lines_in_braid_monodromy() {1: 2} sage: A.braid_monodromy(vertical=True) [s1*s0*s1*s0^-1*s1^-1*s0, s0^-1*s1*s0*s1^-1*s0, s0^-1*s1^2*s0] @@ -797,7 +791,6 @@ class ProjectivePlaneCurveArrangementElement(PlaneCurveArrangementElement): """ An ordered projective plane curve arrangement. """ - def __init__(self, parent, curves, check=True): """ Construct an ordered projective plane curve arrangement. @@ -837,7 +830,7 @@ def fundamental_group(self, simplified=True): INPUT: - ``simplified`` -- boolean (default: True); set if the group - is simplified.. + is simplified OUTPUT: @@ -917,10 +910,10 @@ def fundamental_group(self, simplified=True): affine = AffinePlaneCurveArrangements(K, names=('u', 'v')) u, v = affine.gens() affines = [f.defining_polynomial().subs({x: u, y: v, z: 1}) for f in C] - changes = any([g.degree(v) < g.degree() > 1 for g in affines]) + changes = any(g.degree(v) < g.degree() > 1 for g in affines) while changes: affines = [f.subs({u: u + v}) for f in affines] - changes = any([g.degree(v) < g.degree() > 1 for g in affines]) + changes = any(g.degree(v) < g.degree() > 1 for g in affines) C_affine = affine(affines) proj = not (infinity_divides or infinity_in_C) G = C_affine.fundamental_group(simplified=simplified, vertical=True, @@ -989,9 +982,7 @@ def meridians(self, simplified=True): computed = self._meridians_nonsimpl if computed: return computed - if computed: - return computed - _ = self._fundamental_group(simplified=simplified) + self._fundamental_group(simplified=simplified) if simplified: return self._meridians_simpl else: @@ -1021,6 +1012,7 @@ class PlaneCurveArrangements(UniqueRepresentation, Parent): def __classcall__(cls, base, names=()): """ Normalize names + TESTS:: sage: H. = AffinePlaneCurveArrangements(QQ) @@ -1084,7 +1076,7 @@ def change_ring(self, base_ring): OUTPUT: A new :class:`PlaneCurveArrangements` instance over the new - base ring. + base ring EXAMPLES:: diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index f09c7dae74f..1c3e7e77170 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -1010,7 +1010,7 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}) -> list: # starting and ending at p if G.size() == E.size(): if E.is_cycle(): - j = list(dual_graph.vertices())[0][0] + j = next(dual_graph.vertex_iterator())[0] if j in vertical_regions: vd = {j: 0} else: @@ -1031,8 +1031,7 @@ def geometric_basis(G, E, EC0, p, dual_graph, vertical_regions={}) -> list: break if EC[-i] in Internal: EI = [v for v in E if v in - Internal.connected_component_containing_vertex(EC[-i], - sort=True) + Internal.connected_component_containing_vertex(EC[-i], sort=True) and v != EC[-i]] if EI: q = EC[-i] @@ -1609,7 +1608,7 @@ def fundamental_group_from_braid_mon(bm, degree=None, sage: g = fundamental_group_from_braid_mon(bm, projective=True); g # needs sirocco Finitely presented group < x1, x3 | x3^2*x1^2, x1^-1*x3^-1*x1*x3^-1*x1^-1*x3^-1 > - sage: print (g.order(), g.abelian_invariants()) # needs sirocco + sage: print(g.order(), g.abelian_invariants()) # needs sirocco 12 (4,) sage: B2 = BraidGroup(2) sage: bm = [B2(3 * [1])] @@ -1886,7 +1885,7 @@ def fundamental_group_arrangement(flist, simplified=True, projective=False, R = PolynomialRing(QQ, ('x', 'y')) f = R(1) x, y = R.gens() - flist1 = tuple(list(flist).copy()) + flist1 = tuple(flist) if vertical and vertical_lines_in_braidmon(flist1): infinity = all([Curve(g).is_vertical_line() or g.degree(y) == g.degree() for g in flist1]) From 7f28f0cced3b700fdc1e869320c5f86857dee068 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 9 Apr 2024 08:37:34 +0200 Subject: [PATCH 93/95] more reviewer comments --- .../ordered_arrangement.py | 14 +-- .../schemes/curves/plane_curve_arrangement.py | 101 ++++-------------- src/sage/schemes/curves/zariski_vankampen.py | 9 +- 3 files changed, 27 insertions(+), 97 deletions(-) diff --git a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py index ea6845b183a..bb16768e13b 100644 --- a/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py +++ b/src/sage/geometry/hyperplane_arrangement/ordered_arrangement.py @@ -354,26 +354,18 @@ def affine_meridians(self): sage: A. = OrderedHyperplaneArrangements(QQ) sage: L = [y + x, y + x - 1] sage: H = A(L) - sage: H._affine_meridians is None - True sage: g = H.affine_fundamental_group() sage: g Finitely presented group < x0, x1 | > - sage: H._affine_meridians + sage: H.affine_meridians() {0: [x0], 1: [x1], 2: [x1^-1*x0^-1]} - sage: H.affine_meridians() == H._affine_meridians - True sage: H1 = H.add_hyperplane(y - x) - sage: H1._affine_meridians is None - True - sage: dic = H1.affine_meridians(); dic + sage: H1.affine_meridians() {0: [x0], 1: [x1], 2: [x2], 3: [x2^-1*x1^-1*x0^-1]} - sage: dic == H1._affine_meridians - True """ if self._affine_meridians is None: self.affine_fundamental_group() - return self._affine_meridians + return dict(self._affine_meridians) def projective_fundamental_group(self): r""" diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index 6583d220df9..dcc57684b20 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -636,7 +636,7 @@ def meridians(self, simplified=True, vertical=True): else: computed = self._meridians_nonsimpl_nonvertical if computed: - return computed + return dict(computed) self.fundamental_group(simplified=simplified, vertical=vertical) if simplified and vertical: return dict(self._meridians_simpl_vertical) @@ -981,12 +981,12 @@ def meridians(self, simplified=True): else: computed = self._meridians_nonsimpl if computed: - return computed + return dict(computed) self._fundamental_group(simplified=simplified) if simplified: - return self._meridians_simpl + return dict(self._meridians_simpl) else: - return self._meridians_nonsimpl + return dict(self._meridians_nonsimpl) class PlaneCurveArrangements(UniqueRepresentation, Parent): @@ -1011,7 +1011,7 @@ class PlaneCurveArrangements(UniqueRepresentation, Parent): @staticmethod def __classcall__(cls, base, names=()): """ - Normalize names + Normalize the inputs to ensure a unique representation. TESTS:: @@ -1019,7 +1019,6 @@ def __classcall__(cls, base, names=()): sage: K = AffinePlaneCurveArrangements(QQ, names=('x', 'y')) sage: H is K True - sage: TestSuite(K).run() """ names = normalize_names(len(names), names) return super().__classcall__(cls, base, names) @@ -1032,12 +1031,6 @@ def __init__(self, base_ring, names=tuple()): sage: H. = AffinePlaneCurveArrangements(QQ) sage: TestSuite(H).run() - sage: H. = ProjectivePlaneCurveArrangements(QQ) - sage: K = ProjectivePlaneCurveArrangements(QQ, names=('x', 'y', 'z')) - sage: H is K - True - sage: type(K) - """ if base_ring not in _Fields: raise ValueError('base ring must be a field') @@ -1064,7 +1057,6 @@ def coordinate_ring(self): """ return PolynomialRing(self.base_ring(), self.variable_names()) - @abstract_method def change_ring(self, base_ring): """ Return curve arrangements over a different base ring. @@ -1080,11 +1072,16 @@ def change_ring(self, base_ring): EXAMPLES:: - sage: L. = PlaneCurveArrangements(QQ) - Traceback (most recent call last): - ... - NotImplementedError: - """ + sage: L. = AffinePlaneCurveArrangements(QQ) + sage: L.change_ring(RR).base_ring() + Real Field with 53 bits of precision + + TESTS:: + + sage: L.change_ring(QQ) is L + True + """ + return type(self)(base_ring, names=self.variable_names()) @abstract_method def ambient_space(self): @@ -1097,6 +1094,12 @@ def ambient_space(self): Traceback (most recent call last): ... NotImplementedError: + sage: L. = AffinePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Affine Space of dimension 2 over Rational Field + sage: L. = ProjectivePlaneCurveArrangements(QQ) + sage: L.ambient_space() + Projective Space of dimension 2 over Rational Field """ def _repr_(self): @@ -1265,40 +1268,9 @@ def ambient_space(self): sage: L. = AffinePlaneCurveArrangements(QQ) sage: L.ambient_space() Affine Space of dimension 2 over Rational Field - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.ambient_space() - Projective Space of dimension 2 over Rational Field """ return AffineSpace(self.base_ring(), 2, self._names) - def change_ring(self, base_ring): - """ - Return curve arrangements over a different base ring. - - INPUT: - - - ``base_ring`` -- a ring; the new base ring. - - OUTPUT: - - A new :class:`AffinePlaneCurveArrangements` instance over the new - base ring. - - EXAMPLES:: - - sage: L. = AffinePlaneCurveArrangements(QQ) - sage: L.gen(0) - x - sage: L.change_ring(RR).base_ring() - Real Field with 53 bits of precision - - TESTS:: - - sage: L.change_ring(QQ) is L - True - """ - return AffinePlaneCurveArrangements(base_ring, names=self.variable_names()) - class ProjectivePlaneCurveArrangements(PlaneCurveArrangements): """ @@ -1325,39 +1297,8 @@ def ambient_space(self): EXAMPLES:: - sage: L. = AffinePlaneCurveArrangements(QQ) - sage: L.ambient_space() - Affine Space of dimension 2 over Rational Field sage: L. = ProjectivePlaneCurveArrangements(QQ) sage: L.ambient_space() Projective Space of dimension 2 over Rational Field """ return ProjectiveSpace(self.base_ring(), 2, self._names) - - def change_ring(self, base_ring): - """ - Return curve arrangements over a different base ring. - - INPUT: - - - ``base_ring`` -- a ring; the new base ring. - - OUTPUT: - - A new :class:`ProjectivePlaneCurveArrangements` instance over the new - base ring. - - EXAMPLES:: - - sage: L. = ProjectivePlaneCurveArrangements(QQ) - sage: L.gen(0) - x - sage: L.change_ring(RR).base_ring() - Real Field with 53 bits of precision - - TESTS:: - - sage: L.change_ring(QQ) is L - True - """ - return ProjectivePlaneCurveArrangements(base_ring, names=self.variable_names()) diff --git a/src/sage/schemes/curves/zariski_vankampen.py b/src/sage/schemes/curves/zariski_vankampen.py index 1c3e7e77170..c6b5ca66017 100644 --- a/src/sage/schemes/curves/zariski_vankampen.py +++ b/src/sage/schemes/curves/zariski_vankampen.py @@ -566,19 +566,16 @@ def followstrand(f, factors, x0, x1, y0a, prec=53) -> list: ci = c.imag() coefsfactors += list(cr.endpoints()) coefsfactors += list(ci.endpoints()) - from sage.libs.sirocco import (contpath, contpath_mp, contpath_comps, - contpath_mp_comps) + from sage.libs.sirocco import (contpath, contpath_mp, contpath_comps, contpath_mp_comps) try: if prec == 53: if factors: - points = contpath_comps(deg, coefs, yr, yi, - degsfactors, coefsfactors) + points = contpath_comps(deg, coefs, yr, yi, degsfactors, coefsfactors) else: points = contpath(deg, coefs, yr, yi) else: if factors: - points = contpath_mp_comps(deg, coefs, yr, yi, prec, - degsfactors, coefsfactors) + points = contpath_mp_comps(deg, coefs, yr, yi, prec, degsfactors, coefsfactors) else: points = contpath_mp(deg, coefs, yr, yi, prec) return points From 03b54445fbd14af7b0603a9a8a316f72ebb85a01 Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Tue, 9 Apr 2024 09:43:28 +0200 Subject: [PATCH 94/95] change method change_ring to keep equal objects --- src/sage/schemes/curves/plane_curve_arrangement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/schemes/curves/plane_curve_arrangement.py b/src/sage/schemes/curves/plane_curve_arrangement.py index dcc57684b20..df4ba63f4e6 100644 --- a/src/sage/schemes/curves/plane_curve_arrangement.py +++ b/src/sage/schemes/curves/plane_curve_arrangement.py @@ -1080,8 +1080,8 @@ def change_ring(self, base_ring): sage: L.change_ring(QQ) is L True - """ - return type(self)(base_ring, names=self.variable_names()) + """ + return self.__reduce__()[1][0](base_ring, names=self.variable_names()) @abstract_method def ambient_space(self): From 0339378941598b8df3429739acb8f2d8ea430cef Mon Sep 17 00:00:00 2001 From: Enrique Artal Date: Mon, 15 Apr 2024 11:47:31 +0200 Subject: [PATCH 95/95] minimal intervention in all.py files --- src/sage/geometry/all.py | 2 -- src/sage/geometry/all__sagemath_polyhedra.py | 2 ++ .../geometry/hyperplane_arrangement/all.py | 29 ------------------- 3 files changed, 2 insertions(+), 31 deletions(-) diff --git a/src/sage/geometry/all.py b/src/sage/geometry/all.py index 0f7be0f1855..ebf0d36f808 100644 --- a/src/sage/geometry/all.py +++ b/src/sage/geometry/all.py @@ -10,5 +10,3 @@ from sage.geometry.all__sagemath_gap import * except ImportError: pass - -from sage.geometry.hyperplane_arrangement.all import * diff --git a/src/sage/geometry/all__sagemath_polyhedra.py b/src/sage/geometry/all__sagemath_polyhedra.py index 0b1852de7a5..5791273a319 100644 --- a/src/sage/geometry/all__sagemath_polyhedra.py +++ b/src/sage/geometry/all__sagemath_polyhedra.py @@ -16,5 +16,7 @@ lazy_import('sage.geometry.voronoi_diagram', 'VoronoiDiagram') lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'HyperplaneArrangements') +lazy_import('sage.geometry.hyperplane_arrangement.ordered_arrangement', + 'OrderedHyperplaneArrangements') lazy_import('sage.geometry.hyperplane_arrangement.library', 'hyperplane_arrangements') del lazy_import diff --git a/src/sage/geometry/hyperplane_arrangement/all.py b/src/sage/geometry/hyperplane_arrangement/all.py index bbc86480377..ca4c26e905d 100644 --- a/src/sage/geometry/hyperplane_arrangement/all.py +++ b/src/sage/geometry/hyperplane_arrangement/all.py @@ -1,30 +1 @@ # sage_setup: distribution = sagemath-polyhedra -""" -Hyperplane arrangements -""" - -# ***************************************************************************** -# -# Sage: Open Source Mathematical Software -# -# Copyright (C) 2005 William Stein -# -# Distributed under the terms of the GNU General Public License (GPL) -# -# This code is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -# General Public License for more details. -# -# The full text of the GPL is available at: -# -# http://www.gnu.org/licenses/ -# ***************************************************************************** - -from sage.misc.lazy_import import lazy_import - -lazy_import('sage.geometry.hyperplane_arrangement.arrangement', 'HyperplaneArrangements') - -lazy_import('sage.geometry.hyperplane_arrangement.ordered_arrangement', 'OrderedHyperplaneArrangements') - -lazy_import('sage.geometry.hyperplane_arrangement.library', 'hyperplane_arrangements')