diff --git a/src/sage/crypto/lattice.py b/src/sage/crypto/lattice.py index d24b87fe5d4..ce6c63f66f3 100644 --- a/src/sage/crypto/lattice.py +++ b/src/sage/crypto/lattice.py @@ -211,7 +211,7 @@ def gen_lattice(type='modular', n=4, m=8, q=11, seed=None, [-4 -3 2 -5 0 0 0 0 0 1] ] - sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) + sage: sage.crypto.gen_lattice(m=10, q=11, seed=42, lattice=True) # needs fpylll Free module of degree 10 and rank 10 over Integer Ring User basis matrix: [ 0 0 1 1 0 -1 -1 -1 1 0] diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index 4097d3512b9..dd48c55ee6b 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -311,6 +311,29 @@ def __init__(self): spkg='sagemath_groups', type='standard') +class sage__libs__braiding(PythonModule): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.braiding`. + + EXAMPLES:: + + sage: from sage.features.sagemath import sage__libs__braiding + sage: sage__libs__braiding().is_present() # needs sage.libs.braiding + FeatureTestResult('sage.libs.braiding', True) + """ + + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__libs__braiding + sage: isinstance(sage__libs__braiding(), sage__libs__braiding) + True + """ + PythonModule.__init__(self, 'sage.libs.braiding', + spkg='sagemath_libbraiding', type='standard') + + class sage__libs__ecl(PythonModule): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.libs.ecl`. @@ -330,7 +353,8 @@ def __init__(self): sage: isinstance(sage__libs__ecl(), sage__libs__ecl) True """ - PythonModule.__init__(self, 'sage.libs.ecl') + PythonModule.__init__(self, 'sage.libs.ecl', + spkg='sagemath_symbolics', type='standard') class sage__libs__flint(JoinFeature): @@ -1076,6 +1100,7 @@ def all_features(): sage__geometry__polyhedron(), sage__graphs(), sage__groups(), + sage__libs__braiding(), sage__libs__ecl(), sage__libs__flint(), sage__libs__gap(), diff --git a/src/sage/groups/abelian_gps/dual_abelian_group.py b/src/sage/groups/abelian_gps/dual_abelian_group.py index 85318aa6f83..c6012254630 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.number_field r""" Dual groups of Finite Multiplicative Abelian Groups @@ -25,7 +26,6 @@ sage: F = AbelianGroup(5, [2,5,7,8,9], names='abcde') sage: (a, b, c, d, e) = F.gens() - sage: # needs sage.rings.number_field sage: Fd = F.dual_group(names='ABCDE') sage: Fd.base_ring() Cyclotomic Field of order 2520 and degree 576 @@ -82,7 +82,6 @@ def is_DualAbelianGroup(x): EXAMPLES:: - sage: # needs sage.rings.number_field sage: from sage.groups.abelian_gps.dual_abelian_group import is_DualAbelianGroup sage: F = AbelianGroup(5,[3,5,7,8,9], names=list("abcde")) sage: Fd = F.dual_group() @@ -105,7 +104,7 @@ class DualAbelianGroup_class(UniqueRepresentation, AbelianGroupBase): EXAMPLES:: sage: F = AbelianGroup(5,[3,5,7,8,9], names="abcde") - sage: F.dual_group() # needs sage.rings.number_field + sage: F.dual_group() Dual of Abelian Group isomorphic to Z/3Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z over Cyclotomic Field of order 2520 and degree 576 @@ -123,7 +122,7 @@ def __init__(self, G, names, base_ring): EXAMPLES:: sage: F = AbelianGroup(5,[3,5,7,8,9], names="abcde") - sage: F.dual_group() # needs sage.rings.number_field + sage: F.dual_group() Dual of Abelian Group isomorphic to Z/3Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z over Cyclotomic Field of order 2520 and degree 576 """ @@ -180,9 +179,9 @@ def _repr_(self): EXAMPLES:: sage: F = AbelianGroup(5, [2,5,7,8,9], names='abcde') - sage: Fd = F.dual_group(names='ABCDE', # needs sage.rings.number_field + sage: Fd = F.dual_group(names='ABCDE', ....: base_ring=CyclotomicField(2*5*7*8*9)) - sage: Fd # indirect doctest # needs sage.rings.number_field + sage: Fd # indirect doctest Dual of Abelian Group isomorphic to Z/2Z x Z/5Z x Z/7Z x Z/8Z x Z/9Z over Cyclotomic Field of order 5040 and degree 1152 sage: Fd = F.dual_group(names='ABCDE', base_ring=CC) # needs sage.rings.real_mpfr @@ -209,8 +208,8 @@ def _latex_(self): EXAMPLES:: sage: F = AbelianGroup(3, [2]*3) - sage: Fd = F.dual_group() # needs sage.rings.number_field - sage: Fd._latex_() # needs sage.rings.number_field + sage: Fd = F.dual_group() + sage: Fd._latex_() '$\\mathrm{DualAbelianGroup}( AbelianGroup ( 3, (2, 2, 2) ) )$' """ return r"$\mathrm{DualAbelianGroup}( AbelianGroup ( %s, %s ) )$" % (self.ngens(), self.gens_orders()) @@ -251,7 +250,6 @@ def gen(self, i=0): EXAMPLES:: - sage: # needs sage.rings.number_field sage: F = AbelianGroup(3, [1,2,3], names='a') sage: Fd = F.dual_group(names="A") sage: Fd.0 @@ -279,8 +277,8 @@ def gens(self): EXAMPLES:: - sage: F = AbelianGroup([7,11]).dual_group() # needs sage.rings.number_field - sage: F.gens() # needs sage.rings.number_field + sage: F = AbelianGroup([7,11]).dual_group() + sage: F.gens() (X0, X1) """ n = self.group().ngens() @@ -293,8 +291,8 @@ def ngens(self): EXAMPLES:: sage: F = AbelianGroup([7]*100) - sage: Fd = F.dual_group() # needs sage.rings.number_field - sage: Fd.ngens() # needs sage.rings.number_field + sage: Fd = F.dual_group() + sage: Fd.ngens() 100 """ return self.group().ngens() @@ -310,8 +308,8 @@ def gens_orders(self): EXAMPLES:: sage: F = AbelianGroup([5]*1000) - sage: Fd = F.dual_group() # needs sage.rings.number_field - sage: invs = Fd.gens_orders(); len(invs) # needs sage.rings.number_field + sage: Fd = F.dual_group() + sage: invs = Fd.gens_orders(); len(invs) 1000 """ return self.group().gens_orders() @@ -325,8 +323,8 @@ def invariants(self): EXAMPLES:: sage: F = AbelianGroup([5]*1000) - sage: Fd = F.dual_group() # needs sage.rings.number_field - sage: invs = Fd.gens_orders(); len(invs) # needs sage.rings.number_field + sage: Fd = F.dual_group() + sage: invs = Fd.gens_orders(); len(invs) 1000 """ # TODO: deprecate @@ -340,9 +338,9 @@ def __contains__(self, X): sage: F = AbelianGroup(5,[2, 3, 5, 7, 8], names="abcde") sage: a,b,c,d,e = F.gens() - sage: Fd = F.dual_group(names="ABCDE") # needs sage.rings.number_field - sage: A,B,C,D,E = Fd.gens() # needs sage.rings.number_field - sage: A*B^2*D^7 in Fd # needs sage.rings.number_field + sage: Fd = F.dual_group(names="ABCDE") + sage: A,B,C,D,E = Fd.gens() + sage: A*B^2*D^7 in Fd True """ return X.parent() == self and is_DualAbelianGroupElement(X) @@ -354,8 +352,8 @@ def order(self): EXAMPLES:: sage: G = AbelianGroup([2,3,9]) - sage: Gd = G.dual_group() # needs sage.rings.number_field - sage: Gd.order() # needs sage.rings.number_field + sage: Gd = G.dual_group() + sage: Gd.order() 54 """ G = self.group() @@ -368,10 +366,10 @@ def is_commutative(self): EXAMPLES:: sage: G = AbelianGroup([2,3,9]) - sage: Gd = G.dual_group() # needs sage.rings.number_field - sage: Gd.is_commutative() # needs sage.rings.number_field + sage: Gd = G.dual_group() + sage: Gd.is_commutative() True - sage: Gd.is_abelian() # needs sage.rings.number_field + sage: Gd.is_abelian() True """ return True @@ -384,8 +382,8 @@ def list(self): EXAMPLES:: sage: G = AbelianGroup([2,3], names="ab") - sage: Gd = G.dual_group(names="AB") # needs sage.rings.number_field - sage: Gd.list() # needs sage.rings.number_field + sage: Gd = G.dual_group(names="AB") + sage: Gd.list() (1, B, B^2, A, A*B, A*B^2) """ if not self.is_finite(): @@ -400,8 +398,8 @@ def __iter__(self): EXAMPLES:: sage: G = AbelianGroup([2,3], names="ab") - sage: Gd = G.dual_group(names="AB") # needs sage.rings.number_field - sage: [X for X in Gd] # needs sage.rings.number_field + sage: Gd = G.dual_group(names="AB") + sage: [X for X in Gd] [1, B, B^2, A, A*B, A*B^2] sage: # needs sage.rings.real_mpfr diff --git a/src/sage/groups/abelian_gps/dual_abelian_group_element.py b/src/sage/groups/abelian_gps/dual_abelian_group_element.py index 407323d4f34..6fdb8a68c4e 100644 --- a/src/sage/groups/abelian_gps/dual_abelian_group_element.py +++ b/src/sage/groups/abelian_gps/dual_abelian_group_element.py @@ -1,3 +1,4 @@ +# sage.doctest: needs sage.rings.number_field """ Elements (characters) of the dual group of a finite Abelian group @@ -8,13 +9,12 @@ sage: F Multiplicative Abelian group isomorphic to C2 x C3 x C5 x C7 x C8 - sage: Fd = F.dual_group(names="ABCDE"); Fd # needs sage.rings.number_field + sage: Fd = F.dual_group(names="ABCDE"); Fd Dual of Abelian Group isomorphic to Z/2Z x Z/3Z x Z/5Z x Z/7Z x Z/8Z over Cyclotomic Field of order 840 and degree 192 The elements of the dual group can be evaluated on elements of the original group:: - sage: # needs sage.rings.number_field sage: a,b,c,d,e = F.gens() sage: A,B,C,D,E = Fd.gens() sage: A*B^2*D^7 @@ -71,10 +71,10 @@ def is_DualAbelianGroupElement(x) -> bool: EXAMPLES:: sage: from sage.groups.abelian_gps.dual_abelian_group import is_DualAbelianGroupElement - sage: F = AbelianGroup(5, [5,5,7,8,9], names=list("abcde")).dual_group() # needs sage.rings.number_field - sage: is_DualAbelianGroupElement(F) # needs sage.rings.number_field + sage: F = AbelianGroup(5, [5,5,7,8,9], names=list("abcde")).dual_group() + sage: is_DualAbelianGroupElement(F) False - sage: is_DualAbelianGroupElement(F.an_element()) # needs sage.rings.number_field + sage: is_DualAbelianGroupElement(F.an_element()) True """ return isinstance(x, DualAbelianGroupElement) @@ -96,7 +96,6 @@ def __call__(self, g): EXAMPLES:: - sage: # needs sage.rings.number_field sage: F = AbelianGroup(5, [2,3,5,7,8], names="abcde") sage: a,b,c,d,e = F.gens() sage: Fd = F.dual_group(names="ABCDE") @@ -147,7 +146,6 @@ def word_problem(self, words): EXAMPLES:: - sage: # needs sage.rings.number_field sage: G = AbelianGroup(5,[3, 5, 5, 7, 8], names="abcde") sage: Gd = G.dual_group(names="abcde") sage: a,b,c,d,e = Gd.gens() @@ -156,7 +154,7 @@ def word_problem(self, words): sage: w = a^7*b^3*c^5*d^4*e^4 sage: x = a^3*b^2*c^2*d^3*e^5 sage: y = a^2*b^4*c^2*d^4*e^5 - sage: e.word_problem([u,v,w,x,y]) + sage: e.word_problem([u,v,w,x,y]) # needs sage.libs.gap [[b^2*c^2*d^3*e^5, 245]] """ from sage.libs.gap.libgap import libgap diff --git a/src/sage/groups/affine_gps/affine_group.py b/src/sage/groups/affine_gps/affine_group.py index bfcdd282df1..7b5e4695fb1 100644 --- a/src/sage/groups/affine_gps/affine_group.py +++ b/src/sage/groups/affine_gps/affine_group.py @@ -204,7 +204,10 @@ def __init__(self, degree, ring): sage: G = AffineGroup(2, GF(5)); G Affine Group of degree 2 over Finite Field of size 5 + + sage: # needs sage.libs.gap (for gens) sage: TestSuite(G).run() + sage: G.category() Category of finite groups @@ -289,8 +292,10 @@ def cardinality(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: AffineGroup(6, GF(5)).cardinality() 172882428468750000000000000000 + sage: AffineGroup(6, ZZ).cardinality() +Infinity """ @@ -464,6 +469,7 @@ def random_element(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: G = AffineGroup(4, GF(3)) sage: G.random_element() # random [2 0 1 2] [1] @@ -498,6 +504,7 @@ def some_elements(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: G = AffineGroup(4,5) sage: G.some_elements() [ [2 0 0 0] [1] diff --git a/src/sage/groups/affine_gps/euclidean_group.py b/src/sage/groups/affine_gps/euclidean_group.py index 47de04c6544..59fb411b925 100644 --- a/src/sage/groups/affine_gps/euclidean_group.py +++ b/src/sage/groups/affine_gps/euclidean_group.py @@ -146,6 +146,8 @@ class EuclideanGroup(AffineGroup): True sage: G = EuclideanGroup(2, GF(5)); G Euclidean Group of degree 2 over Finite Field of size 5 + + sage: # needs sage.libs.gap (for gens) sage: TestSuite(G).run() REFERENCES: diff --git a/src/sage/groups/affine_gps/group_element.py b/src/sage/groups/affine_gps/group_element.py index 52fbe9365d6..7df4dc8a69a 100644 --- a/src/sage/groups/affine_gps/group_element.py +++ b/src/sage/groups/affine_gps/group_element.py @@ -78,11 +78,14 @@ class AffineGroupElement(MultiplicativeGroupElement): EXAMPLES:: sage: G = AffineGroup(2, GF(3)) + + sage: # needs sage.libs.gap sage: g = G.random_element() sage: type(g) sage: G(g.matrix()) == g True + sage: G(2) [2 0] [0] x |-> [0 2] x + [0] @@ -107,6 +110,7 @@ def __init__(self, parent, A, b=0, convert=True, check=True): TESTS:: + sage: # needs sage.libs.gap sage: G = AffineGroup(4, GF(5)) sage: g = G.random_element() sage: TestSuite(g).run() @@ -200,6 +204,7 @@ def matrix(self): Composition of affine group elements equals multiplication of the matrices:: + sage: # needs sage.libs.gap sage: g1 = G.random_element() sage: g2 = G.random_element() sage: g1.matrix() * g2.matrix() == (g1*g2).matrix() diff --git a/src/sage/groups/braid.py b/src/sage/groups/braid.py index 18d95ef86f2..bd787d3c153 100644 --- a/src/sage/groups/braid.py +++ b/src/sage/groups/braid.py @@ -72,7 +72,7 @@ from sage.combinat.permutation import Permutation from sage.combinat.permutation import Permutations from sage.combinat.subset import Subsets -from sage.features import PythonModule +from sage.features.sagemath import sage__libs__braiding from sage.groups.artin import FiniteTypeArtinGroup, FiniteTypeArtinGroupElement from sage.groups.finitely_presented import FinitelyPresentedGroup from sage.groups.finitely_presented import GroupMorphismWithGensImages @@ -80,7 +80,6 @@ from sage.functions.generalized import sign from sage.groups.perm_gps.permgroup_named import SymmetricGroup from sage.groups.perm_gps.permgroup_named import SymmetricGroupElement -from sage.knots.knot import Knot from sage.libs.gap.libgap import libgap from sage.matrix.constructor import identity_matrix, matrix from sage.misc.lazy_attribute import lazy_attribute @@ -98,7 +97,8 @@ ['leftnormalform', 'rightnormalform', 'centralizer', 'supersummitset', 'greatestcommondivisor', 'leastcommonmultiple', 'conjugatingbraid', 'ultrasummitset', 'thurston_type', 'rigidity', 'sliding_circuits'], - feature=PythonModule('sage.libs.braiding', spkg='libbraiding', type='standard')) + feature=sage__libs__braiding()) +lazy_import('sage.knots.knot', 'Knot') class Braid(FiniteTypeArtinGroupElement): diff --git a/src/sage/groups/galois_group.py b/src/sage/groups/galois_group.py index 5a48380eb21..a7439d18ab3 100644 --- a/src/sage/groups/galois_group.py +++ b/src/sage/groups/galois_group.py @@ -10,15 +10,17 @@ - David Roe (2019): initial version """ -from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic, PermutationGroup_subgroup from sage.groups.abelian_gps.abelian_group import AbelianGroup_class, AbelianGroup_subgroup -from sage.sets.finite_enumerated_set import FiniteEnumeratedSet -from sage.misc.lazy_attribute import lazy_attribute from sage.misc.abstract_method import abstract_method from sage.misc.cachefunc import cached_method -from sage.structure.category_object import normalize_names +from sage.misc.lazy_attribute import lazy_attribute +from sage.misc.lazy_import import lazy_import from sage.rings.integer_ring import ZZ +lazy_import('sage.groups.galois_group_perm', ['GaloisGroup_perm', 'GaloisSubgroup_perm']) +lazy_import('sage.groups.perm_gps.permgroup', 'PermutationGroup') + + def _alg_key(self, algorithm=None, recompute=False): r""" Return a key for use in cached_method calls. @@ -40,6 +42,7 @@ def _alg_key(self, algorithm=None, recompute=False): algorithm = self._get_algorithm(algorithm) return algorithm + class _GMixin: r""" This class provides some methods for Galois groups to be used for both permutation groups @@ -151,6 +154,7 @@ def _gc_map(self): """ return self._gcdata[1] + class _GaloisMixin(_GMixin): """ This class provides methods for Galois groups, allowing concrete instances @@ -275,6 +279,7 @@ def is_galois(self): """ return self.order() == self._field_degree + class _SubGaloisMixin(_GMixin): """ This class provides methods for subgroups of Galois groups, allowing concrete instances @@ -339,164 +344,6 @@ def _gcdata(self): """ return self._ambient_group._gcdata -class GaloisGroup_perm(_GaloisMixin, PermutationGroup_generic): - r""" - The group of automorphisms of a Galois closure of a given field. - - INPUT: - - - ``field`` -- a field, separable over its base - - - ``names`` -- a string or tuple of length 1, giving a variable name for the splitting field - - - ``gc_numbering`` -- boolean, whether to express permutations in terms of the - roots of the defining polynomial of the splitting field (versus the defining polynomial - of the original extension). The default value may vary based on the type of field. - """ - @abstract_method - def transitive_number(self, algorithm=None, recompute=False): - """ - The transitive number (as in the GAP and Magma databases of transitive groups) - for the action on the roots of the defining polynomial of the top field. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field - sage: G = K.galois_group() # needs sage.rings.number_field - sage: G.transitive_number() # needs sage.rings.number_field - 2 - """ - - @lazy_attribute - def _gens(self): - """ - The generators of this Galois group as permutations of the roots. It's important that this - be computed lazily, since it's often possible to compute other attributes (such as the order - or transitive number) more cheaply. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field - sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field - sage: G._gens # needs sage.rings.number_field - [(1,2,3,5), (1,4,3,2,5)] - """ - return NotImplemented - - def __init__(self, field, algorithm=None, names=None, gc_numbering=False): - r""" - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field - sage: G = K.galois_group() # needs sage.rings.number_field - sage: TestSuite(G).run() # needs sage.rings.number_field - """ - self._field = field - self._default_algorithm = algorithm - self._base = field.base_field() - self._gc_numbering = gc_numbering - if names is None: - # add a c for Galois closure - names = field.variable_name() + 'c' - self._gc_names = normalize_names(1, names) - # We do only the parts of the initialization of PermutationGroup_generic - # that don't depend on _gens - from sage.categories.permutation_groups import PermutationGroups - category = PermutationGroups().FinitelyGenerated().Finite() - # Note that we DON'T call the __init__ method for PermutationGroup_generic - # Instead, the relevant attributes are computed lazily - super(PermutationGroup_generic, self).__init__(category=category) - - @lazy_attribute - def _deg(self): - r""" - The number of moved points in the permutation representation. - - This will be the degree of the original number field if `_gc_numbering`` - is ``False``, or the degree of the Galois closure otherwise. - - EXAMPLES:: - - sage: # needs sage.rings.number_field - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) - sage: G = K.galois_group(gc_numbering=False); G - Galois group 5T3 (5:4) with order 20 of x^5 - 2 - sage: G._deg - 5 - sage: G = K.galois_group(gc_numbering=True); G._deg - 20 - """ - if self._gc_numbering: - return self.order() - else: - try: - return self._field.degree() - except NotImplementedError: # relative number fields don't support degree - return self._field.relative_degree() - - @lazy_attribute - def _domain(self): - r""" - The integers labeling the roots on which this Galois group acts. - - EXAMPLES:: - - sage: # needs sage.rings.number_field - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) - sage: G = K.galois_group(gc_numbering=False); G - Galois group 5T3 (5:4) with order 20 of x^5 - 2 - sage: G._domain - {1, 2, 3, 4, 5} - sage: G = K.galois_group(gc_numbering=True); G._domain - {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} - """ - return FiniteEnumeratedSet(range(1, self._deg+1)) - - @lazy_attribute - def _domain_to_gap(self): - r""" - Dictionary implementing the identity (used by PermutationGroup_generic). - - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field - sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field - sage: G._domain_to_gap[5] # needs sage.rings.number_field - 5 - """ - return {key: i+1 for i, key in enumerate(self._domain)} - - @lazy_attribute - def _domain_from_gap(self): - r""" - Dictionary implementing the identity (used by PermutationGroup_generic). - - EXAMPLES:: - - sage: R. = ZZ[] - sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field - sage: G = K.galois_group(gc_numbering=True) # needs sage.rings.number_field - sage: G._domain_from_gap[20] # needs sage.rings.number_field - 20 - """ - return {i+1: key for i, key in enumerate(self._domain)} - - def ngens(self): - r""" - Number of generators of this Galois group - - EXAMPLES:: - - sage: QuadraticField(-23, 'a').galois_group().ngens() # needs sage.rings.number_field - 1 - """ - return len(self._gens) class GaloisGroup_ab(_GaloisMixin, AbelianGroup_class): r""" @@ -551,7 +398,7 @@ def permutation_group(self): EXAMPLES:: - sage: GF(3^10).galois_group().permutation_group() # needs sage.rings.finite_rings + sage: GF(3^10).galois_group().permutation_group() # needs sage.libs.gap sage.rings.finite_rings Permutation Group with generators [(1,2,3,4,5,6,7,8,9,10)] """ return PermutationGroup(gap_group=self._gap_().RegularActionHomomorphism().Image()) @@ -568,11 +415,12 @@ def transitive_number(self, algorithm=None, recompute=False): sage: from sage.groups.galois_group import GaloisGroup_ab sage: Gtest = GaloisGroup_ab(field=None, generator_orders=(2,2,4)) - sage: Gtest.transitive_number() + sage: Gtest.transitive_number() # needs sage.libs.gap 2 """ return ZZ(self.permutation_group()._gap_().TransitiveIdentification()) + class GaloisGroup_cyc(GaloisGroup_ab): r""" Cyclic Galois groups @@ -614,17 +462,6 @@ def signature(self): """ return ZZ(1) if (self._field.degree() % 2) else ZZ(-1) -class GaloisSubgroup_perm(PermutationGroup_subgroup, _SubGaloisMixin): - """ - Subgroups of Galois groups (implemented as permutation groups), specified - by giving a list of generators. - - Unlike ambient Galois groups, where we use a lazy ``_gens`` attribute in order - to enable creation without determining a list of generators, - we require that generators for a subgroup be specified during initialization, - as specified in the ``__init__`` method of permutation subgroups. - """ - pass class GaloisSubgroup_ab(AbelianGroup_subgroup, _SubGaloisMixin): """ @@ -633,5 +470,4 @@ class GaloisSubgroup_ab(AbelianGroup_subgroup, _SubGaloisMixin): pass -GaloisGroup_perm.Subgroup = GaloisSubgroup_perm GaloisGroup_ab.Subgroup = GaloisSubgroup_ab diff --git a/src/sage/groups/galois_group_perm.py b/src/sage/groups/galois_group_perm.py new file mode 100644 index 00000000000..162e5174143 --- /dev/null +++ b/src/sage/groups/galois_group_perm.py @@ -0,0 +1,186 @@ +r""" +Galois groups of field extensions as permutation groups +""" + +from sage.groups.galois_group import _GaloisMixin, _SubGaloisMixin +from sage.groups.perm_gps.permgroup import PermutationGroup, PermutationGroup_generic, PermutationGroup_subgroup +from sage.misc.abstract_method import abstract_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.sets.finite_enumerated_set import FiniteEnumeratedSet +from sage.structure.category_object import normalize_names + + +class GaloisGroup_perm(_GaloisMixin, PermutationGroup_generic): + r""" + The group of automorphisms of a Galois closure of a given field. + + INPUT: + + - ``field`` -- a field, separable over its base + + - ``names`` -- a string or tuple of length 1, giving a variable name for the splitting field + + - ``gc_numbering`` -- boolean, whether to express permutations in terms of the + roots of the defining polynomial of the splitting field (versus the defining polynomial + of the original extension). The default value may vary based on the type of field. + """ + @abstract_method + def transitive_number(self, algorithm=None, recompute=False): + """ + The transitive number (as in the GAP and Magma databases of transitive groups) + for the action on the roots of the defining polynomial of the top field. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field + sage: G = K.galois_group() # needs sage.rings.number_field + sage: G.transitive_number() # needs sage.rings.number_field + 2 + """ + + @lazy_attribute + def _gens(self): + """ + The generators of this Galois group as permutations of the roots. It's important that this + be computed lazily, since it's often possible to compute other attributes (such as the order + or transitive number) more cheaply. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field + sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field + sage: G._gens # needs sage.rings.number_field + [(1,2,3,5), (1,4,3,2,5)] + """ + return NotImplemented + + def __init__(self, field, algorithm=None, names=None, gc_numbering=False): + r""" + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^3 + 2*x + 2) # needs sage.rings.number_field + sage: G = K.galois_group() # needs sage.rings.number_field + sage: TestSuite(G).run() # needs sage.rings.number_field + """ + self._field = field + self._default_algorithm = algorithm + self._base = field.base_field() + self._gc_numbering = gc_numbering + if names is None: + # add a c for Galois closure + names = field.variable_name() + 'c' + self._gc_names = normalize_names(1, names) + # We do only the parts of the initialization of PermutationGroup_generic + # that don't depend on _gens + from sage.categories.permutation_groups import PermutationGroups + category = PermutationGroups().FinitelyGenerated().Finite() + # Note that we DON'T call the __init__ method for PermutationGroup_generic + # Instead, the relevant attributes are computed lazily + super(PermutationGroup_generic, self).__init__(category=category) + + @lazy_attribute + def _deg(self): + r""" + The number of moved points in the permutation representation. + + This will be the degree of the original number field if `_gc_numbering`` + is ``False``, or the degree of the Galois closure otherwise. + + EXAMPLES:: + + sage: # needs sage.rings.number_field + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) + sage: G = K.galois_group(gc_numbering=False); G + Galois group 5T3 (5:4) with order 20 of x^5 - 2 + sage: G._deg + 5 + sage: G = K.galois_group(gc_numbering=True); G._deg + 20 + """ + if self._gc_numbering: + return self.order() + else: + try: + return self._field.degree() + except NotImplementedError: # relative number fields don't support degree + return self._field.relative_degree() + + @lazy_attribute + def _domain(self): + r""" + The integers labeling the roots on which this Galois group acts. + + EXAMPLES:: + + sage: # needs sage.rings.number_field + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) + sage: G = K.galois_group(gc_numbering=False); G + Galois group 5T3 (5:4) with order 20 of x^5 - 2 + sage: G._domain + {1, 2, 3, 4, 5} + sage: G = K.galois_group(gc_numbering=True); G._domain + {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20} + """ + return FiniteEnumeratedSet(range(1, self._deg+1)) + + @lazy_attribute + def _domain_to_gap(self): + r""" + Dictionary implementing the identity (used by PermutationGroup_generic). + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field + sage: G = K.galois_group(gc_numbering=False) # needs sage.rings.number_field + sage: G._domain_to_gap[5] # needs sage.rings.number_field + 5 + """ + return {key: i+1 for i, key in enumerate(self._domain)} + + @lazy_attribute + def _domain_from_gap(self): + r""" + Dictionary implementing the identity (used by PermutationGroup_generic). + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^5 - 2) # needs sage.rings.number_field + sage: G = K.galois_group(gc_numbering=True) # needs sage.rings.number_field + sage: G._domain_from_gap[20] # needs sage.rings.number_field + 20 + """ + return {i+1: key for i, key in enumerate(self._domain)} + + def ngens(self): + r""" + Number of generators of this Galois group + + EXAMPLES:: + + sage: QuadraticField(-23, 'a').galois_group().ngens() # needs sage.rings.number_field + 1 + """ + return len(self._gens) + + +class GaloisSubgroup_perm(PermutationGroup_subgroup, _SubGaloisMixin): + """ + Subgroups of Galois groups (implemented as permutation groups), specified + by giving a list of generators. + + Unlike ambient Galois groups, where we use a lazy ``_gens`` attribute in order + to enable creation without determining a list of generators, + we require that generators for a subgroup be specified during initialization, + as specified in the ``__init__`` method of permutation subgroups. + """ + pass + + +GaloisGroup_perm.Subgroup = GaloisSubgroup_perm diff --git a/src/sage/groups/generic.py b/src/sage/groups/generic.py index 69bffca1b9f..5e7f23af8d4 100644 --- a/src/sage/groups/generic.py +++ b/src/sage/groups/generic.py @@ -550,10 +550,10 @@ def discrete_log_rho(a, base, ord=None, operation='*', identity=None, inverse=No It also works with matrices:: - sage: A = matrix(GF(50021), [[10577, 23999, 28893], # needs sage.rings.finite_rings + sage: A = matrix(GF(50021), [[10577, 23999, 28893], # needs sage.modules sage.rings.finite_rings ....: [14601, 41019, 30188], ....: [3081, 736, 27092]]) - sage: discrete_log_rho(A^1234567, A) # needs sage.rings.finite_rings + sage: discrete_log_rho(A^1234567, A) # needs sage.modules sage.rings.finite_rings 1234567 Beware, the order must be prime:: @@ -1234,7 +1234,7 @@ def order_from_multiple(P, m, plist=None, factorization=None, check=True, sage: order_from_multiple(w, 230, operation='*') 23 - sage: # needs sage.rings.finite_rings + sage: # needs sage.modules sage.rings.finite_rings sage: F = GF(2^1279,'a') sage: n = F.cardinality() - 1 # Mersenne prime sage: order_from_multiple(F.random_element(), n, diff --git a/src/sage/groups/group.pyx b/src/sage/groups/group.pyx index cd2521e3478..b12da906938 100644 --- a/src/sage/groups/group.pyx +++ b/src/sage/groups/group.pyx @@ -35,9 +35,9 @@ def is_Group(x): EXAMPLES:: - sage: F. = FreeGroup() # needs sage.combinat + sage: F. = FreeGroup() # needs sage.groups sage: from sage.groups.group import is_Group - sage: is_Group(F) # needs sage.combinat + sage: is_Group(F) # needs sage.groups True sage: is_Group("a string") False @@ -140,7 +140,7 @@ cdef class Group(Parent): EXAMPLES:: - sage: SL(2, 7).is_commutative() # needs sage.modules sage.rings.finite_rings + sage: SL(2, 7).is_commutative() # needs sage.libs.gap sage.modules sage.rings.finite_rings False """ return self.is_abelian() @@ -212,8 +212,8 @@ cdef class Group(Parent): EXAMPLES:: - sage: G = AbelianGroup([2,3,4,5]) # needs sage.groups - sage: G.an_element() # needs sage.groups + sage: G = AbelianGroup([2,3,4,5]) # needs sage.modules + sage: G.an_element() # needs sage.modules f0*f1*f2*f3 """ return self.prod(self.gens()) diff --git a/src/sage/groups/matrix_gps/coxeter_group.py b/src/sage/groups/matrix_gps/coxeter_group.py index 6b865805298..d1c647f1286 100644 --- a/src/sage/groups/matrix_gps/coxeter_group.py +++ b/src/sage/groups/matrix_gps/coxeter_group.py @@ -79,7 +79,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene We can create Coxeter groups from Coxeter matrices:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: W = CoxeterGroup([[1, 6, 3], [6, 1, 10], [3, 10, 1]]); W Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [ 1 6 3] @@ -150,7 +150,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene graphs, we can input a Coxeter graph. Following the standard convention, edges with no label (i.e. labelled by ``None``) are treated as 3:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: G = Graph([(0,3,None), (1,3,15), (2,3,7), (0,1,3)]) sage: W = CoxeterGroup(G); W Coxeter group over Universal Cyclotomic Field with Coxeter matrix: @@ -165,7 +165,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene Because there currently is no class for `\ZZ \cup \{ \infty \}`, labels of `\infty` are given by `-1` in the Coxeter matrix:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: G = Graph([(0,1,None), (1,2,4), (0,2,oo)]) sage: W = CoxeterGroup(G) sage: W.coxeter_matrix() @@ -183,7 +183,7 @@ class CoxeterMatrixGroup(UniqueRepresentation, FinitelyGeneratedMatrixGroup_gene [2 3 1 3 3] [2 2 3 1 2] [2 2 3 2 1] - sage: W = CoxeterGroup(['H',3], implementation="reflection"); W # needs sage.rings.number_field + sage: W = CoxeterGroup(['H',3], implementation="reflection"); W # needs sage.libs.gap sage.rings.number_field Finite Coxeter group over Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790? with Coxeter matrix: @@ -240,15 +240,15 @@ def __init__(self, coxeter_matrix, base_ring, index_set): EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) - sage: TestSuite(W).run() # long time + sage: TestSuite(W).run() # long time - sage: # needs sage.rings.number_field + sage: # long time, needs sage.rings.number_field sage.symbolic sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) - sage: TestSuite(W).run() # long time + sage: TestSuite(W).run() sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) - sage: TestSuite(W).run(max_runs=30) # long time + sage: TestSuite(W).run(max_runs=30) sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) - sage: TestSuite(W).run(max_runs=30) # long time + sage: TestSuite(W).run(max_runs=30) We check that :trac:`16630` is fixed:: @@ -340,7 +340,7 @@ def _repr_(self): EXAMPLES:: - sage: CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]]) # needs sage.rings.number_field + sage: CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]]) # needs sage.libs.gap sage.rings.number_field Finite Coxeter group over Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095? with Coxeter matrix: [1 3 2] [3 1 4] @@ -383,8 +383,8 @@ def coxeter_matrix(self): sage: W.coxeter_matrix() [1 3] [3 1] - sage: W = CoxeterGroup(['H',3]) # needs sage.rings.number_field - sage: W.coxeter_matrix() + sage: W = CoxeterGroup(['H',3]) # needs sage.libs.gap sage.rings.number_field + sage: W.coxeter_matrix() # needs sage.libs.gap sage.rings.number_field [1 3 2] [3 1 5] [2 5 1] @@ -423,7 +423,7 @@ def is_finite(self): EXAMPLES:: - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: [l for l in range(2, 9) if ....: CoxeterGroup([[1,3,2],[3,1,l],[2,l,1]]).is_finite()] [2, 3, 4, 5] @@ -482,8 +482,8 @@ def order(self): sage: W = CoxeterGroup([[1,3],[3,1]]) sage: W.order() 6 - sage: W = CoxeterGroup([[1,-1],[-1,1]]) - sage: W.order() + sage: W = CoxeterGroup([[1,-1],[-1,1]]) # needs sage.libs.gap + sage: W.order() # needs sage.libs.gap +Infinity """ if self.is_finite(): @@ -593,7 +593,7 @@ def positive_roots(self): sage: W.positive_roots() ((1, 0, 0), (1, 1, 0), (0, 1, 0), (1, 1, 1), (0, 1, 1), (0, 0, 1)) - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: W = CoxeterGroup(['I',5], implementation='reflection') sage: W.positive_roots() ((1, 0), @@ -651,7 +651,7 @@ def roots(self): (0, -1, -1), (0, 0, -1)) - sage: # needs sage.rings.number_field + sage: # needs sage.libs.gap sage.rings.number_field sage: W = CoxeterGroup(['I',5], implementation='reflection') sage: len(W.roots()) 10 diff --git a/src/sage/groups/matrix_gps/finitely_generated.py b/src/sage/groups/matrix_gps/finitely_generated.py index c8098ad578a..ef6ba42a105 100644 --- a/src/sage/groups/matrix_gps/finitely_generated.py +++ b/src/sage/groups/matrix_gps/finitely_generated.py @@ -9,7 +9,7 @@ sage: F = GF(3) sage: gens = [matrix(F, 2, [1,0, -1,1]), matrix(F, 2, [1,1,0,1])] sage: G = MatrixGroup(gens) - sage: G.conjugacy_classes_representatives() + sage: G.conjugacy_classes_representatives() # needs sage.libs.gap ( [1 0] [0 2] [0 1] [2 0] [0 2] [0 1] [0 2] [0 1], [1 1], [2 1], [0 2], [1 2], [2 2], [1 0] @@ -161,6 +161,8 @@ def QuaternionMatrixGroupGF3(): is the product of `I` and `J`. :: sage: from sage.groups.matrix_gps.finitely_generated import QuaternionMatrixGroupGF3 + + sage: # needs sage.libs.gap sage: Q = QuaternionMatrixGroupGF3() sage: Q.order() 8 @@ -176,22 +178,23 @@ def QuaternionMatrixGroupGF3(): TESTS:: - sage: groups.matrix.QuaternionGF3() # needs sage.modules sage.rings.finite_rings + sage: groups.matrix.QuaternionGF3() Matrix group over Finite Field of size 3 with 2 generators ( [1 1] [2 1] [1 2], [1 1] ) + sage: # needs sage.groups sage: Q = QuaternionMatrixGroupGF3() sage: QP = Q.as_permutation_group() sage: QP.is_isomorphic(QuaternionGroup()) True - sage: H = DihedralGroup(4) # needs sage.groups - sage: H.order() # needs sage.groups + sage: H = DihedralGroup(4) + sage: H.order() 8 - sage: QP.is_abelian(), H.is_abelian() # needs sage.groups + sage: QP.is_abelian(), H.is_abelian() (False, False) - sage: QP.is_isomorphic(H) # needs sage.groups + sage: QP.is_isomorphic(H) False """ from sage.rings.finite_rings.finite_field_constructor import FiniteField @@ -340,6 +343,7 @@ class FinitelyGeneratedMatrixGroup_generic(MatrixGroup_generic): sage: MatrixGroup(m1, m2) == MatrixGroup(m2, m1) False + sage: # needs sage.libs.gap sage: G = GL(2, GF(3)) sage: H = G.as_matrix_group() sage: H == G, G == H @@ -415,6 +419,7 @@ def gen(self, i): EXAMPLES:: + sage: # needs sage.libs.gap sage: H = GL(2, GF(3)) sage: h1, h2 = H([[1,0], [2,1]]), H([[1,1], [0,1]]) sage: G = H.subgroup([h1, h2]) @@ -436,6 +441,7 @@ def ngens(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: H = GL(2, GF(3)) sage: h1, h2 = H([[1,0], [2,1]]), H([[1,1], [0,1]]) sage: G = H.subgroup([h1, h2]) diff --git a/src/sage/groups/matrix_gps/linear.py b/src/sage/groups/matrix_gps/linear.py index 65a8c13645c..822bd576afd 100644 --- a/src/sage/groups/matrix_gps/linear.py +++ b/src/sage/groups/matrix_gps/linear.py @@ -16,6 +16,8 @@ Special Linear Group of degree 2 over Integer Ring sage: G = SL(2, GF(3)); G Special Linear Group of degree 2 over Finite Field of size 3 + + sage: # needs sage.libs.gap sage: G.is_finite() True sage: G.conjugacy_classes_representatives() @@ -94,32 +96,38 @@ def GL(n, R, var='a'): EXAMPLES:: sage: G = GL(6, GF(5)) - sage: G.order() - 11064475422000000000000000 sage: G.base_ring() Finite Field of size 5 sage: G.category() Category of finite groups + + sage: # needs sage.libs.gap + sage: G.order() + 11064475422000000000000000 sage: TestSuite(G).run() sage: G = GL(6, QQ) sage: G.category() Category of infinite groups + + sage: # needs sage.libs.gap sage: TestSuite(G).run() Here is the Cayley graph of (relatively small) finite General Linear Group:: + sage: # needs sage.graphs sage.libs.gap sage: g = GL(2,3) - sage: d = g.cayley_graph(); d # needs sage.graphs + sage: d = g.cayley_graph(); d Digraph on 48 vertices - sage: d.plot(color_by_label=True, vertex_size=0.03, # long time # needs sage.graphs sage.plot + sage: d.plot(color_by_label=True, vertex_size=0.03, # long time # needs sage.plot ....: vertex_labels=False) Graphics object consisting of 144 graphics primitives - sage: d.plot3d(color_by_label=True) # long time # needs sage.graphs sage.plot + sage: d.plot3d(color_by_label=True) # long time # needs sage.plot Graphics3d Object :: + sage: # needs sage.libs.gap sage: F = GF(3); MS = MatrixSpace(F, 2, 2) sage: gens = [MS([[2,0], [0,1]]), MS([[2,1], [2,0]])] sage: G = MatrixGroup(gens) @@ -213,10 +221,13 @@ def SL(n, R, var='a'): Special Linear Group of degree 15 over Finite Field of size 7 sage: G.category() Category of finite groups + + sage: # needs sage.libs.gap sage: G.order() 1956712595698146962015219062429586341124018007182049478916067369638713066737882363393519966343657677430907011270206265834819092046250232049187967718149558134226774650845658791865745408000000 sage: len(G.gens()) 2 + sage: G = SL(2, ZZ); G Special Linear Group of degree 2 over Integer Ring sage: G.category() @@ -231,6 +242,8 @@ def SL(n, R, var='a'): sage: G = SL(3, ZZ); G Special Linear Group of degree 3 over Integer Ring + + sage: # needs sage.libs.gap sage: G.gens() ( [0 1 0] [ 0 1 0] [1 1 0] diff --git a/src/sage/groups/matrix_gps/matrix_group.py b/src/sage/groups/matrix_gps/matrix_group.py index c0c22456320..a9670ea67cc 100644 --- a/src/sage/groups/matrix_gps/matrix_group.py +++ b/src/sage/groups/matrix_gps/matrix_group.py @@ -5,10 +5,10 @@ Loading, saving, ... works:: + sage: # needs sage.libs.gap sage: G = GL(2,5); G General Linear Group of degree 2 over Finite Field of size 5 sage: TestSuite(G).run() - sage: g = G.1; g [4 1] [4 0] @@ -159,7 +159,7 @@ def as_matrix_group(self): EXAMPLES:: sage: G = SU(4, GF(5)) # needs sage.rings.finite_rings - sage: G.as_matrix_group() # needs sage.rings.finite_rings + sage: G.as_matrix_group() # needs sage.libs.gap sage.rings.finite_rings Matrix group over Finite Field in a of size 5^2 with 2 generators ( [ a 0 0 0] [ 1 0 4*a + 3 0] [ 0 2*a + 3 0 0] [ 1 0 0 0] @@ -167,7 +167,8 @@ def as_matrix_group(self): [ 0 0 0 3*a], [ 0 3*a + 1 0 0] ) - sage: G = GO(3,GF(5)) + sage: # needs sage.libs.gap + sage: G = GO(3, GF(5)) sage: G.as_matrix_group() Matrix group over Finite Field of size 5 with 2 generators ( [2 0 0] [0 1 0] @@ -316,8 +317,8 @@ def _repr_option(self, key): EXAMPLES:: - sage: SO3 = groups.matrix.SO(3, QQ) # needs sage.groups sage.modules - sage: SO3._repr_option('element_ascii_art') # needs sage.groups sage.modules + sage: SO3 = groups.matrix.SO(3, QQ) + sage: SO3._repr_option('element_ascii_art') True """ if key == 'element_ascii_art': @@ -479,6 +480,7 @@ def __richcmp__(self, other, op): EXAMPLES:: + sage: # needs sage.libs.gap sage: G = GL(2,3) sage: H = MatrixGroup(G.gens()) sage: H == G @@ -486,6 +488,7 @@ def __richcmp__(self, other, op): sage: G == H True + sage: # needs sage.libs.gap sage: MS = MatrixSpace(QQ, 2, 2) sage: G = MatrixGroup([MS(1), MS([1,2,3,4])]) sage: G == G diff --git a/src/sage/groups/matrix_gps/named_group.py b/src/sage/groups/matrix_gps/named_group.py index 4568c43d326..98841d9f0af 100644 --- a/src/sage/groups/matrix_gps/named_group.py +++ b/src/sage/groups/matrix_gps/named_group.py @@ -10,6 +10,8 @@ Special Linear Group of degree 2 over Integer Ring sage: G = SL(2, GF(3)); G Special Linear Group of degree 2 over Finite Field of size 3 + + sage: # needs sage.libs.gap sage: G.is_finite() True sage: G.conjugacy_classes_representatives() @@ -288,12 +290,13 @@ def __richcmp__(self, other, op): EXAMPLES:: + sage: # needs sage.libs.gap sage: G = GL(2,3) sage: G == MatrixGroup(G.gens()) True - sage: # needs sage.rings.finite_rings - sage: G = groups.matrix.GL(4,2) # needs sage.modules + sage: # needs sage.libs.gap sage.rings.finite_rings + sage: G = groups.matrix.GL(4,2) sage: H = MatrixGroup(G.gens()) sage: G == H True diff --git a/src/sage/groups/matrix_gps/orthogonal.py b/src/sage/groups/matrix_gps/orthogonal.py index 9e90d3ac0dc..8d622f7b14f 100644 --- a/src/sage/groups/matrix_gps/orthogonal.py +++ b/src/sage/groups/matrix_gps/orthogonal.py @@ -39,6 +39,8 @@ sage: G = SO(4, GF(7), 1); G Special Orthogonal Group of degree 4 and form parameter 1 over Finite Field of size 7 + + sage: # needs sage.libs.gap sage: G.random_element() # random [4 3 5 2] [6 6 4 0] @@ -154,7 +156,7 @@ def _OG(n, R, special, e=0, var='a', invariant_form=None): Check that :trac:`26028` is fixed:: - sage: GO(3,25).order() # indirect doctest # needs sage.rings.finite_rings + sage: GO(3,25).order() # indirect doctest # needs sage.libs.gap sage.rings.finite_rings 31200 Check that :trac:`28054` is fixed:: @@ -270,6 +272,8 @@ def GO(n, R, e=0, var='a', invariant_form=None): sage: GO(3, GF(7)) General Orthogonal Group of degree 3 over Finite Field of size 7 + + sage: # needs sage.libs.gap sage: GO(3, GF(7)).order() 672 sage: GO(3, GF(7)).gens() @@ -323,6 +327,7 @@ def GO(n, R, e=0, var='a', invariant_form=None): TESTS:: + sage: # needs sage.libs.gap sage: TestSuite(GO3).run() sage: groups.matrix.GO(2, 3, e=-1) General Orthogonal Group of degree 2 and form parameter -1 over Finite Field of size 3 @@ -378,6 +383,7 @@ def SO(n, R, e=None, var='a', invariant_form=None): sage: G = SO(3,GF(5)); G Special Orthogonal Group of degree 3 over Finite Field of size 5 + sage: # needs sage.libs.gap sage: G = SO(3,GF(5)) sage: G.gens() ( @@ -385,7 +391,6 @@ def SO(n, R, e=None, var='a', invariant_form=None): [0 3 0] [0 2 0] [4 0 0] [0 0 1], [0 3 1], [2 0 4] ) - sage: G = SO(3,GF(5)) sage: G.as_matrix_group() Matrix group over Finite Field of size 5 with 3 generators ( [2 0 0] [3 2 3] [1 4 4] @@ -483,6 +488,7 @@ def invariant_bilinear_form(self): EXAMPLES:: + sage: # needs sage.libs.gap sage: GO(2,3,+1).invariant_bilinear_form() [0 1] [1 0] @@ -503,7 +509,7 @@ def invariant_bilinear_form(self): TESTS:: - sage: GO3m.invariant_form() + sage: GO3m.invariant_form() # needs sage.libs.gap [1 0 0] [0 2 0] [0 0 3] diff --git a/src/sage/groups/matrix_gps/symplectic.py b/src/sage/groups/matrix_gps/symplectic.py index e0f3324b645..ec61d8a0634 100644 --- a/src/sage/groups/matrix_gps/symplectic.py +++ b/src/sage/groups/matrix_gps/symplectic.py @@ -5,6 +5,8 @@ sage: G = Sp(4, GF(7)); G Symplectic Group of degree 4 over Finite Field of size 7 + + sage: # needs sage.libs.gap sage: g = prod(G.gens()); g [3 0 3 0] [1 0 0 0] @@ -138,6 +140,7 @@ def Sp(n, R, var='a', invariant_form=None): sage: groups.matrix.Sp(2, 3) # needs sage.modules sage.rings.finite_rings Symplectic Group of degree 2 over Finite Field of size 3 + sage: # needs sage.libs.gap sage: G = Sp(4,5) sage: TestSuite(G).run() """ diff --git a/src/sage/groups/matrix_gps/unitary.py b/src/sage/groups/matrix_gps/unitary.py index ed6221cb61d..379d8276571 100644 --- a/src/sage/groups/matrix_gps/unitary.py +++ b/src/sage/groups/matrix_gps/unitary.py @@ -8,11 +8,11 @@ sage: # needs sage.rings.finite_rings sage: G = SU(3,5) - sage: G.order() + sage: G.order() # needs sage.libs.gap 378000 sage: G Special Unitary Group of degree 3 over Finite Field in a of size 5^2 - sage: G.gens() + sage: G.gens() # needs sage.libs.gap ( [ a 0 0] [4*a 4 1] [ 0 2*a + 2 0] [ 4 4 0] @@ -100,7 +100,7 @@ def _UG(n, R, special, var='a', invariant_form=None): TESTS:: - sage: GU(3,25).order() # indirect doctest # needs sage.rings.finite_rings + sage: GU(3,25).order() # indirect doctest # needs sage.libs.gap sage.rings.finite_rings 3961191000000 """ prefix = 'General' @@ -195,7 +195,7 @@ def GU(n, R, var='a', invariant_form=None): sage: G = GU(3, 7); G # needs sage.rings.finite_rings General Unitary Group of degree 3 over Finite Field in a of size 7^2 - sage: G.gens() # needs sage.rings.finite_rings + sage: G.gens() # needs sage.libs.gap sage.rings.finite_rings ( [ a 0 0] [6*a 6 1] [ 0 1 0] [ 6 6 0] @@ -207,7 +207,7 @@ def GU(n, R, var='a', invariant_form=None): sage: G = GU(3, 5, var='beta') # needs sage.rings.finite_rings sage: G.base_ring() # needs sage.rings.finite_rings Finite Field in beta of size 5^2 - sage: G.gens() # needs sage.rings.finite_rings + sage: G.gens() # needs sage.libs.gap sage.rings.finite_rings ( [ beta 0 0] [4*beta 4 1] [ 0 1 0] [ 4 4 0] diff --git a/src/sage/groups/pari_group.py b/src/sage/groups/pari_group.py index bbcb1c0a6c9..4d1ca0bff22 100644 --- a/src/sage/groups/pari_group.py +++ b/src/sage/groups/pari_group.py @@ -6,8 +6,10 @@ """ from sage.libs.pari import pari +from sage.misc.lazy_import import lazy_import from sage.rings.integer import Integer -from sage.groups.perm_gps.permgroup_named import TransitiveGroup + +lazy_import('sage.groups.perm_gps.permgroup_named', 'TransitiveGroup') class PariGroup(): diff --git a/src/sage/groups/perm_gps/permgroup.py b/src/sage/groups/perm_gps/permgroup.py index 8e2e6d82c10..71a404827b6 100644 --- a/src/sage/groups/perm_gps/permgroup.py +++ b/src/sage/groups/perm_gps/permgroup.py @@ -344,6 +344,7 @@ def PermutationGroup(gens=None, *args, **kwds): Note that we provide generators for the acting group. The permutation group we construct is its homomorphic image:: + sage: # needs sage.combinat sage: a = lambda g, x: vector(g*x, immutable=True) sage: X = [vector(x, immutable=True) for x in GF(3)^2] sage: G = SL(2,3); G.gens() @@ -1547,6 +1548,7 @@ def representative_action(self,x,y): TESTS:: + sage: # needs sage.graphs sage: g = graphs.PetersenGraph() sage: g.relabel(list("abcdefghik")) sage: g.vertices(sort=True) @@ -3706,6 +3708,7 @@ def has_regular_subgroup(self, return_group=False): But the automorphism group of Petersen's graph does not:: + sage: # needs sage.graphs sage: G = graphs.PetersenGraph().automorphism_group() sage: G.has_regular_subgroup() False @@ -3767,6 +3770,7 @@ def blocks_all(self, representatives=True): Picking an interesting group:: + sage: # needs sage.graphs sage: g = graphs.DodecahedralGraph() sage: g.is_vertex_transitive() True @@ -3776,12 +3780,12 @@ def blocks_all(self, representatives=True): Computing its blocks representatives:: - sage: ag.blocks_all() + sage: ag.blocks_all() # needs sage.graphs [[0, 15]] Now the full block:: - sage: sorted(ag.blocks_all(representatives = False)[0]) + sage: sorted(ag.blocks_all(representatives=False)[0]) # needs sage.graphs [[0, 15], [1, 16], [2, 12], [3, 13], [4, 9], [5, 10], [6, 11], [7, 18], [8, 17], [14, 19]] @@ -3977,6 +3981,7 @@ def minimal_generating_set(self): EXAMPLES:: + sage: # needs sage.graphs sage: g = graphs.CompleteGraph(4) sage: g.relabel(['a','b','c','d']) sage: mgs = g.automorphism_group().minimal_generating_set(); len(mgs) diff --git a/src/sage/knots/knotinfo.py b/src/sage/knots/knotinfo.py index a2f48a996ed..0ef601e124c 100644 --- a/src/sage/knots/knotinfo.py +++ b/src/sage/knots/knotinfo.py @@ -97,26 +97,26 @@ If you have `SnapPy `__ installed inside -Sage you can obtain an instance of :class:`~spherogram.links.links_base.Link`, +Sage, you can obtain an instance of :class:`~spherogram.links.links_base.Link`, too:: + sage: # optional - snappy sage: L6 = KnotInfo.L6a1_0 - sage: l6s = L6.link(snappy=True); l6s # optional - snappy + sage: l6s = L6.link(snappy=True); l6s Plink failed to import tkinter. - - sage: type(l6s) # optional - snappy + sage: type(l6s) sage: l6 = L6.link() - sage: l6 == l6s.sage_link() # optional - snappy + sage: l6 == l6s.sage_link() True - sage: L6.link(L6.items.name, snappy=True) # optional - snappy + sage: L6.link(L6.items.name, snappy=True) - sage: l6sn = _ # optional - snappy - sage: l6s == l6sn # optional - snappy + sage: l6sn = _ + sage: l6s == l6sn False - sage: l6m = l6.mirror_image() # optional - snappy - sage: l6sn.sage_link().is_isotopic(l6m) # optional - snappy + sage: l6m = l6.mirror_image() + sage: l6sn.sage_link().is_isotopic(l6m) True But observe that the name conversion to SnapPy does not distinguish orientation @@ -238,14 +238,16 @@ from enum import Enum from sage.misc.cachefunc import cached_method +from sage.misc.lazy_import import lazy_import from sage.misc.sage_eval import sage_eval from sage.structure.sage_object import SageObject from sage.structure.unique_representation import UniqueRepresentation from sage.rings.integer_ring import ZZ -from sage.groups.braid import BraidGroup from sage.knots.knot import Knots from sage.databases.knotinfo_db import KnotInfoColumns, db +lazy_import('sage.groups.braid', 'BraidGroup') + def eval_knotinfo(string, locals={}, to_tuple=True): r""" @@ -694,8 +696,7 @@ def braid_length(self): @cached_method def braid(self): r""" - Return the braid notation of self as an instance of :class:`~sage.groups.braid.Braid`. - + Return the braid notation of ``self`` as an instance of :class:`~sage.groups.braid.Braid`. EXAMPLES:: diff --git a/src/sage/libs/gap/libgap.pyx b/src/sage/libs/gap/libgap.pyx index 3803f32b191..63400adab4c 100644 --- a/src/sage/libs/gap/libgap.pyx +++ b/src/sage/libs/gap/libgap.pyx @@ -46,7 +46,7 @@ equivalent:: sage: type(_) - sage: libgap.eval('5/3 + 7*E(3)').sage() + sage: libgap.eval('5/3 + 7*E(3)').sage() # needs sage.rings.number_field 7*zeta3 + 5/3 sage: gens_of_group = libgap.AlternatingGroup(4).GeneratorsOfGroup() @@ -265,7 +265,7 @@ class Gap(Parent): sage: libgap.has_coerce_map_from(ZZ) True - sage: libgap.has_coerce_map_from(CyclotomicField(5)['x','y']) + sage: libgap.has_coerce_map_from(CyclotomicField(5)['x','y']) # needs sage.rings.number_field True """ return True @@ -362,11 +362,12 @@ class Gap(Parent): We gracefully handle the case that the conversion fails (:trac:`18039`):: - sage: F. = GF(9, modulus="first_lexicographic") - sage: libgap(Matrix(F, [[a]])) + sage: F. = GF(9, modulus="first_lexicographic") # needs sage.rings.finite_rings + sage: libgap(Matrix(F, [[a]])) # needs sage.rings.finite_rings Traceback (most recent call last): ... - NotImplementedError: conversion of (Givaro) finite field element to GAP not implemented except for fields defined by Conway polynomials. + NotImplementedError: conversion of (Givaro) finite field element to GAP + not implemented except for fields defined by Conway polynomials. """ ring = M.base_ring() try: diff --git a/src/sage/quadratic_forms/quadratic_form__automorphisms.py b/src/sage/quadratic_forms/quadratic_form__automorphisms.py index 89b2c079478..175f447bbea 100644 --- a/src/sage/quadratic_forms/quadratic_form__automorphisms.py +++ b/src/sage/quadratic_forms/quadratic_form__automorphisms.py @@ -349,7 +349,7 @@ def automorphisms(self): 48 sage: 2^3 * factorial(3) 48 - sage: len(Q.automorphisms()) + sage: len(Q.automorphisms()) # needs sage.libs.gap 48 :: @@ -357,14 +357,14 @@ def automorphisms(self): sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.number_of_automorphisms() 16 - sage: aut = Q.automorphisms() - sage: len(aut) + sage: aut = Q.automorphisms() # needs sage.libs.gap + sage: len(aut) # needs sage.libs.gap 16 - sage: all(Q(M) == Q for M in aut) + sage: all(Q(M) == Q for M in aut) # needs sage.libs.gap True sage: Q = QuadraticForm(ZZ, 3, [2, 1, 2, 2, 1, 3]) - sage: sorted(Q.automorphisms()) + sage: sorted(Q.automorphisms()) # needs sage.libs.gap [ [-1 0 0] [1 0 0] [ 0 -1 0] [0 1 0] diff --git a/src/sage/rings/number_field/galois_group.py b/src/sage/rings/number_field/galois_group.py index 2e6b876e541..9ea602dc1aa 100644 --- a/src/sage/rings/number_field/galois_group.py +++ b/src/sage/rings/number_field/galois_group.py @@ -10,9 +10,9 @@ """ from sage.structure.sage_object import SageObject -from sage.groups.galois_group import _alg_key, GaloisGroup_perm, GaloisSubgroup_perm +from sage.groups.galois_group import _alg_key +from sage.groups.galois_group_perm import GaloisGroup_perm, GaloisSubgroup_perm from sage.groups.perm_gps.permgroup import standardize_generator - from sage.groups.perm_gps.permgroup_element import PermutationGroupElement from sage.misc.superseded import deprecation from sage.misc.cachefunc import cached_method diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index e34606e085b..f9e7ac365b0 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -371,18 +371,20 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, One can embed into any other field:: - sage: K. = NumberField(x^3-2, embedding=CC.gen()-0.6) + sage: K. = NumberField(x^3 - 2, embedding=CC.gen() - 0.6) sage: CC(a) -0.629960524947436 + 1.09112363597172*I - sage: L = Qp(5) # needs sage.rings.padics + + sage: # needs sage.rings.padics + sage: L = Qp(5) sage: f = polygen(L)^3 - 2 - sage: K. = NumberField(x^3-2, embedding=f.roots()[0][0]) + sage: K. = NumberField(x^3 - 2, embedding=f.roots()[0][0]) sage: a + L(1) 4 + 2*5^2 + 2*5^3 + 3*5^4 + 5^5 + 4*5^6 + 2*5^8 + 3*5^9 + 4*5^12 + 4*5^14 + 4*5^15 + 3*5^16 + 5^17 + 5^18 + 2*5^19 + O(5^20) - sage: L. = NumberField(x^6-x^2+1/10, embedding=1) - sage: K. = NumberField(x^3-x+1/10, embedding=b^2) - sage: a+b + sage: L. = NumberField(x^6 - x^2 + 1/10, embedding=1) + sage: K. = NumberField(x^3 - x + 1/10, embedding=b^2) + sage: a + b b^2 + b sage: CC(a) == CC(b)^2 True @@ -409,7 +411,7 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, Note that the codomain of the embedding must be ``QQbar`` or ``AA`` for this to work (see :trac:`20184`):: - sage: N. = NumberField(x^3 + 2,embedding=1) + sage: N. = NumberField(x^3 + 2, embedding=1) sage: 1 < g False sage: g > 1 @@ -493,7 +495,7 @@ def NumberField(polynomial, name=None, check=True, names=None, embedding=None, The following has been fixed in :trac:`8800`:: sage: P. = QQ[] - sage: K. = NumberField(x^3 - 5,embedding=0) + sage: K. = NumberField(x^3 - 5, embedding=0) sage: L. = K.extension(x^2 + a) sage: F, R = L.construction() sage: F(R) == L # indirect doctest @@ -1561,7 +1563,7 @@ def construction(self): :: sage: P. = QQ[] - sage: K. = NumberField(x^3-5,embedding=0) + sage: K. = NumberField(x^3-5, embedding=0) sage: L. = K.extension(x^2+a) sage: a*b a*b @@ -8224,7 +8226,7 @@ def _coerce_from_other_number_field(self, x): The following was fixed in :trac:`8800`:: sage: P. = QQ[] - sage: K. = NumberField(x^3 - 5,embedding=0) + sage: K. = NumberField(x^3 - 5, embedding=0) sage: L. = K.extension(x^2 + a) sage: F,R = L.construction() sage: F(R) == L #indirect doctest diff --git a/src/sage/rings/number_field/number_field_ideal.py b/src/sage/rings/number_field/number_field_ideal.py index 19c9e95ed39..28105221ac7 100644 --- a/src/sage/rings/number_field/number_field_ideal.py +++ b/src/sage/rings/number_field/number_field_ideal.py @@ -1002,6 +1002,7 @@ def is_maximal(self): EXAMPLES:: + sage: x = polygen(ZZ) sage: K. = NumberField(x^3 + 3); K Number Field in a with defining polynomial x^3 + 3 sage: K.ideal(5).is_maximal() @@ -1017,6 +1018,7 @@ def is_prime(self): EXAMPLES:: + sage: x = polygen(ZZ) sage: K. = NumberField(x^2 - 17); K Number Field in a with defining polynomial x^2 - 17 sage: K.ideal(5).is_prime() # inert prime @@ -1031,7 +1033,7 @@ def is_prime(self): Check that we do not factor the norm of the ideal, this used to take half an hour, see :trac:`33360`:: - sage: K. = NumberField([x^2-2,x^2-3,x^2-5]) + sage: K. = NumberField([x^2 - 2, x^2 - 3, x^2 - 5]) sage: t = (((-2611940*c + 1925290/7653)*b - 1537130/7653*c ....: + 10130950)*a + (1343014/7653*c - 8349770)*b ....: + 6477058*c - 2801449990/4002519) @@ -1112,6 +1114,7 @@ def _cache_bnfisprincipal(self, proof=None, gens=False): Check that no warnings are triggered from PARI/GP (see :trac:`30801`):: + sage: x = polygen(ZZ) sage: K. = NumberField(x^2 - x + 112941801) sage: I = K.ideal((112941823, a + 49942513)) sage: I.is_principal() @@ -1494,7 +1497,7 @@ def decomposition_group(self): EXAMPLES:: - sage: QuadraticField(-23, 'w').primes_above(7)[0].decomposition_group() + sage: QuadraticField(-23, 'w').primes_above(7)[0].decomposition_group() # needs sage.groups Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23) """ return self.number_field().galois_group().decomposition_group(self) @@ -1510,9 +1513,9 @@ def ramification_group(self, v): EXAMPLES:: - sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(0) + sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(0) # needs sage.groups Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23) - sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(1) + sage: QuadraticField(-23, 'w').primes_above(23)[0].ramification_group(1) # needs sage.groups Subgroup generated by [()] of (Galois group 2T1 (S2) with order 2 of x^2 + 23) """ @@ -1528,7 +1531,7 @@ def inertia_group(self): EXAMPLES:: - sage: QuadraticField(-23, 'w').primes_above(23)[0].inertia_group() + sage: QuadraticField(-23, 'w').primes_above(23)[0].inertia_group() # needs sage.groups Subgroup generated by [(1,2)] of (Galois group 2T1 (S2) with order 2 of x^2 + 23) """ return self.ramification_group(0) @@ -1594,7 +1597,7 @@ def artin_symbol(self): EXAMPLES:: - sage: QuadraticField(-23, 'w').primes_above(7)[0].artin_symbol() + sage: QuadraticField(-23, 'w').primes_above(7)[0].artin_symbol() # needs sage.groups (1,2) """ return self.number_field().galois_group().artin_symbol(self) diff --git a/src/sage/rings/number_field/totallyreal_rel.py b/src/sage/rings/number_field/totallyreal_rel.py index af44569f167..47ee18456f7 100644 --- a/src/sage/rings/number_field/totallyreal_rel.py +++ b/src/sage/rings/number_field/totallyreal_rel.py @@ -51,7 +51,7 @@ sage: [ f[0] for f in ls ] [725, 1125, 1600, 2000, 2225, 2525, 3600, 4225, 4400, 4525, 5125, 5225, 5725, 6125, 7225, 7600, 7625, 8000, 8525, 8725, 9225] - sage: [NumberField(ZZx(x[1]), 't').is_galois() for x in ls] + sage: [NumberField(ZZx(x[1]), 't').is_galois() for x in ls] # needs sage.groups [False, True, True, True, False, False, True, True, False, False, False, False, False, True, True, False, False, True, False, False, False] Eight out of 21 such fields are Galois (with Galois group `C_4`