Graphic Matroids¶

Let $$G = (V,E)$$ be a graph and let $$C$$ be the collection of the edge sets of cycles in $$G$$. The corresponding graphic matroid $$M(G)$$ has ground set $$E$$ and circuits $$C$$.

Construction¶

The recommended way to create a graphic matroid is by using the Matroid() function, with a graph $$G$$ as input. This function can accept many different kinds of input to get a graphic matroid if the graph keyword is used, similar to the Graph() constructor. However, invoking the class directly is possible too. To get access to it, type:

Graphic matroids do not have a representation matrix or any of the functionality of regular matroids. It is possible to get an instance of the RegularMatroid class by using the regular keyword when constructing the matroid. It is also possible to cast a GraphicMatroid as a RegularMatroid with the regular_matroid() method:

sage: M1 = Matroid(graphs.DiamondGraph(), regular=True)
sage: M2 = Matroid(graphs.DiamondGraph())
sage: M3 = M2.regular_matroid()

Below are some examples of constructing a graphic matroid.

sage: edgelist = [(0, 1, 'a'), (0, 2, 'b'), (1, 2, 'c')]
sage: G = Graph(edgelist)
sage: M1 = Matroid(G)
sage: M2 = Matroid(graph=edgelist)
sage: M3 = Matroid(graphs.CycleGraph(3))
sage: M1 == M3
False
sage: M1.is_isomorphic(M3)
True
sage: M1.equals(M2)
True
sage: M1 == M2
True
sage: isinstance(M1, GraphicMatroid)
True
sage: isinstance(M1, RegularMatroid)
False

Note that if there is not a complete set of unique edge labels, and there are no parallel edges, then vertex tuples will be used for the ground set. The user may wish to override this by specifying the ground set, as the vertex tuples will not be updated if the matroid is modified:

sage: G = graphs.DiamondGraph()
sage: M1 = Matroid(G)
sage: N1 = M1.contract((0,1))
sage: N1.graph().edges_incident(0, sort=True)
[(0, 2, (0, 2)), (0, 2, (1, 2)), (0, 3, (1, 3))]
sage: M2 = Matroid(range(G.num_edges()), G)
sage: N2 = M2.contract(0)
sage: N1.is_isomorphic(N2)
True

AUTHORS:

• Zachary Gershkoff (2017-07-07): initial version

Methods¶

class sage.matroids.graphic_matroid.GraphicMatroid(G, groundset=None)

The graphic matroid class.

INPUT:

• G – a Graph
• groundset – (optional) a list in 1-1 correspondence with G.edge_iterator()

OUTPUT:

A GraphicMatroid instance where the ground set elements are the edges of G.

..NOTE:

If a disconnected graph is given as input, the instance of
GraphicMatroid will connect the graph components and store
this as its graph.

EXAMPLES:

sage: M = GraphicMatroid(graphs.BullGraph()); M
Graphic matroid of rank 4 on 5 elements
sage: N = GraphicMatroid(graphs.CompleteBipartiteGraph(3,3)); N
Graphic matroid of rank 5 on 9 elements

A disconnected input will get converted to a connected graph internally:

sage: G1 = graphs.CycleGraph(3); G2 = graphs.DiamondGraph()
sage: G = G1.disjoint_union(G2)
sage: len(G)
7
sage: G.is_connected()
False
sage: M = GraphicMatroid(G)
sage: M
Graphic matroid of rank 5 on 8 elements
sage: H = M.graph()
sage: H
Looped multi-graph on 6 vertices
sage: H.is_connected()
True
sage: M.is_connected()
False

You can still locate an edge using the vertices of the input graph:

sage: G1 = graphs.CycleGraph(3); G2 = graphs.DiamondGraph()
sage: G = G1.disjoint_union(G2)
sage: M = Matroid(G)
sage: H = M.graph()
sage: vm = M.vertex_map()
sage: (u, v, l) = G.random_edge()
sage: H.has_edge(vm[u], vm[v])
True
graph()

Return the graph that represents the matroid.

The graph will always have loops and multiedges enabled.

OUTPUT:

A Graph.

EXAMPLES:

sage: M = Matroid(Graph([(0, 1, 'a'), (0, 2, 'b'), (0, 3, 'c')]))
sage: M.graph().edges()
[(0, 1, 'a'), (0, 2, 'b'), (0, 3, 'c')]
sage: M = Matroid(graphs.CompleteGraph(5))
sage: M.graph()
Looped multi-graph on 5 vertices
graphic_coextension(u, v=None, X=None, element=None)

Return a matroid coextended by a new element.

A coextension in a graphic matroid is the opposite of contracting an edge; that is, a vertex is split, and a new edge is added between the resulting vertices. This method will create a new vertex $$v$$ adjacent to $$u$$, and move the edges indicated by $$X$$ from $$u$$ to $$v$$.

INPUT:

• u – the vertex to be split
• v – (optional) the name of the new vertex after splitting
• X – (optional) a list of the matroid elements corresponding to edges incident to u that move to the new vertex after splitting
• element – (optional) The name of the newly added element

OUTPUT:

An instance of GraphicMatroid coextended by the new element. If X is not specified, the new element will be a coloop.

Note

A loop on u will stay a loop unless it is in X.

EXAMPLES:

sage: G = Graph([(0, 1, 0), (0, 2, 1), (0, 3, 2), (0, 4, 3), (1, 2, 4), (1, 4, 5), (2, 3, 6), (3, 4, 7)])
sage: M = Matroid(G)
sage: M1 = M.graphic_coextension(0, X=[1,2], element='a')
sage: M1.graph().edges()
[(0, 1, 0),
(0, 4, 3),
(0, 5, 'a'),
(1, 2, 4),
(1, 4, 5),
(2, 3, 6),
(2, 5, 1),
(3, 4, 7),
(3, 5, 2)]
sage: M = Matroid(graphs.CycleGraph(3))
sage: M = M.graphic_coextension(u=2, element='a')
sage: M.graph()
Looped multi-graph on 4 vertices
sage: M.graph().loops()
[]
sage: M = M.graphic_coextension(u=2, element='a')
Traceback (most recent call last):
...
ValueError: cannot extend by element already in ground set
sage: M = M.graphic_coextension(u=4)
Traceback (most recent call last):
...
ValueError: u must be an existing vertex
sage: M = Matroid(range(5), graphs.DiamondGraph())
sage: N = M.graphic_coextension(u=3, v=5, element='a')
sage: N.graph().edges()
[(0, 1, 0), (0, 2, 1), (1, 2, 2), (1, 3, 3), (2, 3, 4), (3, 5, 'a')]
sage: N = M.graphic_coextension(u=3, element='a')
sage: N.graph().edges()
[(0, 1, 0), (0, 2, 1), (1, 2, 2), (1, 3, 3), (2, 3, 4), (3, 4, 'a')]
sage: N = M.graphic_coextension(u=3, v=3, element='a')
Traceback (most recent call last):
...
ValueError: u and v must be distinct
graphic_coextensions(vertices=None, v=None, element=None, cosimple=False)

Return an iterator of graphic coextensions.

This method iterates over the vertices in the input. If cosimple == False, it first coextends by a coloop and series edge for every edge incident with the vertices. For vertices of degree four or higher, it will consider the ways to partition the vertex into two sets of cardinality at least two, and these will be the edges incident with the vertices after splitting.

At most one series coextension will be taken for each series class.

INPUT:

• vertices – (optional) the vertices to be split
• v – (optional) the name of the new vertex
• element – (optional) the name of the new element
• cosimple – (default: False) if true, coextensions by a coloop or series elements will not be taken

OUTPUT:

An iterable containing instances of GraphicMatroid. If vertices is not specified, the method iterates over all vertices.

EXAMPLES:

sage: G = Graph([(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 4), (2, 3), (3, 4)])
sage: M = Matroid(range(8), G)
sage: I = M.graphic_coextensions(vertices=[0], element='a')
sage: for N in I:
....:     N.graph().edges_incident(0, sort=True)
[(0, 1, 0), (0, 2, 1), (0, 3, 2), (0, 4, 3), (0, 5, 'a')]
[(0, 2, 1), (0, 3, 2), (0, 4, 3), (0, 5, 'a')]
[(0, 1, 0), (0, 2, 1), (0, 3, 2), (0, 5, 'a')]
[(0, 1, 0), (0, 3, 2), (0, 4, 3), (0, 5, 'a')]
[(0, 1, 0), (0, 2, 1), (0, 4, 3), (0, 5, 'a')]
[(0, 2, 1), (0, 3, 2), (0, 5, 'a')]
[(0, 1, 0), (0, 3, 2), (0, 5, 'a')]
[(0, 1, 0), (0, 2, 1), (0, 5, 'a')]
sage: N = Matroid(range(4), graphs.CycleGraph(4))
sage: I = N.graphic_coextensions(element='a')
sage: for N1 in I:
....:     N1.graph().edges(sort=True)
[(0, 1, 0), (0, 3, 1), (0, 4, 'a'), (1, 2, 2), (2, 3, 3)]
[(0, 1, 0), (0, 3, 1), (1, 4, 2), (2, 3, 3), (2, 4, 'a')]
sage: sum(1 for n in N.graphic_coextensions(cosimple=True))
0
graphic_extension(u, v=None, element=None)

Return a graphic matroid extended by a new element.

A new edge will be added between u and v. If v is not specified, then a loop is added on u.

INPUT:

• u – a vertex in the matroid’s graph
• v – (optional) another vertex
• element – (optional) the label of the new element

OUTPUT:

A GraphicMatroid with the specified element added. Note that if v is not specifies or if v is u, then the new element will be a loop. If the new element’s label is not specified, it will be generated automatically.

EXAMPLES:

sage: M = matroids.CompleteGraphic(4)
sage: M1 = M.graphic_extension(0,1,'a'); M1
Graphic matroid of rank 3 on 7 elements
sage: M1.graph().edges()
[(0, 1, 0), (0, 1, 'a'), (0, 2, 1), (0, 3, 2), (1, 2, 3), (1, 3, 4), (2, 3, 5)]
sage: M2 = M1.graphic_extension(3); M2
Graphic matroid of rank 3 on 8 elements
sage: M = Matroid(range(10), graphs.PetersenGraph())
sage: M.graphic_extension(0, 'b', 'c').graph().vertices()
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'b']
sage: M.graphic_extension('a', 'b', 'c').graph().vertices()
Traceback (most recent call last):
...
ValueError: u must be an existing vertex
graphic_extensions(element=None, vertices=None, simple=False)

Return an iterable containing the graphic extensions.

This method iterates over the vertices in the input. If simple == False, it first extends by a loop. It will then add an edge between every pair of vertices in the input, skipping pairs of vertices with an edge already between them if simple == True.

This method only considers the current graph presentation, and does not take 2-isomorphism into account. Use twist or one_sum if you wish to change the graph presentation.

INPUT:

• element – (optional) the name of the newly added element in each extension
• vertices – (optional) a set of vertices over which the extension may be taken
• simple – (default: False) if true, extensions by loops and parallel elements are not taken

OUTPUT:

An iterable containing instances of GraphicMatroid. If vertices is not specified, every vertex is used.

Note

The extension by a loop will always occur unless simple == True. The extension by a coloop will never occur.

EXAMPLES:

sage: M = Matroid(range(5), graphs.DiamondGraph())
sage: I = M.graphic_extensions('a')
sage: for N in I:
....:     N.graph().edges()
[(0, 0, 'a'), (0, 1, 0), (0, 2, 1), (1, 2, 2), (1, 3, 3), (2, 3, 4)]
[(0, 1, 0), (0, 1, 'a'), (0, 2, 1), (1, 2, 2), (1, 3, 3), (2, 3, 4)]
[(0, 1, 0), (0, 2, 1), (0, 2, 'a'), (1, 2, 2), (1, 3, 3), (2, 3, 4)]
[(0, 1, 0), (0, 2, 1), (0, 3, 'a'), (1, 2, 2), (1, 3, 3), (2, 3, 4)]
[(0, 1, 0), (0, 2, 1), (1, 2, 2), (1, 2, 'a'), (1, 3, 3), (2, 3, 4)]
[(0, 1, 0), (0, 2, 1), (1, 2, 2), (1, 3, 3), (1, 3, 'a'), (2, 3, 4)]
[(0, 1, 0), (0, 2, 1), (1, 2, 2), (1, 3, 3), (2, 3, 4), (2, 3, 'a')]
sage: M = Matroid(graphs.CompleteBipartiteGraph(3,3))
sage: I = M.graphic_extensions(simple=True)
sage: sum (1 for i in I)
6
sage: I = M.graphic_extensions(vertices=[0,1,2])
sage: sum (1 for i in I)
4
groundset()

Return the ground set of the matroid as a frozenset.

EXAMPLES:

sage: M = Matroid(graphs.DiamondGraph())
sage: sorted(M.groundset())
[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3)]
sage: G = graphs.CompleteGraph(3).disjoint_union(graphs.CompleteGraph(4))
sage: M = Matroid(range(G.num_edges()), G); sorted(M.groundset())
[0, 1, 2, 3, 4, 5, 6, 7, 8]
sage: M = Matroid(Graph([(0, 1, 'a'), (0, 2, 'b'), (0, 3, 'c')]))
sage: sorted(M.groundset())
['a', 'b', 'c']
groundset_to_edges(X)

Return a list of edges corresponding to a set of ground set elements.

INPUT:

• X – a subset of the ground set

OUTPUT:

A list of graph edges.

EXAMPLES:

sage: M = Matroid(range(5), graphs.DiamondGraph())
sage: M.groundset_to_edges([2,3,4])
[(1, 2, 2), (1, 3, 3), (2, 3, 4)]
sage: M.groundset_to_edges([2,3,4,5])
Traceback (most recent call last):
...
ValueError: input must be a subset of the ground set
is_valid()

Test if the data obey the matroid axioms.

Since a graph is used for the data, this is always the case.

OUTPUT:

True.

EXAMPLES:

sage: M = matroids.CompleteGraphic(4); M
M(K4): Graphic matroid of rank 3 on 6 elements
sage: M.is_valid()
True
one_sum(X, u, v)

Arrange matroid components in the graph.

The matroid’s graph must be connected even if the matroid is not connected, but if there are multiple matroid components, the user may choose how they are arranged in the graph. This method will take the block of the graph that represents $$X$$ and attach it by vertex $$u$$ to another vertex $$v$$ in the graph.

INPUT:

• X – a subset of the ground set
• u – a vertex spanned by the edges of the elements in X
• v – a vertex spanned by the edges of the elements not in X

OUTPUT:

An instance of GraphicMatroid isomorphic to this matroid but with a graph that is not necessarily isomorphic.

EXAMPLES:

sage: edgedict = {0:[1, 2], 1:[2, 3], 2:[3], 3:[4, 5], 6:[4, 5]}
sage: M = Matroid(range(9), Graph(edgedict))
sage: M.graph().edges()
[(0, 1, 0),
(0, 2, 1),
(1, 2, 2),
(1, 3, 3),
(2, 3, 4),
(3, 4, 5),
(3, 5, 6),
(4, 6, 7),
(5, 6, 8)]
sage: M1 = M.one_sum(u=3, v=1, X=[5, 6, 7, 8])
sage: M1.graph().edges()
[(0, 1, 0),
(0, 2, 1),
(1, 2, 2),
(1, 3, 3),
(1, 4, 5),
(1, 5, 6),
(2, 3, 4),
(4, 6, 7),
(5, 6, 8)]
sage: M2 = M.one_sum(u=4, v=3, X=[5, 6, 7, 8])
sage: M2.graph().edges()
[(0, 1, 0),
(0, 2, 1),
(1, 2, 2),
(1, 3, 3),
(2, 3, 4),
(3, 6, 7),
(3, 7, 5),
(5, 6, 8),
(5, 7, 6)]
sage: M = Matroid(range(5), graphs.BullGraph())
sage: M.graph().edges()
[(0, 1, 0), (0, 2, 1), (1, 2, 2), (1, 3, 3), (2, 4, 4)]
sage: M1 = M.one_sum(u=3, v=0, X=[3,4])
Traceback (most recent call last):
...
ValueError: too many vertices in the intersection

sage: M1 = M.one_sum(u=3, v=2, X=[3])
sage: M1.graph().edges()
[(0, 1, 0), (0, 2, 1), (1, 2, 2), (2, 4, 4), (2, 5, 3)]

sage: M2 = M1.one_sum(u=5, v=0, X=[3,4])
sage: M2.graph().edges()
[(0, 1, 0), (0, 2, 1), (0, 3, 3), (1, 2, 2), (3, 4, 4)]

sage: M = Matroid(range(5), graphs.BullGraph())
sage: M.one_sum(u=0, v=1, X=[3])
Traceback (most recent call last):
...
ValueError: first vertex must be spanned by the input

sage: M.one_sum(u=1, v=3, X=[3])
Traceback (most recent call last):
...
ValueError: second vertex must be spanned by the rest of the graph
regular_matroid()

Return an instance of RegularMatroid isomorphic to this GraphicMatroid.

EXAMPLES:

sage: M = matroids.CompleteGraphic(5); M
M(K5): Graphic matroid of rank 4 on 10 elements
sage: N = M.regular_matroid(); N
Regular matroid of rank 4 on 10 elements with 125 bases
sage: M.equals(N)
True
sage: M == N
False
subgraph_from_set(X)

Return the subgraph corresponding to the matroid restricted to $$X$$.

INPUT:

• X – a subset of the ground set

OUTPUT:

A Graph.

EXAMPLES:

sage: M = Matroid(range(5), graphs.DiamondGraph())
sage: M.subgraph_from_set([0,1,2])
Looped multi-graph on 3 vertices
sage: M.subgraph_from_set([3,4,5])
Traceback (most recent call last):
...
ValueError: input must be a subset of the ground set
twist(X)

Perform a Whitney twist on the graph.

$$X$$ must be part of a 2-separation. The connectivity of $$X$$ must be 1, and the subgraph induced by $$X$$ must intersect the subgraph induced by the rest of the elements on exactly two vertices.

INPUT:

• X – the set of elements to be twisted with respect to the rest of the matroid

OUTPUT:

An instance of GraphicMatroid isomorphic to this matroid but with a graph that is not necessarily isomorphic.

EXAMPLES:

sage: edgelist = [(0,1,0), (1,2,1), (1,2,2), (2,3,3), (2,3,4), (2,3,5), (3,0,6)]
sage: M = Matroid(Graph(edgelist, multiedges=True))
sage: M1 = M.twist([0,1,2]); M1.graph().edges()
[(0, 1, 1), (0, 1, 2), (0, 3, 6), (1, 2, 0), (2, 3, 3), (2, 3, 4), (2, 3, 5)]
sage: M2 = M.twist([0,1,3])
Traceback (most recent call last):
...
ValueError: the input must display a 2-separation that is not a 1-separation
vertex_map()

Return a dictionary mapping the input vertices to the current vertices.

The graph for the matroid is alway connected. If the constructor is given a graph with multiple components, it will connect them. The Python dictionary given by this method has the vertices from the input graph as keys, and the corresponding vertex label after any merging as values.

OUTPUT:

A dictionary.

EXAMPLES:

sage: G = Graph([(0, 1), (0, 2), (1, 2), (3, 4), (3, 5), (4, 5),
....: (6, 7), (6, 8), (7, 8), (8, 8), (7, 8)], multiedges=True, loops=True)
sage: M = Matroid(range(G.num_edges()), G)
sage: M.graph().edges()
[(0, 1, 0),
(0, 2, 1),
(1, 2, 2),
(2, 4, 3),
(2, 5, 4),
(4, 5, 5),
(5, 7, 6),
(5, 8, 7),
(7, 8, 8),
(7, 8, 9),
(8, 8, 10)]
sage: M.vertex_map()
{0: 0, 1: 1, 2: 2, 3: 2, 4: 4, 5: 5, 6: 5, 7: 7, 8: 8}