Skip to content

Commit 5b35944

Browse files
committed
new QAOA example
1 parent 7ccbdcf commit 5b35944

File tree

2 files changed

+159
-0
lines changed

2 files changed

+159
-0
lines changed

examples/QAOA.jl

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using Yao, Yao.Blocks
2+
using NLopt
3+
4+
include("maxcut_gw.jl")
5+
6+
HB(nbit::Int) = sum([put(nbit, i=>X) for i=1:nbit])
7+
tb = TimeEvolution(HB(3), 0.1)
8+
function HC(W::AbstractMatrix)
9+
nbit = size(W, 1)
10+
ab = add(nbit)
11+
for i=1:nbit,j=i+1:nbit
12+
if W[i,j] != 0
13+
push!(ab, 0.5*W[i,j]*repeat(nbit, Z, [i,j]))
14+
end
15+
end
16+
ab
17+
end
18+
19+
function qaoa_circuit(W::AbstractMatrix, depth::Int; use_cache::Bool=false)
20+
nbit = size(W, 1)
21+
hb = HB(nbit)
22+
hc = HC(W)
23+
use_cache && (hb = hb |> cache; hc = hc |> cache)
24+
c = chain(nbit, [repeat(nbit, H, 1:nbit)])
25+
append!(c, [chain(nbit, [timeevolve(hc, 0.0, tol=1e-5), timeevolve(hb, 0.0, tol=1e-5)]) for i=1:depth])
26+
end
27+
28+
29+
function cobyla_optimize(circuit::MatrixBlock{N}, hc::MatrixBlock; niter::Int) where N
30+
function f(params, grad)
31+
reg = zero_state(N) |> dispatch!(circuit, params)
32+
loss = expect(hc, reg) |> real
33+
#println(loss)
34+
loss
35+
end
36+
opt = Opt(:LN_COBYLA, nparameters(circuit))
37+
min_objective!(opt, f)
38+
maxeval!(opt, niter)
39+
cost, params, info = optimize(opt, parameters(circuit))
40+
pl = zero_state(N) |> circuit |> probs
41+
cost, params, pl
42+
end
43+
44+
using Random, Test
45+
@testset "qaoa circuit" begin
46+
Random.seed!(2)
47+
nbit = 5
48+
W = [0 5 2 1 0;
49+
5 0 3 2 0;
50+
2 3 0 0 0;
51+
1 2 0 0 4;
52+
0 0 0 4 0]
53+
54+
# the exact solution
55+
exact_cost, sets = goemansWilliamson(W)
56+
57+
hc = HC(W)
58+
@test ishermitian(hc)
59+
# the actual loss is -<Hc> + sum(wij)/2
60+
@test -expect(hc, product_state(5, bmask(sets[1]...)))+sum(W)/4 exact_cost
61+
62+
# build a QAOA circuit and start training.
63+
qc = qaoa_circuit(W, 20; use_cache=false)
64+
opt_pl = nothing
65+
opt_cost = Inf
66+
for i = 1:10
67+
dispatch!(qc, :random)
68+
cost, params, pl = cobyla_optimize(qc, hc; niter=2000)
69+
@show cost
70+
cost < opt_cost && (opt_pl = pl)
71+
end
72+
73+
# check the correctness of result
74+
config = argmax(opt_pl)-1
75+
@test config in [bmask(set...) for set in sets]
76+
end

examples/maxcut_gw.jl

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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

Comments
 (0)