def libgap_group(self): from sage.libs.gap.libgap import libgap if not self._gens: return libgap.Group([libgap.PermList([])]) gens = [] for a in self._gens: gens.append(libgap.PermList([a[i] + 1 for i in range(self._n)])) return libgap.Group(gens)
def __init__(self, n): """ Initialize ``self``. EXAMPLES:: sage: G = groups.matrix.BinaryDihedral(4) sage: TestSuite(G).run() """ self._n = n if n % 2 == 0: R = CyclotomicField(2 * n) zeta = R.gen() i = R.gen()**(n // 2) else: R = CyclotomicField(4 * n) zeta = R.gen()**2 i = R.gen()**n MS = MatrixSpace(R, 2) zero = R.zero() gens = [MS([zeta, zero, zero, ~zeta]), MS([zero, i, i, zero])] from sage.libs.gap.libgap import libgap gap_gens = [libgap(matrix_gen) for matrix_gen in gens] gap_group = libgap.Group(gap_gens) FinitelyGeneratedMatrixGroup_gap.__init__(self, ZZ(2), R, gap_group, category=Groups().Finite())
def __init__(self, n=1, R=0): """ Initialize ``self``. EXAMPLES:: sage: H = groups.matrix.Heisenberg(n=2, R=5) sage: TestSuite(H).run() # long time sage: H = groups.matrix.Heisenberg(n=2, R=4) sage: TestSuite(H).run() # long time sage: H = groups.matrix.Heisenberg(n=3) sage: TestSuite(H).run(max_runs=30, skip="_test_elements") # long time sage: H = groups.matrix.Heisenberg(n=2, R=GF(4)) sage: TestSuite(H).run() # long time """ def elementary_matrix(i, j, val, MS): elm = copy(MS.one()) elm[i, j] = val elm.set_immutable() return elm self._n = n self._ring = R # We need the generators of the ring as a commutative additive group if self._ring is ZZ: ring_gens = [self._ring.one()] else: if self._ring.cardinality() == self._ring.characteristic(): ring_gens = [self._ring.one()] else: # This is overkill, but is the only way to ensure # we get all of the elements ring_gens = list(self._ring) dim = ZZ(n + 2) MS = MatrixSpace(self._ring, dim) gens_x = [ elementary_matrix(0, j, gen, MS) for j in range(1, dim - 1) for gen in ring_gens ] gens_y = [ elementary_matrix(i, dim - 1, gen, MS) for i in range(1, dim - 1) for gen in ring_gens ] gen_z = [elementary_matrix(0, dim - 1, gen, MS) for gen in ring_gens] gens = gens_x + gens_y + gen_z from sage.libs.gap.libgap import libgap gap_gens = [libgap(single_gen) for single_gen in gens] gap_group = libgap.Group(gap_gens) cat = Groups().FinitelyGenerated() if self._ring in Rings().Finite(): cat = cat.Finite() FinitelyGeneratedMatrixGroup_gap.__init__(self, ZZ(dim), self._ring, gap_group, category=cat)
def G(self): # Reconstruct the group from the data stored above if self.order == 1: # trvial return libgap.TrivialGroup() elif self.elt_rep_type == 0: # PcGroup return libgap.PcGroupCode(self.pc_code, self.order) elif self.elt_rep_type < 0: # Permutation group gens = [self.decode(g) for g in self.perm_gens] return libgap.Group(gens) else: # TODO: Matrix groups raise NotImplementedError
def __init__(self, degree, base_ring, gens, invariant_bilinear_form, category=None, check=True, invariant_submodule=None, invariant_quotient_module=None): r""" Create this orthogonal group from the input. TESTS:: sage: from sage.groups.matrix_gps.isometries import GroupOfIsometries sage: bil = Matrix(ZZ,2,[3,2,2,3]) sage: gens = [-Matrix(ZZ,2,[0,1,1,0])] sage: cat = Groups().Finite() sage: O = GroupOfIsometries(2, ZZ, gens, bil, category=cat) sage: TestSuite(O).run() """ from copy import copy G = copy(invariant_bilinear_form) G.set_immutable() self._invariant_bilinear_form = G self._invariant_submodule = invariant_submodule self._invariant_quotient_module = invariant_quotient_module if check: I = invariant_submodule Q = invariant_quotient_module for f in gens: self._check_matrix(f) if (not I is None) and I * f != I: raise ValueError("the submodule is not preserved") if not Q is None and (Q.W() != Q.W() * f or Q.V() * f != Q.V()): raise ValueError("the quotient module is not preserved") if len(gens) == 0: # handle the trivial group gens = [G.parent().identity_matrix()] from sage.libs.gap.libgap import libgap gap_gens = [libgap(matrix_gen) for matrix_gen in gens] gap_group = libgap.Group(gap_gens) FinitelyGeneratedMatrixGroup_gap.__init__(self, degree, base_ring, gap_group, category=category)
def _gap_eval_string(s): """ Evalute a string with libGAP. In some of our examples, permutation groups arise whose string representations are too large to be directly processed by libGAP. Therefore, we introduce this auxiliary function that cuts permutation group definitions into smaller bits before evaluation. NOTE: It could be that this function is in fact not needed, as we couldn't reproduce an example where a direct string evaluation in libGAP fails. """ if s.startswith('Group(['): return gap.Group([ gap.eval(p if p.endswith(')') else p + ')') for p in s[7:-2].strip().split('),') ]) return gap.eval(s)
def NonisotropicOrthogonalPolarGraph(m, q, sign="+", perp=None): r""" Returns the Graph `NO^{\epsilon,\perp}_{m}(q)` Let the vectorspace of dimension `m` over `F_q` be endowed with a nondegenerate quadratic form `F`, of type ``sign`` for `m` even. * `m` even: assume further that `q=2` or `3`. Returns the graph of the points (in the underlying projective space) `x` satisfying `F(x)=1`, with adjacency given by orthogonality w.r.t. `F`. Parameter ``perp`` is ignored. * `m` odd: if ``perp`` is not ``None``, then we assume that `q=5` and return the graph of the points `x` satisfying `F(x)=\pm 1` if ``sign="+"``, respectively `F(x) \in \{2,3\}` if ``sign="-"``, with adjacency given by orthogonality w.r.t. `F` (cf. Sect 7.D of [BvL84]_). Otherwise return the graph of nongenerate hyperplanes of type ``sign``, adjacent whenever the intersection is degenerate (cf. Sect. 7.C of [BvL84]_). Note that for `q=2` one will get a complete graph. For more information, see Sect. 9.9 of [BH12]_ and [BvL84]_. Note that the `page of Andries Brouwer's website <http://www.win.tue.nl/~aeb/graphs/srghub.html>`_ uses different notation. INPUT: - ``m`` - integer, half the dimension of the underlying vectorspace - ``q`` - a power of a prime number, the size of the underlying field - ``sign`` -- ``"+"`` (default) or ``"-"``. EXAMPLES: `NO^-(4,2)` is isomorphic to Petersen graph:: sage: g=graphs.NonisotropicOrthogonalPolarGraph(4,2,'-'); g NO^-(4, 2): Graph on 10 vertices sage: g.is_strongly_regular(parameters=True) (10, 3, 0, 1) `NO^-(6,2)` and `NO^+(6,2)`:: sage: g=graphs.NonisotropicOrthogonalPolarGraph(6,2,'-') sage: g.is_strongly_regular(parameters=True) (36, 15, 6, 6) sage: g=graphs.NonisotropicOrthogonalPolarGraph(6,2,'+'); g NO^+(6, 2): Graph on 28 vertices sage: g.is_strongly_regular(parameters=True) (28, 15, 6, 10) `NO^+(8,2)`:: sage: g=graphs.NonisotropicOrthogonalPolarGraph(8,2,'+') sage: g.is_strongly_regular(parameters=True) (120, 63, 30, 36) Wilbrink's graphs for `q=5`:: sage: graphs.NonisotropicOrthogonalPolarGraph(5,5,perp=1).is_strongly_regular(parameters=True) # long time (325, 60, 15, 10) sage: graphs.NonisotropicOrthogonalPolarGraph(5,5,'-',perp=1).is_strongly_regular(parameters=True) # long time (300, 65, 10, 15) Wilbrink's graphs:: sage: g=graphs.NonisotropicOrthogonalPolarGraph(5,4,'+') sage: g.is_strongly_regular(parameters=True) (136, 75, 42, 40) sage: g=graphs.NonisotropicOrthogonalPolarGraph(5,4,'-') sage: g.is_strongly_regular(parameters=True) (120, 51, 18, 24) sage: g=graphs.NonisotropicOrthogonalPolarGraph(7,4,'+'); g # not tested (long time) NO^+(7, 4): Graph on 2080 vertices sage: g.is_strongly_regular(parameters=True) # not tested (long time) (2080, 1071, 558, 544) TESTS:: sage: g=graphs.NonisotropicOrthogonalPolarGraph(4,2); g NO^+(4, 2): Graph on 6 vertices sage: graphs.NonisotropicOrthogonalPolarGraph(4,3,'-').is_strongly_regular(parameters=True) (15, 6, 1, 3) sage: g=graphs.NonisotropicOrthogonalPolarGraph(3,5,'-',perp=1); g NO^-,perp(3, 5): Graph on 10 vertices sage: g.is_strongly_regular(parameters=True) (10, 3, 0, 1) sage: g=graphs.NonisotropicOrthogonalPolarGraph(6,3,'+') # long time sage: g.is_strongly_regular(parameters=True) # long time (117, 36, 15, 9) sage: g=graphs.NonisotropicOrthogonalPolarGraph(6,3,'-'); g # long time NO^-(6, 3): Graph on 126 vertices sage: g.is_strongly_regular(parameters=True) # long time (126, 45, 12, 18) sage: g=graphs.NonisotropicOrthogonalPolarGraph(5,5,'-') # long time sage: g.is_strongly_regular(parameters=True) # long time (300, 104, 28, 40) sage: g=graphs.NonisotropicOrthogonalPolarGraph(5,5,'+') # long time sage: g.is_strongly_regular(parameters=True) # long time (325, 144, 68, 60) sage: g=graphs.NonisotropicOrthogonalPolarGraph(6,4,'+') Traceback (most recent call last): ... ValueError: for m even q must be 2 or 3 """ from sage.graphs.generators.classical_geometries import _orthogonal_polar_graph from sage.rings.arith import is_prime_power p, k = is_prime_power(q, get_data=True) if k == 0: raise ValueError('q must be a prime power') dec = '' if m % 2 == 0: if q in [2, 3]: G = _orthogonal_polar_graph(m, q, sign=sign, point_type=[1]) else: raise ValueError("for m even q must be 2 or 3") elif not perp is None: if q == 5: G = _orthogonal_polar_graph(m, q, point_type=\ [-1,1] if sign=='+' else [2,3] if sign=='-' else []) dec = ",perp" else: raise ValueError("for perp not None q must be 5") else: if not sign in ['+', '-']: raise ValueError("sign must be '+' or '-'") from sage.libs.gap.libgap import libgap g0 = libgap.GeneralOrthogonalGroup(m, q) g = libgap.Group( libgap.List(g0.GeneratorsOfGroup(), libgap.TransposedMat)) F = libgap.GF(q) # F_q W = libgap.FullRowSpace(F, m) # F_q^m e = 1 if sign == '+' else -1 n = (m - 1) / 2 # we build (q^n(q^n+e)/2, (q^n-e)(q^(n-1)+e), 2(q^(2n-2)-1)+eq^(n-1)(q-1), # 2q^(n-1)(q^(n-1)+e))-srg # **use** v and k to select appropriate orbit and orbital nvert = (q**n) * (q**n + e) / 2 # v deg = (q**n - e) * (q**(n - 1) + e) # k S=map(lambda x: libgap.Elements(libgap.Basis(x))[0], \ libgap.Elements(libgap.Subspaces(W,1))) V = filter(lambda x: len(x) == nvert, libgap.Orbits(g, S, libgap.OnLines)) assert len(V) == 1 V = V[0] gp = libgap.Action(g, V, libgap.OnLines) # make a permutation group h = libgap.Stabilizer(gp, 1) Vh = filter(lambda x: len(x) == deg, libgap.Orbits(h, libgap.Orbit(gp, 1))) assert len(Vh) == 1 Vh = Vh[0][0] L = libgap.Orbit(gp, [1, Vh], libgap.OnSets) G = Graph() G.add_edges(L) G.name("NO^" + sign + dec + str((m, q))) return G
def MatrixGroup(*gens, **kwds): r""" Return the matrix group with given generators. INPUT: - ``*gens`` -- matrices, or a single list/tuple/iterable of matrices, or a matrix group. - ``check`` -- boolean keyword argument (optional, default: ``True``). Whether to check that each matrix is invertible. EXAMPLES:: sage: F = GF(5) sage: gens = [matrix(F,2,[1,2, -1, 1]), matrix(F,2, [1,1, 0,1])] sage: G = MatrixGroup(gens); G Matrix group over Finite Field of size 5 with 2 generators ( [1 2] [1 1] [4 1], [0 1] ) In the second example, the generators are a matrix over `\ZZ`, a matrix over a finite field, and the integer `2`. Sage determines that they both canonically map to matrices over the finite field, so creates that matrix group there:: sage: gens = [matrix(2,[1,2, -1, 1]), matrix(GF(7), 2, [1,1, 0,1]), 2] sage: G = MatrixGroup(gens); G Matrix group over Finite Field of size 7 with 3 generators ( [1 2] [1 1] [2 0] [6 1], [0 1], [0 2] ) Each generator must be invertible:: sage: G = MatrixGroup([matrix(ZZ,2,[1,2,3,4])]) Traceback (most recent call last): ... ValueError: each generator must be an invertible matrix sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: MatrixGroup([MS.0]) Traceback (most recent call last): ... ValueError: each generator must be an invertible matrix sage: MatrixGroup([MS.0], check=False) # works formally but is mathematical nonsense Matrix group over Finite Field of size 5 with 1 generators ( [1 0] [0 0] ) Some groups are not supported, or do not have much functionality implemented:: sage: G = SL(0, QQ) Traceback (most recent call last): ... ValueError: the degree must be at least 1 sage: SL2C = SL(2, CC); SL2C Special Linear Group of degree 2 over Complex Field with 53 bits of precision sage: SL2C.gens() Traceback (most recent call last): ... AttributeError: 'LinearMatrixGroup_generic_with_category' object has no attribute 'gens' """ if isinstance(gens[-1], dict): # hack for unpickling kwds.update(gens[-1]) gens = gens[:-1] check = kwds.get('check', True) if len(gens) == 1: if isinstance(gens[0], (list, tuple)): gens = list(gens[0]) else: try: gens = [g.matrix() for g in gens[0]] except AttributeError: pass if len(gens) == 0: raise ValueError('need at least one generator') gens = normalize_square_matrices(gens) if check and any(not g.is_invertible() for g in gens): raise ValueError('each generator must be an invertible matrix') MS = gens.universe() base_ring = MS.base_ring() degree = ZZ(MS.ncols()) # == MS.nrows() from sage.libs.gap.libgap import libgap try: gap_gens = [libgap(matrix_gen) for matrix_gen in gens] gap_group = libgap.Group(gap_gens) return FinitelyGeneratedMatrixGroup_gap(degree, base_ring, gap_group) except (TypeError, ValueError): return FinitelyGeneratedMatrixGroup_generic(degree, base_ring, gens)