def _polar_graph(m, q, g, intersection_size=None): r""" The helper function to build graphs `(D)U(m,q)` and `(D)Sp(m,q)` Building a graph on an orbit of a group `g` of `m\times m` matrices over `GF(q)` on the points (or subspaces of dimension ``m//2``) isotropic w.r.t. the form `F` left invariant by the group `g`. The only constraint is that the first ``m//2`` elements of the standard basis must generate a totally isotropic w.r.t. `F` subspace; this is the case with these groups coming from GAP; namely, `F` has the anti-diagonal all-1 matrix. INPUT: - ``m`` -- the dimension of the underlying vector space - ``q`` -- the size of the field - ``g`` -- the group acting - ``intersection_size`` -- if ``None``, build the graph on the isotropic points, with adjacency being orthogonality w.r.t. `F`. Otherwise, build the graph on the maximal totally isotropic subspaces, with adjacency specified by ``intersection_size`` being as given. TESTS:: sage: from sage.graphs.generators.classical_geometries import _polar_graph sage: _polar_graph(4, 4, libgap.GeneralUnitaryGroup(4, 2)) Graph on 45 vertices sage: _polar_graph(4, 4, libgap.GeneralUnitaryGroup(4, 2), intersection_size=1) Graph on 27 vertices """ from sage.libs.gap.libgap import libgap from itertools import combinations W = libgap.FullRowSpace(libgap.GF(q), m) # F_q^m B = libgap.Elements(libgap.Basis(W)) # the standard basis of W V = libgap.Orbit(g, B[0], libgap.OnLines) # orbit on isotropic points gp = libgap.Action(g, V, libgap.OnLines) # make a permutation group s = libgap.Subspace( W, [B[i] for i in range(m // 2)]) # a totally isotropic subspace # and the points there sp = [ libgap.Elements(libgap.Basis(x))[0] for x in libgap.Elements(s.Subspaces(1)) ] h = libgap.Set(map(lambda x: libgap.Position(V, x), sp)) # indices of the points in s L = libgap.Orbit(gp, h, libgap.OnSets) # orbit on these subspaces if intersection_size == None: G = Graph() for x in L: # every pair of points in the subspace is adjacent to each other in G G.add_edges(combinations(x, 2)) return G else: return Graph([ L, lambda i, j: libgap.Size(libgap.Intersection(i, j)) == intersection_size ], loops=False)
def NonisotropicUnitaryPolarGraph(m, q): r""" Returns the Graph `NU(m,q)`. Returns the graph on nonisotropic, with respect to a nondegenerate Hermitean form, points of the `(m-1)`-dimensional projective space over `F_q`, with points adjacent whenever they lie on a tangent (to the set of isotropic points) line. For more information, see Sect. 9.9 of [BH12]_ and series C14 in [Hu75]_. INPUT: - ``m,q`` (integers) -- `q` must be a prime power. EXAMPLES:: sage: g=graphs.NonisotropicUnitaryPolarGraph(5,2); g NU(5, 2): Graph on 176 vertices sage: g.is_strongly_regular(parameters=True) (176, 135, 102, 108) TESTS:: sage: graphs.NonisotropicUnitaryPolarGraph(4,2).is_strongly_regular(parameters=True) (40, 27, 18, 18) sage: graphs.NonisotropicUnitaryPolarGraph(4,3).is_strongly_regular(parameters=True) # long time (540, 224, 88, 96) sage: graphs.NonisotropicUnitaryPolarGraph(6,6) Traceback (most recent call last): ... ValueError: q must be a prime power REFERENCE: .. [Hu75] X. L. Hubaut. Strongly regular graphs. Disc. Math. 13(1975), pp 357--381. http://dx.doi.org/10.1016/0012-365X(75)90057-6 """ 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') from sage.libs.gap.libgap import libgap from itertools import combinations F = libgap.GF(q**2) # F_{q^2} W = libgap.FullRowSpace(F, m) # F_{q^2}^m B = libgap.Elements(libgap.Basis(W)) # the standard basis of W if m % 2 != 0: point = B[(m - 1) / 2] else: if p == 2: point = B[m / 2] + F.PrimitiveRoot() * B[(m - 2) / 2] else: point = B[(m - 2) / 2] + B[m / 2] g = libgap.GeneralUnitaryGroup(m, q) V = libgap.Orbit(g, point, libgap.OnLines) # orbit on nonisotropic points gp = libgap.Action(g, V, libgap.OnLines) # make a permutation group s = libgap.Subspace(W, [point, point + B[0]]) # a tangent line on point # and the points there sp = [ libgap.Elements(libgap.Basis(x))[0] for x in libgap.Elements(s.Subspaces(1)) ] h = libgap.Set( map(lambda x: libgap.Position(V, x), libgap.Intersection(V, sp))) # indices L = libgap.Orbit(gp, h, libgap.OnSets) # orbit on the tangent lines G = Graph() for x in L: # every pair of points in the subspace is adjacent to each other in G G.add_edges(combinations(x, 2)) G.relabel() G.name("NU" + str((m, q))) return G
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