Simple and Hyper Graphs#

Stochastic Matchings allows to build arbitrary models from their graph adjacency or incidence matrix, but also to directly instantiate some families. This tutorial gives a tour of these families and details the graph behavior of a Model instance.

First, we load the package.

[1]:
import stochastic_matching as sm

Simple graph: manual definition and basic usage#

A simple graph can be defined by providing its adjacency or incidence matrix. We give a tour of the possibilities using the diamond graph as running example.

[2]:
diamond_adjacency = [[0, 1, 1, 0], [1, 0, 1, 1], [1, 1, 0, 1], [0, 1, 1, 0]]
diamond = sm.Model(adjacency=diamond_adjacency)
diamond.show_graph(vis_options={"height": 200})
Reload

If you input an incidence matrix, the graph is treated as a hypergraph.

[3]:
diamond_incidence = [[1, 1, 0, 0, 0], [1, 0, 1, 1, 0], [0, 1, 1, 0, 1], [0, 0, 0, 1, 1]]

diamond = sm.Model(incidence=diamond_incidence)
diamond.show_graph(vis_options={"height": 200})
Reload

A Model possesses many attributes.

Number of edges and nodes:

[4]:
diamond.n, diamond.m
[4]:
(4, 5)

Adjacency matrix does not exist for hypergraphs:

[5]:
diamond.adjacency
[6]:
diamond = sm.Model(adjacency=diamond_adjacency)
diamond.adjacency
[6]:
array([[0, 1, 1, 0],
       [1, 0, 1, 1],
       [1, 1, 0, 1],
       [0, 1, 1, 0]])

Incidence (nodes X edges):

[7]:
diamond.incidence
[7]:
array([[1, 1, 0, 0, 0],
       [1, 0, 1, 1, 0],
       [0, 1, 1, 0, 1],
       [0, 0, 0, 1, 1]])

As already seen, you can visualize the graph with the show_graph method.

[8]:
diamond.show_graph()
Reload

You can customize display with vis_options (the options will be pass to the vis engine). For example, to display a red graph with no navigation button:

[9]:
diamond.show_graph(
    vis_options={
        "nodes": {"color": "red"},
        "height": 200,
        "interaction": {"navigationButtons": True}
    }
)
Reload

Names can be specified.

[10]:
diamond.names = ["Ga", "Bu", "Zo", "Meu"]
diamond.show_graph(vis_options={"height": 200})
Reload

Automatic letter labeling can be obtained using alpha as input for names

[11]:
diamond.names = "alpha"
diamond.show_graph(vis_options={"height": 200})
Reload

Pre-defined models#

For convenience, many graph families are provided. You can have the list with the get_class method.

[12]:
sm.common.get_classes(sm.Model)
[12]:
{'Path': stochastic_matching.graphs.Path,
 'Star': stochastic_matching.graphs.Star,
 'Cycle': stochastic_matching.graphs.Cycle,
 'Complete': stochastic_matching.graphs.Complete,
 'Codomino': stochastic_matching.graphs.Codomino,
 'Erdös-Rényi': stochastic_matching.graphs.ErdosRenyi,
 'Nazari-Stolyar': stochastic_matching.graphs.NS19,
 'Pyramid': stochastic_matching.graphs.Pyramid,
 'Tadpole': stochastic_matching.graphs.Tadpole,
 'Lollipop': stochastic_matching.graphs.Lollipop,
 'Kayak Paddle': stochastic_matching.graphs.KayakPaddle,
 'Barbell': stochastic_matching.graphs.Barbell,
 'Cycle Chain': stochastic_matching.graphs.CycleChain,
 'Hyper Kayak Paddle': stochastic_matching.graphs.HyperPaddle,
 'Fan': stochastic_matching.graphs.Fan}

Path graphs#

Paths are defined by a parameter \(n\) (the size, e.g. number of nodes) that defaults to 2.

[13]:
sm.Path().show_graph(vis_options={"height": 200})
Reload
[14]:
sm.Path(names="alpha", n=5).show_graph(vis_options={"height": 200})
Reload

Cycle graphs#

Cycles are defined by a parameter \(n\) that defaults to 3.

[15]:
sm.Cycle().show_graph(vis_options={"height": 200})
Reload
[16]:
sm.Cycle(names="alpha", n=5).show_graph(vis_options={"height": 200})
Reload

Star graphs#

Stars are defined by a parameter \(n\) (the number of nodes) that defaults to 4.

[17]:
sm.Star().show_graph(vis_options={"height": 200})
Reload
[18]:
sm.Star(names="alpha", n=5).show_graph(vis_options={"height": 200})
Reload

Complete graphs#

Complete graphs are defined by a parameter \(n\) that defaults to 3.

[19]:
sm.Complete().show_graph(vis_options={"height": 200})
Reload
[20]:
sm.Complete(names="alpha", n=5).show_graph(vis_options={"height": 200})
Reload

Tadpole graphs#

A tadpole combines a cycle of length \(m\) and a path of length \(n\). Default is the paw graph (\(m=3, n=1\))

[21]:
# Paw graph
sm.Tadpole().show_graph(vis_options={"height": 200})
Reload
[22]:
sm.Tadpole(n=4).show_graph(vis_options={"height": 200})
Reload
[23]:
# Banner graph
sm.Tadpole(m=4).show_graph(vis_options={"height": 200})
Reload
[24]:
# Ursa major
sm.Tadpole(
    n=3, m=4, names=["Dubhe", "Merak", "Phecda", "Megrez", "Alioth", "Mizar", "Alkaid"]
).show_graph(vis_options={"height": 400})
Reload

Lollipop graphs#

A lollipop combines a complete graph of size \(m\) and a path of length \(n\). Default is the paw graph (\(m=3, n=1\))

[25]:
# Paw graph
sm.Lollipop().show_graph(vis_options={"height": 200})
Reload
[26]:
sm.Lollipop(m=4).show_graph(vis_options={"height": 200})
Reload
[27]:
sm.Lollipop(n=3, m=5).show_graph(vis_options={"height": 400})
Reload

Kayak paddle graphs#

Combines a cycle of length \(k\), a path of length \(l\), and a cycle of length \(m\). Defaults to \(k=3, l=1, m=k\).

[28]:
sm.KayakPaddle().show_graph(vis_options={"height": 200})
Reload
[29]:
sm.KayakPaddle(5, 2).show_graph(vis_options={"height": 300})
Reload
[30]:
## Fish graph
sm.KayakPaddle(l=0, m=4).show_graph(vis_options={"height": 300})
Reload

Barbell graph#

Combines a complete graph of size \(k\), a path of length \(l\), and a complete graph of size \(m\). Defaults to \(k=3, l=1, m=k\).

[31]:
sm.Barbell().show_graph(vis_options={"height": 200})
Reload
[32]:
sm.Barbell(5, 2).show_graph(vis_options={"height": 300})
Reload
[33]:
sm.Barbell(l=0, m=4).show_graph(vis_options={"height": 300})
Reload

Chained cycle graphs#

Concatenates cycles of size \(n\), \(c\) of them. Each cycle has \(o\) nodes that overlap with the next one. Defaults to \(n=3, c=2, o=2\).

[34]:
# Diamond
sm.CycleChain().show_graph(vis_options={"height": 200})
Reload
[35]:
# Triamond
sm.CycleChain(c=3).show_graph(vis_options={"height": 300})
Reload
[36]:
# Domino
sm.CycleChain(n=4).show_graph(vis_options={"height": 300})
Reload
[37]:
# The triangular snake graph TS_9 (cf https://mathworld.wolfram.com/TriangularSnakeGraph.html)
sm.CycleChain(c=4, o=1).show_graph(vis_options={"height": 400})
Reload
[38]:
# 3 separated squares
sm.CycleChain(n=4, c=3, o=0).show_graph(vis_options={"height": 300})
Reload
[39]:
# A big chain of triangle
sm.CycleChain(c=30, names="alpha").show_graph()
Reload

Erdös-Rényi graph#

Standard random graph with default parameters \(n=10\), \(d=3\), \(seed=None\).

[40]:
sm.ErdosRenyi(seed=42).show_graph(vis_options={"height": 300})
Reload
[41]:
sm.ErdosRenyi(n=20, d=2, seed=42).show_graph(vis_options={"height": 400})
Reload

Concatenation#

Building more elaborate graphs on top of the provided generators is relatively easy.).

One way to build these graphs is to use concatenation.

Concatenation generalizes the chained cycles above. It uses a list of graphs and the overlapping between graph. Overlapping can be uniform or heterogeneous (you need then to provide a list of overlaps).

[42]:
# House
sm.concatenate([sm.Cycle(), sm.Cycle(4)], 2).show_graph(vis_options={"height": 200})
Reload
[43]:
# Barred house
sm.concatenate([sm.Cycle(), sm.Complete(4)], 2).show_graph(vis_options={"height": 200})
Reload
[44]:
# House with a triangle on top
sm.concatenate([sm.Cycle(), sm.Cycle(), sm.Cycle(4)], [1, 2]).show_graph(
    vis_options={"height": 200}
)
Reload

For some examples proposed in https://hal.archives-ouvertes.fr/hal-03502084, direct shortcut is provided:

  • the co-domino graph;

  • the Egyptian pyramid graph (please see the above paper for the origin of the name.

[45]:
# Codomino
sm.Codomino().show_graph(vis_options={"height": 200})
Reload
[46]:
# Pyramid
sm.Pyramid().show_graph(vis_options={"height": 200})
Reload

Hypergraphs#

Hypergraph egdes can link an arbitrary number of nodes (nor necessarily 2). Hypergraphs mainly differ from simple graphs by the absence of adjacency matrix (only the incidence matrix is used) and a different way of been displayed.

Simple graphs as hypergraphs#

All simple graphs are hypergraphs if you remove the adjacency.

[49]:
paw = sm.Tadpole()
paw.adjacency = None
paw.show_graph(vis_options={"height": 200})
Reload

Note

Hypergraph convention: edges are represented by small black nodes, and the edges displayed alway link one single node to one edge. Also note that it is possible to make the bipartite structure between nodes and edges more apparent by passing the bipartite flag.

[50]:
paw.show_graph(bipartite=True, vis_options={"height": 200})
Reload

Nazari-Stolyar example#

Hypergraph from https://arxiv.org/pdf/1608.01646 (no parameter).

[51]:
sm.NS19().show_graph(vis_options={"height": 300})
Reload

Hyper paddle#

A variation of the regular paddle where the two cycles are linked by 3-egdes. Default to \(k=3\), \(m=3\), \(l=1\).

The default version, named Candy graph, is one of the simplest graph for which one can find stabilizable rates that no greedy policy can stabilize.

[52]:
candy = sm.HyperPaddle()
candy.show_graph()
Reload

A larger version.

[53]:
poodle = sm.HyperPaddle(k=4, l=3)
poodle.show_graph()
Reload

Fans#

Fans are cycles linked by hyperedges. Default to \(cycles=3\), \(cycle_size=3\), \(hyperedges=1\).

Fans are useful to study the equivalent of bipartite graphs (non-trivial left kernel) in the hypergraphs world.

The default fan is called a clover.

[54]:
sm.Fan().show_graph()
Reload

Next one is a highly degenerated example (as shown in another notebook)!

[55]:
sm.Fan(cycle_size=4, hyperedges=2).show_graph()
Reload