aboutsummaryrefslogtreecommitdiff
path: root/main.py
blob: 18d33985a4298578aa8662648cbfa7791f0eb720 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import pennylane as qml
from pennylane import numpy as np

n_vertices = 5
n_wires = 2*n_vertices

graph = [(0,1),(0,2),(0,3),(0,4),(1,2),(1,3),(2,3),(2,4),(3,4)]
# graph = [(0,2)]

cost_hamiltonian = sum(qml.Hamiltonian([1,1,1], [
    qml.PauliZ(2*edge[0]) @ qml.PauliZ(2*edge[1]),
    qml.PauliZ(2*edge[0]+1) @ qml.PauliZ(2*edge[1]+1),
    qml.PauliZ(2*edge[0]) @ qml.PauliZ(2*edge[0]+1) @ qml.PauliZ(2*edge[1]) @ qml.PauliZ(2*edge[1]+1)
]) for edge in graph)

# unitary operator U_B with parameter beta
def U_B(beta):
    for wire in range(n_wires):
        qml.RX(2 * beta, wires=wire)


# unitary operator U_C with parameter gamma
def U_C(gamma):
    for edge in graph:
        wire1 = 2*edge[0]
        wire2 = wire1+1
        wire3 = 2*edge[1]
        wire4 = wire3+1

        qml.CNOT(wires=[wire1, wire3])
        qml.RZ(gamma, wires=wire3)
        qml.CNOT(wires=[wire1, wire3])

        qml.CNOT(wires=[wire2, wire4])
        qml.RZ(gamma, wires=wire4)
        qml.CNOT(wires=[wire2, wire4])

        qml.CNOT(wires=[wire1, wire4])
        qml.CNOT(wires=[wire2, wire4])
        qml.CNOT(wires=[wire3, wire4])
        qml.RZ(gamma, wires=wire4)
        qml.CNOT(wires=[wire3, wire4])
        qml.CNOT(wires=[wire2, wire4])
        qml.CNOT(wires=[wire1, wire4])


def bitstring_to_int(bit_string_sample):
    bit_string = "".join(str(bs) for bs in bit_string_sample)
    return int(bit_string, base=2)

dev = qml.device("default.qubit", wires=n_wires, shots=1)

@qml.qnode(dev)
def circuit(gammas, betas, sample=False, n_layers=1):
    # apply Hadamards to get the n qubit |+> state
    for wire in range(n_wires):
        qml.Hadamard(wires=wire)
    # p instances of unitary operators
    for i in range(n_layers):
        U_C(gammas[i])
        U_B(betas[i])
    if sample:
        # measurement phase
        return qml.sample()

    return qml.expval(cost_hamiltonian)

def qaoa_maxcut(n_layers=1):
    print("\np={:d}".format(n_layers))

    # initialize the parameters near zero
    init_gammas = 0.01 * np.random.rand(n_layers, requires_grad=True)
    init_betas = 0.01 * np.random.rand(n_layers, requires_grad=True)

    # initialize optimizer: Adagrad works well empirically
    opt = qml.QNSPSAOptimizer()

    # optimize parameters in objective
    gammas = init_gammas
    betas = init_betas
    steps = 200
    for i in range(steps):
        params, cost = opt.step_and_cost(circuit, gammas, betas, n_layers=n_layers)
        gammas = params[0]
        betas = params[1]
        if (i + 1) % 5 == 0:
            print("Objective after step {:5d}: {: .7f}".format(i + 1, cost))

    # sample measured bitstrings 100 times
    bit_strings = []
    n_samples = 1000
    for i in range(0, n_samples):
        bit_strings.append(bitstring_to_int(circuit(gammas, betas, sample=True, n_layers=n_layers)))

    # print optimal parameters and most frequently sampled bitstring
    counts = np.bincount(np.array(bit_strings))
    most_freq_bit_string = np.argmax(counts)
    print("Optimized (gamma, beta) vectors:\n{} {}".format(gammas, betas))
    print("Most frequently sampled bit string is: {:010b}".format(most_freq_bit_string))

    return circuit(gammas, betas, n_layers=n_layers), bit_strings

bitstrings1 = qaoa_maxcut(n_layers=2)[1]

# import matplotlib.pyplot as plt
# matplotlib.use('module://matplotlib-backend-sixel')
#
# xticks = range(0, 16)
# xtick_labels = list(map(lambda x: format(x, "04b"), xticks))
# bins = np.arange(0, 17) - 0.5
#
# fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))
# plt.subplot(1, 2, 1)
# plt.title("n_layers=1")
# plt.xlabel("bitstrings")
# plt.ylabel("freq.")
# plt.xticks(xticks, xtick_labels, rotation="vertical")
# plt.hist(bitstrings1, bins=bins)
# plt.subplot(1, 2, 2)
# plt.title("n_layers=2")
# plt.xlabel("bitstrings")
# plt.ylabel("freq.")
# plt.xticks(xticks, xtick_labels, rotation="vertical")
# plt.hist(bitstrings2, bins=bins)
# plt.tight_layout()
# plt.show()