Exemple #1
0
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)
Exemple #2
0
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
Exemple #3
0
def _orthogonal_polar_graph(m, q, sign="+", point_type=[0]):
    r"""
    A helper function to build ``OrthogonalPolarGraph`` and ``NO2,3,5`` graphs.

    See see the `page of
    Andries Brouwer's website <http://www.win.tue.nl/~aeb/graphs/srghub.html>`_.

    INPUT:

    - ``m,q`` (integers) -- `q` must be a prime power.

    - ``sign`` -- ``"+"`` or ``"-"`` if `m` is even, ``"+"`` (default)
      otherwise.

    - ``point_type`` -- a list of elements from `F_q`

    EXAMPLES:
    
    Petersen graph::
`
        sage: from sage.graphs.generators.classical_geometries import _orthogonal_polar_graph
        sage: g=_orthogonal_polar_graph(3,5,point_type=[2,3])
        sage: g.is_strongly_regular(parameters=True)
        (10, 3, 0, 1)

    A locally Petersen graph (a.k.a. Doro graph, a.k.a. Hall graph)::

        sage: g=_orthogonal_polar_graph(4,5,'-',point_type=[2,3])
        sage: g.is_distance_regular(parameters=True)
        ([10, 6, 4, None], [None, 1, 2, 5])

    Various big and slow to build graphs:

    `NO^+(7,3)`::

        sage: g=_orthogonal_polar_graph(7,3,point_type=[1])  # not tested (long time)
        sage: g.is_strongly_regular(parameters=True)       # not tested (long time)
        (378, 117, 36, 36)

    `NO^-(7,3)`::

        sage: g=_orthogonal_polar_graph(7,3,point_type=[-1]) # not tested (long time)
        sage: g.is_strongly_regular(parameters=True)       # not tested (long time)
        (351, 126, 45, 45)

    `NO^+(6,3)`::

        sage: g=_orthogonal_polar_graph(6,3,point_type=[1])
        sage: g.is_strongly_regular(parameters=True)
        (117, 36, 15, 9)

    `NO^-(6,3)`::

        sage: g=_orthogonal_polar_graph(6,3,'-',point_type=[1])
        sage: g.is_strongly_regular(parameters=True)
        (126, 45, 12, 18)

    `NO^{-,\perp}(5,5)`::

        sage: g=_orthogonal_polar_graph(5,5,point_type=[2,3]) # long time
        sage: g.is_strongly_regular(parameters=True)          # long time
        (300, 65, 10, 15)

    `NO^{+,\perp}(5,5)`::

        sage: g=_orthogonal_polar_graph(5,5,point_type=[1,-1]) # not tested (long time)
        sage: g.is_strongly_regular(parameters=True) # not tested (long time)
        (325, 60, 15, 10)

    TESTS::

        sage: g=_orthogonal_polar_graph(5,3,point_type=[-1])
        sage: g.is_strongly_regular(parameters=True)
        (45, 12, 3, 3)
        sage: g=_orthogonal_polar_graph(5,3,point_type=[1])
        sage: g.is_strongly_regular(parameters=True)
        (36, 15, 6, 6)

    """
    from sage.schemes.projective.projective_space import ProjectiveSpace
    from sage.rings.finite_rings.constructor import FiniteField
    from sage.modules.free_module_element import free_module_element as vector
    from sage.matrix.constructor import Matrix
    from sage.libs.gap.libgap import libgap
    from itertools import combinations

    if m % 2 == 0:
        if sign != "+" and sign != "-":
            raise ValueError("sign must be equal to either '-' or '+' when "
                             "m is even")
    else:
        if sign != "" and sign != "+":
            raise ValueError("sign must be equal to either '' or '+' when "
                             "m is odd")
        sign = ""

    e = {'+': 1, '-': -1, '': 0}[sign]

    M = Matrix(
        libgap.InvariantQuadraticForm(libgap.GeneralOrthogonalGroup(
            e, m, q))['matrix'])
    Fq = libgap.GF(q).sage()
    PG = map(vector, ProjectiveSpace(m - 1, Fq))
    map(lambda x: x.set_immutable(), PG)

    def F(x):
        return x * M * x

    if q % 2 == 0:

        def P(x, y):
            return F(x - y)
    else:

        def P(x, y):
            return x * M * y + y * M * x

    V = [x for x in PG if F(x) in point_type]

    G = Graph([V, lambda x, y: P(x, y) == 0], loops=False)

    G.relabel()
    return G
Exemple #4
0
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
Exemple #5
0
def AffineOrthogonalPolarGraph(d, q, sign="+"):
    r"""
    Returns the affine polar graph `VO^+(d,q),VO^-(d,q)` or `VO(d,q)`.

    Affine Polar graphs are built from a `d`-dimensional vector space over
    `F_q`, and a quadratic form which is hyperbolic, elliptic or parabolic
    according to the value of ``sign``.

    Note that `VO^+(d,q),VO^-(d,q)` are strongly regular graphs, while `VO(d,q)`
    is not.

    For more information on Affine Polar graphs, see `Affine Polar
    Graphs page of Andries Brouwer's website
    <http://www.win.tue.nl/~aeb/graphs/VO.html>`_.

    INPUT:

    - ``d`` (integer) -- ``d`` must be even if ``sign != None``, and odd
      otherwise.

    - ``q`` (integer) -- a power of a prime number, as `F_q` must exist.

    - ``sign`` -- must be equal to ``"+"``, ``"-"``, or ``None`` to compute
      (respectively) `VO^+(d,q),VO^-(d,q)` or `VO(d,q)`. By default
      ``sign="+"``.

    .. NOTE::

        The graph `VO^\epsilon(d,q)` is the graph induced by the
        non-neighbors of a vertex in an :meth:`Orthogonal Polar Graph
        <OrthogonalPolarGraph>` `O^\epsilon(d+2,q)`.

    EXAMPLES:

    The :meth:`Brouwer-Haemers graph <BrouwerHaemersGraph>` is isomorphic to
    `VO^-(4,3)`::

        sage: g = graphs.AffineOrthogonalPolarGraph(4,3,"-")
        sage: g.is_isomorphic(graphs.BrouwerHaemersGraph())
        True

    Some examples from `Brouwer's table or strongly regular graphs
    <http://www.win.tue.nl/~aeb/graphs/srg/srgtab.html>`_::

        sage: g = graphs.AffineOrthogonalPolarGraph(6,2,"-"); g
        Affine Polar Graph VO^-(6,2): Graph on 64 vertices
        sage: g.is_strongly_regular(parameters=True)
        (64, 27, 10, 12)
        sage: g = graphs.AffineOrthogonalPolarGraph(6,2,"+"); g
        Affine Polar Graph VO^+(6,2): Graph on 64 vertices
        sage: g.is_strongly_regular(parameters=True)
        (64, 35, 18, 20)

    When ``sign is None``::

        sage: g = graphs.AffineOrthogonalPolarGraph(5,2,None); g
        Affine Polar Graph VO^-(5,2): Graph on 32 vertices
        sage: g.is_strongly_regular(parameters=True)
        False
        sage: g.is_regular()
        True
        sage: g.is_vertex_transitive()
        True
    """
    if sign in ["+", "-"]:
        s = 1 if sign == "+" else -1
        if d % 2 == 1:
            raise ValueError("d must be even when sign!=None")
    else:
        if d % 2 == 0:
            raise ValueError("d must be odd when sign==None")
        s = 0

    from sage.interfaces.gap import gap
    from sage.rings.finite_rings.constructor import FiniteField
    from sage.modules.free_module import VectorSpace
    from sage.matrix.constructor import Matrix
    from sage.libs.gap.libgap import libgap
    from itertools import combinations

    M = Matrix(
        libgap.InvariantQuadraticForm(libgap.GeneralOrthogonalGroup(
            s, d, q))['matrix'])
    F = libgap.GF(q).sage()
    V = list(VectorSpace(F, d))

    G = Graph()
    G.add_vertices([tuple(_) for _ in V])
    for x, y in combinations(V, 2):
        if not (x - y) * M * (x - y):
            G.add_edge(tuple(x), tuple(y))

    G.name("Affine Polar Graph VO^" + str('+' if s == 1 else '-') + "(" +
           str(d) + "," + str(q) + ")")
    G.relabel()
    return G