|
| 1 | +using Convex |
| 2 | +using SCS |
| 3 | +using LinearAlgebra |
| 4 | + |
| 5 | +" |
| 6 | +The Goemans-Williamson algorithm for the MAXCUT problem. |
| 7 | +
|
| 8 | +From: https://github.com/ericproffitt/MaxCut |
| 9 | +" |
| 10 | + |
| 11 | +function goemansWilliamson(W::Matrix{T}; tol::Real=1e-1, iter::Int=100) where T<:Real |
| 12 | + "Partition a graph into two disjoint sets such that the sum of the edge weights |
| 13 | + which cross the partition is as large as possible (known to be NP-hard)." |
| 14 | + |
| 15 | + "A cut of a graph can be produced by assigning either 1 or -1 to each vertex. The Goemans-Williamson |
| 16 | + algorithm relaxes this binary condition to allow for vector assignments drawn from the (n-1)-sphere |
| 17 | + (choosing an n-1 dimensional space will ensure seperability). This relaxation can then be written as |
| 18 | + an SDP. Once the optimal vector assignments are found, origin centered hyperplanes are generated and |
| 19 | + their corresponding cuts evaluated. After 'iter' trials, or when the desired tolerance is reached, |
| 20 | + the hyperplane with the highest corresponding binary cut is used to partition the vertices." |
| 21 | + |
| 22 | + "W: Adjacency matrix." |
| 23 | + "tol: Maximum acceptable distance between a cut and the MAXCUT upper bound." |
| 24 | + "iter: Maximum number of hyperplane iterations before a cut is chosen." |
| 25 | + |
| 26 | + LinearAlgebra.checksquare(W) |
| 27 | + @assert LinearAlgebra.issymmetric(W) "Adjacency matrix must be symmetric." |
| 28 | + @assert all(W .>= 0) "Entries of the adjacency matrix must be nonnegative." |
| 29 | + @assert all(diag(W) .== 0) "Diagonal entries of adjacency matrix must be zero." |
| 30 | + @assert tol > 0 "The tolerance 'tol' must be positive." |
| 31 | + @assert iter > 0 "The number of iterations 'iter' must be a positive integer." |
| 32 | + |
| 33 | + "This is the standard SDP Relaxation of the MAXCUT problem, a reference can be found at |
| 34 | + http://www.sfu.ca/~mdevos/notes/semidef/GW.pdf." |
| 35 | + k = size(W, 1) |
| 36 | + S = Semidefinite(k) |
| 37 | + |
| 38 | + expr = dot(W, S) |
| 39 | + constr = [S[i,i] == 1.0 for i in 1:k] |
| 40 | + problem = minimize(expr, constr...) |
| 41 | + solve!(problem, SCSSolver(verbose=0)) |
| 42 | + |
| 43 | + ### Ensure symmetric positive-definite. |
| 44 | + A = 0.5 * (S.value + S.value') |
| 45 | + A += (max(0, -eigmin(A)) + eps(1e3))*I |
| 46 | + |
| 47 | + X = Matrix(cholesky(A)) |
| 48 | + |
| 49 | + ### A non-trivial upper bound on MAXCUT. |
| 50 | + upperbound = (sum(W) - dot(W, S.value)) / 4 |
| 51 | + |
| 52 | + "Random origin-centered hyperplanes, generated to produce partitions of the graph." |
| 53 | + maxcut = 0 |
| 54 | + maxpartition = nothing |
| 55 | + |
| 56 | + for i in 1:iter |
| 57 | + gweval = X' * randn(k) |
| 58 | + partition = (findall(x->x>0, gweval), findall(x->x<0, gweval)) |
| 59 | + cut = sum(W[partition...]) |
| 60 | + |
| 61 | + if cut > maxcut |
| 62 | + maxpartition = partition |
| 63 | + maxcut = cut |
| 64 | + end |
| 65 | + |
| 66 | + upperbound - maxcut < tol && break |
| 67 | + i == iter && println("Max iterations reached.") |
| 68 | + end |
| 69 | + return round(maxcut; digits=3), maxpartition |
| 70 | +end |
| 71 | + |
| 72 | +using Test |
| 73 | +@testset "max cut" begin |
| 74 | + W = [0 5 2 1 0; |
| 75 | + 5 0 3 2 0; |
| 76 | + 2 3 0 0 0; |
| 77 | + 1 2 0 0 4; |
| 78 | + 0 0 0 4 0] |
| 79 | + |
| 80 | + maxcut, maxpartition = goemansWilliamson(W) |
| 81 | + @test maxcut == 14 |
| 82 | + @test [2,5] in maxpartition |
| 83 | +end |
0 commit comments