def __init__(self, homset, imgs, check=True): r""" Constructor method. TESTS: The following input does not define a valid group homomorphism:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: A = AbelianGroupGap([2]) sage: G = MatrixGroup([Matrix(ZZ, 2, [0,1,-1,0])]) sage: A.hom([G.gen(0)]) Traceback (most recent call last): ... ValueError: images do not define a group homomorphism """ from sage.libs.gap.libgap import libgap Morphism.__init__(self, homset) dom = homset.domain() codom = homset.codomain() gens = [x.gap() for x in dom.gens()] imgs = [codom(x).gap() for x in imgs] if check: if not len(gens) == len(imgs): raise ValueError("provide an image for each generator") self._phi = libgap.GroupHomomorphismByImages( dom.gap(), codom.gap(), gens, imgs) # if it is not a group homomorphism, then # self._phi is the gap boolean fail if self._phi.is_bool(): # check we did not fail raise ValueError("images do not define a group homomorphism") else: ByImagesNC = libgap.function_factory("GroupHomomorphismByImagesNC") self._phi = ByImagesNC(dom.gap(), codom.gap(), gens, imgs)
def orbits_lines_mod_p(self, p): r""" Let `(L, q)` be a lattice. This returns representatives of the orbits of lines in `L/pL` under the orthogonal group of `q`. INPUT: - ``p`` -- a prime number OUTPUT: - a list of vectors over ``GF(p)`` EXAMPLES:: sage: from sage.quadratic_forms.quadratic_form__neighbors import orbits_lines_mod_p sage: Q = QuadraticForm(ZZ, 3, [1, 0, 0, 2, 1, 3]) sage: Q.orbits_lines_mod_p(2) [(0, 0, 1), (0, 1, 0), (0, 1, 1), (1, 0, 0), (1, 0, 1), (1, 1, 0), (1, 1, 1)] """ from sage.libs.gap.libgap import libgap # careful the self.automorphism_group() acts from the left # but in gap we act from the right!! --> transpose gens = self.automorphism_group().gens() gens = [g.matrix().transpose().change_ring(GF(p)) for g in gens] orbs = libgap.function_factory( """function(gens, p) local one, G, reps, V, n, orb; one:= One(GF(p)); G:=Group(List(gens, g -> g*one)); n:= Size(gens[1]); V:= GF(p)^n; orb:= OrbitsDomain(G, V, OnLines); reps:= List(orb, g->g[1]); return reps; end;""") # run this at startup if you need more memory... #from sage.interfaces.gap import get_gap_memory_pool_size, set_gap_memory_pool_size #memory_gap = get_gap_memory_pool_size() #set_gap_memory_pool_size(1028*memory_gap) orbs_reps = orbs(gens, p) #set_gap_memory_pool_size(memory_gap) M = GF(p)**self.dim() return [M(m.sage()) for m in orbs_reps if not m.IsZero()]
def cardinality(self): """ Return the size of combinations(mset, k). IMPLEMENTATION: Wraps GAP's NrCombinations. EXAMPLES:: sage: mset = [1,1,2,3,4,4,5] sage: Combinations(mset,2).cardinality() 12 """ from sage.libs.gap.libgap import libgap items = [self.mset.index(_) for _ in self.mset] nc = libgap.function_factory('NrCombinations') return ZZ(nc(items, ZZ(self.k)))
def module_composition_factors(self, algorithm=None): r""" Return a list of triples consisting of [base field, dimension, irreducibility], for each of the Meataxe composition factors modules. The ``algorithm="verbose"`` option returns more information, but in Meataxe notation. EXAMPLES:: sage: F = GF(3); MS = MatrixSpace(F,4,4) sage: M = MS(0) sage: M[0,1]=1;M[1,2]=1;M[2,3]=1;M[3,0]=1 sage: G = MatrixGroup([M]) sage: G.module_composition_factors() [(Finite Field of size 3, 1, True), (Finite Field of size 3, 1, True), (Finite Field of size 3, 2, True)] sage: F = GF(7); MS = MatrixSpace(F,2,2) sage: gens = [MS([[0,1],[-1,0]]),MS([[1,1],[2,3]])] sage: G = MatrixGroup(gens) sage: G.module_composition_factors() [(Finite Field of size 7, 2, True)] Type ``G.module_composition_factors(algorithm='verbose')`` to get a more verbose version. For more on MeatAxe notation, see https://www.gap-system.org/Manuals/doc/ref/chap69.html """ from sage.libs.gap.libgap import libgap F = self.base_ring() if not F.is_finite(): raise NotImplementedError("Base ring must be finite.") n = self.degree() MS = MatrixSpace(F, n, n) mats = [MS(g.matrix()) for g in self.gens()] # initializing list of mats by which the gens act on self mats_gap = libgap(mats) M = mats_gap.GModuleByMats(F) compo = libgap.function_factory('MTX.CompositionFactors') MCFs = compo(M) if algorithm == "verbose": print(str(MCFs) + "\n") return sorted((MCF['field'].sage(), MCF['dimension'].sage(), MCF['IsIrreducible'].sage()) for MCF in MCFs)
def _element_constructor_(self, x, check=True, **options): r""" Handle conversions and coercions. EXAMPLES:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: A = AbelianGroupGap([2,4]) sage: H = A.Hom(A) sage: H([g^2 for g in A.gens()]) Group endomorphism of Abelian group with gap, generator orders (2, 4) sage: H(2) Traceback (most recent call last): ... TypeError: unable to convert 2 to an element of ... A group homomorphism between a finitely presented group and a subgroup of a permutation group:: sage: PG = PGU(6,2) sage: g, h = PG.gens() sage: p1 = h^-3*(h^-1*g^-1)^2*h*g*h^2*g^-1*h^2*g*h^-5*g^-1 sage: p2 = g*(g*h)^2*g*h^-4*(g*h)^2*(h^2*g*h^-2*g)^2*h^-2*g*h^-2*g^-1*h^-1*g*h*g*h^-1*g sage: p3 = h^-3*g^-1*h*g*h^4*g^-1*h^-1*g*h*(h^2*g^-1)^2*h^-4*g*h^2*g^-1*h^-7*g^-2*h^-2*g*h^-2*g^-1*h^-1*(g*h)^2*h^3 sage: p4 = h*(h^3*g)^2*h*g*h^-1*g*h^2*g^-1*h^-2*g*h^4*g^-1*h^3*g*h^-2*g*h^-1*g^-1*h^2*g*h*g^-1*h^-2*g*h*g^-1*h^2*g*h^2*g^-1 sage: p5 = h^2*g*h^2*g^-1*h*g*h^-1*g*h*g^-1*h^2*g*h^-2*g*h^2*g*h^-2*(h^-1*g)^2*h^4*(g*h^-1)^2*g^-1 sage: UPG = PG.subgroup([p1, p2, p3, p4, p5], canonicalize=False) sage: B6 = BraidGroup(6) sage: reprB6 = B6.hom(UPG.gens()) sage: b1, b2, b3, b4, b5 = B6.gens() sage: reprB6(b1*b2*b3*b4*b5) == p1*p2*p3*p4*p5 True TESTS: The following input does not define a valid group homomorphism:: sage: from sage.groups.abelian_gps.abelian_group_gap import AbelianGroupGap sage: A = AbelianGroupGap([2]) sage: G = MatrixGroup([Matrix(ZZ, 2, [0,1,-1,0])]) sage: A.hom([G.gen(0)]) Traceback (most recent call last): ... ValueError: images do not define a group homomorphism """ if isinstance(x, (tuple, list)): # there should be a better way from sage.libs.gap.libgap import libgap dom = self.domain() codom = self.codomain() gens = dom.gap().GeneratorsOfGroup() imgs = [codom(g).gap() for g in x] if check: if not len(gens) == len(imgs): raise ValueError("provide an image for each generator") phi = libgap.GroupHomomorphismByImages(dom.gap(), codom.gap(), gens, imgs) # if it is not a group homomorphism, then # self._phi is the gap boolean fail if phi.is_bool(): # check we did not fail raise ValueError("images do not define a group homomorphism") else: ByImagesNC = libgap.function_factory("GroupHomomorphismByImagesNC") phi = ByImagesNC(dom.gap(), codom.gap(), gens, imgs) return self.element_class(self, phi, check=check, **options) if isinstance(x, GapElement): try: return self.element_class(self, x, check=True, **options) except ValueError: pass return super(GroupHomset_libgap, self)._element_constructor_(x, check=check, **options)
def CossidentePenttilaGraph(q): r""" Cossidente-Penttila `((q^3+1)(q+1)/2,(q^2+1)(q-1)/2,(q-3)/2,(q-1)^2/2)`-strongly regular graph For each odd prime power `q`, one can partition the points of the `O_6^-(q)`-generalized quadrange `GQ(q,q^2)` into two parts, so that on any of them the induced subgraph of the point graph of the GQ has parameters as above [CP05]_. Directly follwing the construction in [CP05]_ is not efficient, as one then needs to construct the dual `GQ(q^2,q)`. Thus we describe here a more efficient approach that we came up with, following a suggestion by T.Penttila. Namely, this partition is invariant under the subgroup `H=\Omega_3(q^2)<O_6^-(q)`. We build the appropriate `H`, which leaves the form `B(X,Y,Z)=XY+Z^2` invariant, and pick up two orbits of `H` on the `F_q`-points. One them is `B`-isotropic, and we take the representative `(1:0:0)`. The other one corresponds to the points of `PG(2,q^2)` that have all the lines on them either missing the conic specified by `B`, or intersecting the conic in two points. We take `(1:1:e)` as the representative. It suffices to pick `e` so that `e^2+1` is not a square in `F_{q^2}`. Indeed, The conic can be viewed as the union of `\{(0:1:0)\}` and `\{(1:-t^2:t) | t \in F_{q^2}\}`. The coefficients of a generic line on `(1:1:e)` are `[1:-1-eb:b]`, for `-1\neq eb`. Thus, to make sure the intersection with the conic is always even, we need that the discriminant of `1+(1+eb)t^2+tb=0` never vanishes, and this is if and only if `e^2+1` is not a square. Further, we need to adjust `B`, by multiplying it by appropriately chosen `\nu`, so that `(1:1:e)` becomes isotropic under the relative trace norm `\nu B(X,Y,Z)+(\nu B(X,Y,Z))^q`. The latter is used then to define the graph. INPUT: - ``q`` -- an odd prime power. EXAMPLES: For `q=3` one gets Sims-Gewirtz graph. :: sage: G=graphs.CossidentePenttilaGraph(3) # optional - gap_packages (grape) sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) (56, 10, 0, 2) For `q>3` one gets new graphs. :: sage: G=graphs.CossidentePenttilaGraph(5) # optional - gap_packages (grape) sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) (378, 52, 1, 8) TESTS:: sage: G=graphs.CossidentePenttilaGraph(7) # optional - gap_packages (grape) # long time sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) # long time (1376, 150, 2, 18) sage: graphs.CossidentePenttilaGraph(2) Traceback (most recent call last): ... ValueError: q(=2) must be an odd prime power REFERENCES: .. [CP05] \A.Cossidente and T.Penttila Hemisystems on the Hermitian surface Journal of London Math. Soc. 72(2005), 731-741 """ p, k = is_prime_power(q,get_data=True) if k==0 or p==2: raise ValueError('q(={}) must be an odd prime power'.format(q)) from sage.libs.gap.libgap import libgap from sage.misc.package import is_package_installed, PackageNotFoundError if not is_package_installed('gap_packages'): raise PackageNotFoundError('gap_packages') adj_list=libgap.function_factory("""function(q) local z, e, so, G, nu, G1, G0, B, T, s, O1, O2, x; LoadPackage("grape"); G0:=SO(3,q^2); so:=GeneratorsOfGroup(G0); G1:=Group(Comm(so[1],so[2]),Comm(so[1],so[3]),Comm(so[2],so[3])); B:=InvariantBilinearForm(G0).matrix; z:=Z(q^2); e:=z; sqo:=(q^2-1)/2; if IsInt(sqo/Order(e^2+z^0)) then e:=z^First([2..q^2-2], x-> not IsInt(sqo/Order(z^(2*x)+z^0))); fi; nu:=z^First([0..q^2-2], x->z^x*(e^2+z^0)+(z^x*(e^2+z^0))^q=0*z); T:=function(x) local r; r:=nu*x*B*x; return r+r^q; end; s:=Group([Z(q)*IdentityMat(3,GF(q))]); O1:=Orbit(G1, Set(Orbit(s,z^0*[1,0,0])), OnSets); O2:=Orbit(G1, Set(Orbit(s,z^0*[1,1,e])), OnSets); G:=Graph(G1,Concatenation(O1,O2),OnSets, function(x,y) return x<>y and 0*z=T(x[1]+y[1]); end); return List([1..OrderGraph(G)],x->Adjacency(G,x)); end;""") adj = adj_list(q) # for each vertex, we get the list of vertices it is adjacent to G = Graph(((i,int(j-1)) for i,ni in enumerate(adj) for j in ni), format='list_of_edges', multiedges=False) G.name('CossidentePenttila('+str(q)+')') return G
Operations for LibGAP Elements GAP functions for which several methods can be available are called operations, so GAP ``Size`` is an example of an operation. This module is for inspecting GAP operations from Python. In particular, it can list the operations that take a particular LibGAP element as first argument. This is used in tab completion, where Python ``x.[TAB]`` lists all GAP operations for which ``Operation(x, ...)`` is defined. """ import re import string from sage.structure.sage_object import SageObject from sage.libs.gap.libgap import libgap Length = libgap.function_factory('Length') FlagsType = libgap.function_factory('FlagsType') TypeObj = libgap.function_factory('TypeObj') IS_SUBSET_FLAGS = libgap.function_factory('IS_SUBSET_FLAGS') OPERATIONS = libgap.get_global('OPERATIONS') NameFunction = libgap.function_factory('NameFunction') NAME_RE = re.compile(r'(Setter|Getter|Tester)\((.*)\)') class OperationInspector(SageObject): def __init__(self, libgap_element): """ Information about operations that can act on a given LibGAP element INPUT:
""" ############################################################################### # Copyright (C) 2016, Volker Braun <*****@*****.**> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ ############################################################################### from six.moves import cPickle from sage.libs.gap.libgap import libgap from sage.libs.gap.saved_workspace import workspace NamesGVars = libgap.function_factory('NamesGVars') Filtered = libgap.function_factory('Filtered') ValueGlobal = libgap.function_factory('ValueGlobal') IsBoundGlobal = libgap.function_factory('IsBoundGlobal') IsFunction = libgap.function_factory('IsFunction') IsDocumentedWord = libgap.function_factory('IsDocumentedWord') def load_or_compute(name, function): """ Helper to load a cached value or compute it INPUT: - ``name`` -- string. Part of the cache filename
# Copyright (C) 2016, Volker Braun <*****@*****.**> # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of # the License, or (at your option) any later version. # http://www.gnu.org/licenses/ ############################################################################### import cPickle import string from sage.libs.gap.libgap import libgap from sage.libs.gap.saved_workspace import workspace NamesGVars = libgap.function_factory('NamesGVars') Filtered =libgap.function_factory('Filtered') ValueGlobal = libgap.function_factory('ValueGlobal') IsBoundGlobal = libgap.function_factory('IsBoundGlobal') IsFunction = libgap.function_factory('IsFunction') IsDocumentedWord = libgap.function_factory('IsDocumentedWord') def load_or_compute(name, function): """ Helper to load a cached value or compute it INPUT: - ``name`` -- string. Part of the cache filename
GAP functions for which several methods can be available are called operations, so GAP ``Size`` is an example of an operation. This module is for inspecting GAP operations from Python. In particular, it can list the operations that take a particular LibGAP element as first argument. This is used in tab completion, where Python ``x.[TAB]`` lists all GAP operations for which ``Operation(x, ...)`` is defined. """ import re import string from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method from sage.libs.gap.libgap import libgap Length = libgap.function_factory('Length') FlagsType = libgap.function_factory('FlagsType') TypeObj = libgap.function_factory('TypeObj') IS_SUBSET_FLAGS = libgap.function_factory('IS_SUBSET_FLAGS') OPERATIONS = libgap.get_global('OPERATIONS') NameFunction = libgap.function_factory('NameFunction') NAME_RE = re.compile('(Setter|Getter|Tester)\((.*)\)') class OperationInspector(SageObject): def __init__(self, libgap_element): """ Information about operations that can act on a given LibGAP element
""" All Documented GAP Functions This Python module contains all documented GAP functions, they can be thought of as the official API of GAP. EXAMPLES:: sage: from sage.libs.gap.all_documented_functions import * sage: DihedralGroup(8) <pc group of size 8 with 3 generators> sage: GeneratorsOfGroup(_) [ f1, f2, f3 ] sage: List(_, Order) [ 2, 4, 2 ] """ from sage.libs.gap.libgap import libgap from sage.libs.gap.assigned_names import FUNCTIONS as _FUNCTIONS for _f in _FUNCTIONS: globals()[_f] = libgap.function_factory(_f)
def CossidentePenttilaGraph(q): r""" Cossidente-Penttila `((q^3+1)(q+1)/2,(q^2+1)(q-1)/2,(q-3)/2,(q-1)^2/2)`-strongly regular graph For each odd prime power `q`, one can partition the points of the `O_6^-(q)`-generalized quadrange `GQ(q,q^2)` into two parts, so that on any of them the induced subgraph of the point graph of the GQ has parameters as above [CP05]_. Directly follwing the construction in [CP05]_ is not efficient, as one then needs to construct the dual `GQ(q^2,q)`. Thus we describe here a more efficient approach that we came up with, following a suggestion by T.Penttila. Namely, this partition is invariant under the subgroup `H=\Omega_3(q^2)<O_6^-(q)`. We build the appropriate `H`, which leaves the form `B(X,Y,Z)=XY+Z^2` invariant, and pick up two orbits of `H` on the `F_q`-points. One them is `B`-isotropic, and we take the representative `(1:0:0)`. The other one corresponds to the points of `PG(2,q^2)` that have all the lines on them either missing the conic specified by `B`, or intersecting the conic in two points. We take `(1:1:e)` as the representative. It suffices to pick `e` so that `e^2+1` is not a square in `F_{q^2}`. Indeed, The conic can be viewed as the union of `\{(0:1:0)\}` and `\{(1:-t^2:t) | t \in F_{q^2}\}`. The coefficients of a generic line on `(1:1:e)` are `[1:-1-eb:b]`, for `-1\neq eb`. Thus, to make sure the intersection with the conic is always even, we need that the discriminant of `1+(1+eb)t^2+tb=0` never vanishes, and this is if and only if `e^2+1` is not a square. Further, we need to adjust `B`, by multiplying it by appropriately chosen `\nu`, so that `(1:1:e)` becomes isotropic under the relative trace norm `\nu B(X,Y,Z)+(\nu B(X,Y,Z))^q`. The latter is used then to define the graph. INPUT: - ``q`` -- an odd prime power. EXAMPLES: For `q=3` one gets Sims-Gewirtz graph. :: sage: G=graphs.CossidentePenttilaGraph(3) # optional - gap_packages (grape) sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) (56, 10, 0, 2) For `q>3` one gets new graphs. :: sage: G=graphs.CossidentePenttilaGraph(5) # optional - gap_packages (grape) sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) (378, 52, 1, 8) TESTS:: sage: G=graphs.CossidentePenttilaGraph(7) # optional - gap_packages (grape) # long time sage: G.is_strongly_regular(parameters=True) # optional - gap_packages (grape) # long time (1376, 150, 2, 18) sage: graphs.CossidentePenttilaGraph(2) Traceback (most recent call last): ... ValueError: q(=2) must be an odd prime power REFERENCES: .. [CP05] A.Cossidente and T.Penttila Hemisystems on the Hermitian surface Journal of London Math. Soc. 72(2005), 731-741 """ p, k = is_prime_power(q,get_data=True) if k==0 or p==2: raise ValueError('q(={}) must be an odd prime power'.format(q)) from sage.libs.gap.libgap import libgap from sage.misc.package import is_package_installed, PackageNotFoundError if not is_package_installed('gap_packages'): raise PackageNotFoundError('gap_packages') adj_list=libgap.function_factory("""function(q) local z, e, so, G, nu, G1, G0, B, T, s, O1, O2, x; LoadPackage("grape"); G0:=SO(3,q^2); so:=GeneratorsOfGroup(G0); G1:=Group(Comm(so[1],so[2]),Comm(so[1],so[3]),Comm(so[2],so[3])); B:=InvariantBilinearForm(G0).matrix; z:=Z(q^2); e:=z; sqo:=(q^2-1)/2; if IsInt(sqo/Order(e^2+z^0)) then e:=z^First([2..q^2-2], x-> not IsInt(sqo/Order(z^(2*x)+z^0))); fi; nu:=z^First([0..q^2-2], x->z^x*(e^2+z^0)+(z^x*(e^2+z^0))^q=0*z); T:=function(x) local r; r:=nu*x*B*x; return r+r^q; end; s:=Group([Z(q)*IdentityMat(3,GF(q))]); O1:=Orbit(G1, Set(Orbit(s,z^0*[1,0,0])), OnSets); O2:=Orbit(G1, Set(Orbit(s,z^0*[1,1,e])), OnSets); G:=Graph(G1,Concatenation(O1,O2),OnSets, function(x,y) return x<>y and 0*z=T(x[1]+y[1]); end); return List([1..OrderGraph(G)],x->Adjacency(G,x)); end;""") adj = adj_list(q) # for each vertex, we get the list of vertices it is adjacent to G = Graph(((i,int(j-1)) for i,ni in enumerate(adj) for j in ni), format='list_of_edges', multiedges=False) G.name('CossidentePenttila('+str(q)+')') return G