Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Merge branch 'u/dimpase/seidelsw' of git://trac.sagemath.org/sage int…
Browse files Browse the repository at this point in the history
…o seidelsw
  • Loading branch information
dimpase committed Aug 3, 2015
2 parents edb1b08 + 21b01bc commit 0aafa88
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 7 deletions.
1 change: 1 addition & 0 deletions src/doc/en/reference/combinat/module_list.rst
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ Comprehensive Module list
sage/combinat/designs/orthogonal_arrays_find_recursive
sage/combinat/designs/steiner_quadruple_systems
sage/combinat/designs/subhypergraph_search
sage/combinat/designs/twographs
sage/combinat/diagram_algebras
sage/combinat/dict_addition
sage/combinat/dlx
Expand Down
2 changes: 2 additions & 0 deletions src/sage/combinat/designs/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,5 @@
trivial_covering_design)

import design_catalog as designs

from twographs import *
95 changes: 95 additions & 0 deletions src/sage/combinat/designs/twographs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
r"""
Two-graphs
A two-graph on `n` points is a family `T \subset \binom {[n]}{3}`
of `3`-sets, such that any `4`-set `S\subset [n]` of size four
contains an even number of elements of `T`. Any graph `([n],E)`
gives rise to a two-graph
`T(E)=\{t \in \binom {[n]}{3} : | \binom {t}{2} \cap E | odd \}`,
and any two graphs with the same two-graph can be obtained one
from the other by :meth:`Seidel switching <sage.graphs.Graph.seidel_switching>`.
This defines an equivalence relation on the graphs on `[n]`,
called Seidel switching equivalence.
Conversely, given a two-graph `T`, one can construct a graph
`\Gamma` in the corresponding Seidel switching class with an
isolated vertex `w`. The graph `\Gamma \setminus w` is called
the descendant of `T` w.r.t. `v`.
`T` is called regular if each two-subset of `[n]` is contained
in the same number alpha of triples of `T`.
This module implements a direct construction of a two-graph from a list of
triples, constrution of descendant graphs, regularity checking, and other
things such as constructing the complement two-graph.
REFERENCES:
.. [BH12] A. E. Brouwer, W. H. Haemers,
Spectra of Graphs,
Springer, 2012
http://dx.doi.org/10.1007/978-1-4614-1939-6
AUTHORS:
- Dima Pasechnik (Aug 2015)
Index
-----
This module's functions are the following :
.. csv-table::
:class: contentstable
:widths: 30, 70
:delim: |
:func:`~is_regular_twograph` | returns True if the inc. system is regular twograph
:func:`~is_twograph` | returns True if the inc.system is a two-graph
:func:`~twograph_complement` | returns the complement of self
:func:`~twograph_descendant` | returns the descendant graph at `w`
Functions
---------
"""
from sage.combinat.designs.incidence_structures import IncidenceStructure
from itertools import combinations
from sage.misc.functional import is_odd, is_even

def is_regular_twograph(T, alpha=False, check=False):
"""
returns True if the inc. system is regular twograph
"""
if check:
if not is_twograph(T):
if alpha:
return False, 0
return False
r, (_,_,_,alpha) = T.is_t_design(t=2, return_parameters=True)
if alpha:
return r, alpha
return r

def is_twograph(T):
"""
True if the inc.system is a two-graph
"""
return all(map(lambda f: is_even(sum(map(lambda x: x in T.blocks(), combinations(f, 3)))),
combinations(T.ground_set(), 4)))

def twograph_descendant(T,v):
"""
the descendant graph at `v`
"""
from sage.graphs.graph import Graph
edges = map(lambda y: frozenset(filter(lambda z: z != v, y)), filter(lambda x: v in x, T1.blocks()))
V = T.ground_set()
V.remove(v)
return Graph([V, lambda i, j: frozenset((i,j)) in edges])

def twograph_complement(T):
"""
the complement
"""
Tc = filter(lambda x: not list(x) in T.blocks(), combinations(T.ground_set(), 3))
return IncidenceStructure(T.ground_set(), Tc)
19 changes: 17 additions & 2 deletions src/sage/graphs/generators/families.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,10 +462,13 @@ def chang_graphs():
information about the Chang graphs, see :wikipedia:`Chang_graphs` or
http://www.win.tue.nl/~aeb/graphs/Chang.html.
EXAMPLES::
EXAMPLES: check that we get 4 non-isomorphic s.r.g.'s with the
same parameters::
sage: chang_graphs = graphs.chang_graphs()
sage: four_srg = chang_graphs + [graphs.CompleteGraph(8).line_graph()]
sage: K8 = graphs.CompleteGraph(8)
sage: T8 = K8.line_graph()
sage: four_srg = chang_graphs + [T8]
sage: for g in four_srg:
....: print g.is_strongly_regular(parameters=True)
(28, 12, 6, 4)
Expand All @@ -475,6 +478,18 @@ def chang_graphs():
sage: from itertools import combinations
sage: for g1,g2 in combinations(four_srg,2):
....: assert not g1.is_isomorphic(g2)
Construct the Chang graphs by Seidel switching::
sage: c3c5=graphs.CycleGraph(3).disjoint_union(graphs.CycleGraph(5))
sage: c8=graphs.CycleGraph(8)
sage: s=[K8.subgraph_search(c8).edges(),
....: [(0,1,None),(2,3,None),(4,5,None),(6,7,None)],
....: K8.subgraph_search(c3c5).edges()]
sage: map(lambda x,G: T8.seidel_switching(x).is_isomorphic(G),
....: s, chang_graphs)
[True, True, True]
"""
g1 = Graph("[}~~EebhkrRb_~SoLOIiAZ?LBBxDb?bQcggjHKEwoZFAaiZ?Yf[?dxb@@tdWGkwn",
loops=False, multiedges=False)
Expand Down
142 changes: 137 additions & 5 deletions src/sage/graphs/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
:meth:`~Graph.bipartite_color` | Returns a dictionary with vertices as the keys and the color class as the values.
:meth:`~Graph.is_directed` | Since graph is undirected, returns False.
:meth:`~Graph.join` | Returns the join of self and other.
:meth:`~Graph.seidel_switching` | Returns Seidel switching w.r.t. a subset of vertices.
:meth:`~Graph.seidel_adjacency_matrix` | Returns Seidel adjacency matrix of self.
:meth:`~Graph.twograph` | Returns the two-graph of self.
**Distances:**
Expand Down Expand Up @@ -622,6 +625,8 @@ class Graph(GenericGraph):
#. A Sage adjacency matrix or incidence matrix
#. A Sage Seidel adjacency matrix
#. A pygraphviz graph
#. A NetworkX graph
Expand All @@ -647,8 +652,8 @@ class Graph(GenericGraph):
- ``weighted`` - whether graph thinks of itself as
weighted or not. See ``self.weighted()``
- ``format`` - if None, Graph tries to guess- can be
several values, including:
- ``format`` - if None, Graph tries to guess; can take
a number of values, namely:
- ``'int'`` - an integer specifying the number of vertices in an
edge-free graph with vertices labelled from 0 to n-1
Expand All @@ -668,6 +673,10 @@ class Graph(GenericGraph):
matrix M, with M[i,j] equal to the weight of the single edge {i,j}.
Given this format, weighted is ignored (assumed True).
- ``'seidel_adjacency_matrix'`` - a symmetric Sage matrix M
with 0s on the diagonal, and the other entries -1 or 1,
M[i,j]=-1 indicating that (i,j) is an edge, otherwise M[i,j]=1.
- ``'incidence_matrix'`` - a Sage matrix, with one
column C for each edge, where if C represents {i, j}, C[i] is -1
and C[j] is 1
Expand Down Expand Up @@ -1344,6 +1353,40 @@ def __init__(self, data=None, pos=None, loops=None, format=None,
self.allow_multiple_edges(multiedges, check=False)
self.add_vertices(range(data.nrows()))
self.add_edges(positions)
elif format == 'seidel_adjacency_matrix':
assert is_Matrix(data)
if data.base_ring() != ZZ:
try:
data = data.change_ring(ZZ)
except TypeError:
raise ValueError("Graph's Seidel adjacency matrix must"+
" have only 0,1,-1 integer entries")

if data.is_sparse():
entries = set(data[i,j] for i,j in data.nonzero_positions())
else:
entries = set(data.list())

if any(e < -1 or e > 1 for e in entries):
raise ValueError("Graph's Seidel adjacency matrix must"+
" have only 0,1,-1 integer entries")
if any(i==j for i,j in data.nonzero_positions()):
raise ValueError("Graph's Seidel adjacency matrix must"+
" have 0s on the main diagonal")
if not data.is_symmetric():
raise ValueError("Graph's Seidel adjacency matrix must"+
" be symmetric")
multiedges = False
weighted = False
loops = False
self.allow_loops(False)
self.allow_multiple_edges(False)
self.add_vertices(range(data.nrows()))
e = []
for i,j in data.nonzero_positions():
if i <= j and data[i,j] < 0:
e.append((i,j))
self.add_edges(e)
elif format == 'Graph':
if loops is None: loops = data.allows_loops()
if multiedges is None: multiedges = data.allows_multiple_edges()
Expand Down Expand Up @@ -4910,6 +4953,95 @@ def join(self, other, verbose_relabel=None, labels="pairs"):
G.name('%s join %s'%(self.name(), other.name()))
return G


def seidel_adjacency_matrix(self, vertices=None):
"""
Returns the Seidel adjacency matrix of self.
Returns the Seidel adjacency matrix of the graph.
For `A` the (ordinary) adjacency matrix of ``self``,
i.e. :meth:`GenericGraph.adjacency_matrix`,
`I` the identity matrix, and `J` the all-1 matrix
is given by `J-I-2A`. It is closely related to twographs,
see :meth:`twograph`.
The matrix returned is over the integers. If a different ring is
desired, use either the change_ring function or the matrix
function.
INPUT:
- ``vertices`` (list) -- the ordering of the vertices defining how they
should appear in the matrix. By default, the ordering given by
:meth:`GenericGraph.vertices` is used.
EXAMPLES::
sage: G = graphs.CycleGraph(5)
sage: G = G.disjoint_union(graphs.CompleteGraph(1))
sage: G.seidel_adjacency_matrix().minpoly()
x^2 - 5
"""

return -self.adjacency_matrix(sparse=False, vertices=vertices)+ \
self.complement().adjacency_matrix(sparse=False, \
vertices=vertices)

def seidel_switching(self, s):
"""
Returns the Seidel switching of self w.r.t. subset of vertices ``s``.
Returns the graph obtained by Seidel switching of self
with respect to the subset of vertices ``s``. This is the graph
given by Seidel adjacency matrix DSD, for S the Seidel
adjacency matrix of self, and D the diagonal matrix with -1s
at positions corresponding to ``s``, and 1s elsewhere.
INPUT:
- ``s`` -- a list of vertices
EXAMPLES::
sage: G = graphs.CycleGraph(5)
sage: G = G.disjoint_union(graphs.CompleteGraph(1))
sage: H = G.seidel_switching([(0,1),(1,0),(0,0)])
sage: H.seidel_adjacency_matrix().minpoly()
x^2 - 5
sage: H.is_connected()
True
"""
idx = frozenset([self.vertices().index(v) for v in s])
S = self.seidel_adjacency_matrix()
for i in xrange(S.nrows()):
for j in xrange(i):
if (i in idx and (not j in idx)) or \
(j in idx and (not i in idx)):
S[i,j] = - S[i,j]
S[j,i] = - S[j,i]
from sage.graphs.graph import Graph
H = Graph(S, format="seidel_adjacency_matrix")
H.relabel(self.vertices())
return H


def twograph(self):
"""
Returns the two-graph of self
Returns the two-graph with the triples
`T=\{t \in \binom {V}{3} : | \binom {t}{2} \cap E | odd \}`
where `V` and `E` are vertices and edges of self, respectively.
"""
from sage.combinat.designs.incidence_structures import IncidenceStructure
from itertools import combinations
from sage.misc.functional import is_odd

T = [t for t in combinations(self.vertices(), 3) \
if is_odd([i in self.neighbors(j) for i,j in combinations(t, 2)].count(True))]
return IncidenceStructure(T)

### Visualization

def write_to_eps(self, filename, **options):
Expand All @@ -4927,9 +5059,9 @@ def write_to_eps(self, filename, **options):
sage: P.write_to_eps(tmp_filename(ext='.eps'))
It is relatively simple to include this file in a LaTeX
document. ``\usepackagegraphics`` must appear in the
preamble, and ``\includegraphics{filename.eps}`` will include
the file. To compile the document to ``pdf`` with ``pdflatex``
document. ``\usepackage{graphics}`` must appear in the
preamble, and ``\includegraphics{filename}`` will include
the file. To compile the document to ``pdf`` with ``pdflatex`` or ``xelatex``
the file needs first to be converted to ``pdf``, for example
with ``ps2pdf filename.eps filename.pdf``.
"""
Expand Down

0 comments on commit 0aafa88

Please sign in to comment.