Maximum Cut Problem
Overview
A cut in graph theory divides vertices into two disjoint subsets. The size of a cut is measured by the number of edges (or sum of edge weights) that cross between the subsets. The Maximum Cut (MaxCut) problem seeks to find a cut with the largest possible size.
Key concepts covered:
- Finding maximum cuts
- Computing cut polynomials
- Visualizing cut configurations
This example uses the Petersen graph to demonstrate these concepts.
using GenericTensorNetworks, Graphs
Create a Petersen graph instance
graph = Graphs.smallgraph(:petersen)
{10, 15} undirected simple Int64 graph
Define vertex layout for visualization
rot15(a, b, i::Int) = cos(2i*π/5)*a + sin(2i*π/5)*b, cos(2i*π/5)*b - sin(2i*π/5)*a
locations = [[rot15(0.0, 60.0, i) for i=0:4]..., [rot15(0.0, 30, i) for i=0:4]...]
show_graph(graph, locations; format=:svg)
Tensor Network Formulation
Define the MaxCut problem using tensor networks:
maxcut = MaxCut(graph)
MaxCut{Int64, UnitWeight}(SimpleGraph{Int64}(15, [[2, 5, 6], [1, 3, 7], [2, 4, 8], [3, 5, 9], [1, 4, 10], [1, 8, 9], [2, 9, 10], [3, 6, 10], [4, 6, 7], [5, 7, 8]]), [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
The objective is to maximize the number of edges crossing the cut
objectives(maxcut)
15-element Vector{ProblemReductions.LocalSolutionSize{Int64}}:
LocalSolutionSize{Int64} on [1, 2]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [1, 5]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [1, 6]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [2, 3]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [2, 7]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [3, 4]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [3, 8]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [4, 5]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [4, 9]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [5, 10]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [6, 8]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [6, 9]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [7, 9]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [7, 10]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
LocalSolutionSize{Int64} on [8, 10]
┌───────────────┬──────┐
│ Configuration │ Size │
├───────────────┼──────┤
│ [0, 0] │ 0 │
│ [1, 0] │ 1 │
│ [0, 1] │ 1 │
│ [1, 1] │ 0 │
└───────────────┴──────┘
Convert to tensor network representation:
problem = GenericTensorNetwork(maxcut)
GenericTensorNetwork{MaxCut{Int64, UnitWeight}, OMEinsum.DynamicNestedEinsum{Int64}, Int64}
- open vertices: Int64[]
- fixed vertices: Dict{Int64, Int64}()
- contraction time = 2^7.807, space = 2^4.0, read-write = 2^8.379
Mathematical Background
For a graph $G=(V,E)$, we assign a boolean variable $s_v ∈ \{0,1\}$ to each vertex, indicating which subset it belongs to. The tensor network uses:
For edge $(i,j)$ with weight $w_{ij}$:
\[B(x_i, x_j, w_{ij}) = \begin{pmatrix} 1 & x_i^{w_{ij}} \\ x_j^{w_{ij}} & 1 \end{pmatrix}\]
- Contributes $x_i^{w_{ij}}$ or $x_j^{w_{ij}}$ when vertices are in different subsets
The contraction complexity is $O(2^{tw(G)})$, where $tw(G)$ is the graph's tree-width.
Solution Analysis
1. Maximum Cut Size
Find the size of the maximum cut:
max_cut_size = solve(problem, SizeMax())[]
12.0ₜ
2. Cut Polynomial
The cut polynomial $C(G,x) = \sum_i c_i x^i$ counts cuts by size, where $c_i/2$ is the number of cuts of size $i$
max_config = solve(problem, GraphPolynomial())[]
2 + 20∙x3 + 30∙x4 + 72∙x5 + 200∙x6 + 240∙x7 + 150∙x8 + 120∙x9 + 120∙x10 + 60∙x11 + 10∙x123. Maximum Cut Configuration
Find one maximum cut solution:
max_vertex_config = read_config(solve(problem, SingleConfigMax())[])
0101010001
Verify the cut size matches our earlier computation:
max_cut_size_verify = cut_size(graph, max_vertex_config)
12
Visualize the maximum cut:
show_graph(graph, locations; vertex_colors=[
iszero(max_vertex_config[i]) ? "white" : "red" for i=1:nv(graph)], format=:svg)
Note: Red and white vertices represent the two disjoint subsets of the cut
More APIs
The Independent Set Problem chapter has more examples on how to use the APIs.
This page was generated using Literate.jl.