-
Notifications
You must be signed in to change notification settings - Fork 14
/
qaoa_circuit_maxcut.py
139 lines (119 loc) · 4.48 KB
/
qaoa_circuit_maxcut.py
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
127
128
129
130
131
132
133
134
135
136
137
138
139
###############################################################################
# // SPDX-License-Identifier: Apache-2.0
# // Copyright : JP Morgan Chase & Co
###############################################################################
# QAOA circuit for MAXCUT
import networkx as nx
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit import ParameterVector
from typing import Sequence
def append_zz_term(qc, q1, q2, gamma):
qc.rzz(-gamma / 2, q1, q2)
def append_maxcut_cost_operator_circuit(qc, G, gamma):
for i, j in G.edges():
if nx.is_weighted(G):
append_zz_term(qc, i, j, gamma * G[i][j]["weight"])
else:
append_zz_term(qc, i, j, gamma)
def append_x_term(qc, q1, beta):
qc.rx(2 * beta, q1)
def append_mixer_operator_circuit(qc, G, beta):
for n in G.nodes():
append_x_term(qc, n, beta)
def get_qaoa_circuit(G: nx.Graph, betas: Sequence, gammas: Sequence, save_statevector: bool = True, qr: QuantumRegister = None, cr: ClassicalRegister = None):
"""Generates a circuit for weighted MaxCut on graph G.
Parameters
----------
G : networkx.Graph
Graph to solve MaxCut on
beta : list-like
QAOA parameter beta
gamma : list-like
QAOA parameter gamma
save_statevector : bool, default True
Add save state instruction to the end of the circuit
qr : qiskit.QuantumRegister, default None
Registers to use for the circuit.
Useful when one has to compose circuits in a complicated way
By default, G.number_of_nodes() registers are used
cr : qiskit.ClassicalRegister, default None
Classical registers, useful if measuring
By default, no classical registers are added
Returns
-------
qc : qiskit.QuantumCircuit
Quantum circuit implementing QAOA
"""
assert len(betas) == len(gammas)
p = len(betas) # infering number of QAOA steps from the parameters passed
N = G.number_of_nodes()
if qr is not None:
assert qr.size >= N
else:
qr = QuantumRegister(N)
if cr is not None:
qc = QuantumCircuit(qr, cr)
else:
qc = QuantumCircuit(qr)
# first, apply a layer of Hadamards
qc.h(range(N))
# second, apply p alternating operators
for i in range(p):
append_maxcut_cost_operator_circuit(qc, G, gammas[i])
append_mixer_operator_circuit(qc, G, betas[i])
if save_statevector:
qc.save_statevector()
return qc
def get_parameterized_qaoa_circuit(
G: nx.Graph, p: int, save_statevector: bool = True, qr: QuantumRegister = None, cr: ClassicalRegister = None, return_parameter_vectors: bool = False
):
"""Generates a parameterized circuit for weighted MaxCut on graph G.
This version is recommended for long circuits
Parameters
----------
G : networkx.Graph
Graph to solve MaxCut on
p : int
Number of QAOA layers (number of parameters will be 2*p)
save_statevector : bool, default True
Add save state instruction to the end of the circuit
qr : qiskit.QuantumRegister, default None
Registers to use for the circuit.
Useful when one has to compose circuits in a complicated way
By default, G.number_of_nodes() registers are used
cr : qiskit.ClassicalRegister, default None
Classical registers, useful if measuring
By default, no classical registers are added
return_parameter_vectors : bool, default False
Return ParameterVector for betas and gammas
Returns
-------
qc : qiskit.QuantumCircuit
Parameterized quantum circuit implementing QAOA
Parameters are two ParameterVector sorted alphabetically
(beta first, then gamma). To bind:
qc.bind_parameters(np.hstack([angles['beta'], angles['gamma']]))
"""
N = G.number_of_nodes()
if qr is not None:
assert qr.size >= N
else:
qr = QuantumRegister(N)
if cr is not None:
qc = QuantumCircuit(qr, cr)
else:
qc = QuantumCircuit(qr)
betas = ParameterVector("beta", p)
gammas = ParameterVector("gamma", p)
# first, apply a layer of Hadamards
qc.h(range(N))
# second, apply p alternating operators
for i in range(p):
append_maxcut_cost_operator_circuit(qc, G, gammas[i])
append_mixer_operator_circuit(qc, G, betas[i])
if save_statevector:
qc.save_statevector()
if return_parameter_vectors:
return qc, betas, gammas
else:
return qc