This demonstrates spectral partitioning. For this. the lower eigenvectors of the Laplacian are used, since verices "near" each other tend to have similar entries in these. The graph is then recursively bisected, using the next bigger eigenvector, until the desired number of partitions is reached.
In [1]:
import networkit
First, we partition some random graph. We try to partition into 10 partitions, and we aim to have them as well balanced as possible. This means that he bisection will use the median of the eigenvector's entries to bisect. In the literature, the approach of using the mean instead can be found. This can be accomplished by setting the balanced parameter to False.
In [2]:
G = networkit.generators.ErdosRenyiGenerator(1000, 0.003).generate()
partitioner = networkit.partitioning.SpectralPartitioner(G, 10, balanced=True)
partitioner.run()
zeta = partitioner.getPartition()
In [3]:
networkit.partitioning.inspectPartitions(zeta, G)
Unfortunately, random graphs do not behave incredibly well. As you can see, the edge cut is rather high. Note: The imbalance will probably not be 1 (as it should be in a balanced case). This is because a clean bisection is not always possible, especially for rather small graphs and cases where the desired number of partitions is not a power of two.
Let's try some mesh-like graphs instead (this takes a while):
In [5]:
G = networkit.graphio.readGraph("../../input/airfoil1.graph")
partitioner = networkit.partitioning.SpectralPartitioner(G, 8)
partitioner.run()
zeta = partitioner.getPartition()
networkit.partitioning.inspectPartitions(zeta, G)
Nice results! But wait.. we have an algorithm for spectral drawing lying around, right? (See other notebook...) I wonder what happens if I combine both...
In [6]:
colors = { v: (zeta[v] / zeta.numberOfSubsets()) for v in G.nodes() }
layout = networkit.viztools.layout.SpectralLayout()
networkit.viztasks.drawGraph(G, layout=layout, figsize=(15,15), coloring = colors, nodeSizes = [35.0]*G.numberOfNodes())
Nice! Let's try another...
In [7]:
G = networkit.graphio.readGraph("../../input/wing.graph")
partitioner = networkit.partitioning.SpectralPartitioner(G, 4)
partitioner.run()
zeta = partitioner.getPartition()
networkit.partitioning.inspectPartitions(zeta, G)
In [9]:
colors = { v: (zeta[v] / zeta.numberOfSubsets()) for v in G.nodes() }
layout = networkit.viztools.layout.SpectralLayout()
networkit.viztasks.drawGraph(G, layout=layout, figsize=(15,15), coloring = colors, nodeSizes = [35.0]*G.numberOfNodes())
Here we can observe one very nice fact: Because the two smallest eigenvectors of the Laplacian are used for partitioning as well as for drawing, the two first cuts (forming the first 4 partitions) will always be more or less vertical and horizontal.
And another one, for fun!
In [8]:
G = networkit.graphio.readGraph("../../input/4elt.graph")
partitioner = networkit.partitioning.SpectralPartitioner(G, 16)
partitioner.run()
zeta = partitioner.getPartition()
networkit.partitioning.inspectPartitions(zeta, G)
In [9]:
colors = { v: (zeta[v] / zeta.numberOfSubsets()) for v in G.nodes() }
layout = networkit.viztools.layout.SpectralLayout()
networkit.viztasks.drawGraph(G, layout=layout, figsize=(15,15), coloring = colors, nodeSizes = [35.0]*G.numberOfNodes())
And a last one... the best known cut for this one (while still being balanced and 16 partitions) is 1000, by the way. See the Internet Graph Archive.
In [ ]:
G = networkit.graphio.readGraph("../../input/fe_4elt2.graph")
partitioner = networkit.partitioning.SpectralPartitioner(G, 16)
partitioner.run()
zeta = partitioner.getPartition()
networkit.partitioning.inspectPartitions(zeta, G)
In [ ]:
colors = { v: (zeta[v] / zeta.numberOfSubsets()) for v in G.nodes() }
layout = networkit.viztools.layout.SpectralLayout()
networkit.viztasks.drawGraph(G, layout=layout, figsize=(15,15), coloring = colors, nodeSizes = [35.0]*G.numberOfNodes())
In [ ]:
G = NetworKit.graph.Graph(10000)
vertices = {}
for i in range(0, 100):
for j in range(0,100):
# Top
if j > 0:
G.addEdge((i * 100 + j), (i * 100 + j - 1))
# left
if i > 0:
G.addEdge((i * 100 + j), ((i - 1) * 100 + j))
partitioner = networkit.partitioning.SpectralPartitioner(G, 16)
partitioner.run()
zeta = partitioner.getPartition()
networkit.partitioning.inspectPartitions(zeta, G)
In [ ]:
colors = { v: (zeta[v] / zeta.numberOfSubsets()) for v in G.nodes() }
layout = networkit.viztools.layout.SpectralLayout()
networkit.viztasks.drawGraph(G, layout=layout, figsize=(15,15), coloring = colors, nodeSizes = [35.0]*G.numberOfNodes())
In [ ]: