Skip to content

Commit b194a17

Browse files
authored
added maxkcut (#28)
* added maxkcut
1 parent cf61fca commit b194a17

25 files changed

+2010
-46
lines changed

qaoa/initialstates/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
22
from .plus_initialstate import Plus
33
from .dicke_initialstate import Dicke
44
from .statevector_initialstate import StateVector
5+
from .maxkcut_feasible_initialstate import MaxKCutFeasible
6+
from .tensor_initialstate import Tensor
7+
from .lessthank_initialstate import LessThanK
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import numpy as np
2+
from qiskit import QuantumCircuit, QuantumRegister
3+
from qiskit.circuit.library import XXPlusYYGate, PauliEvolutionGate
4+
5+
from qiskit.quantum_info import SparsePauliOp
6+
7+
from .base_initialstate import InitialState
8+
9+
10+
class Dicke1_2(InitialState):
11+
"""
12+
Returs equal superposition Dicke 1 and Dicke 2 states. Hard Coded
13+
"""
14+
15+
def __init__(self) -> None:
16+
self.k = 6
17+
self.N_qubits = 3
18+
19+
def create_circuit(self) -> None:
20+
q = QuantumRegister(self.N_qubits)
21+
circuit = QuantumCircuit(q)
22+
X = SparsePauliOp("X")
23+
Y = SparsePauliOp("Y")
24+
operator = Y ^ Y ^ Y
25+
circuit.x(0)
26+
# qc.ry(np.pi/2,2)
27+
circuit.append(PauliEvolutionGate(operator, time=np.pi / 4), q)
28+
circuit.append(XXPlusYYGate(np.arcsin(2 * np.sqrt(2) / 3), np.pi / 2), [0, 1])
29+
circuit.append(XXPlusYYGate(-np.pi / 2, np.pi / 2), [0, 2])
30+
circuit.x(1)
31+
circuit.cz(q[1], q[2])
32+
circuit.x(1)
33+
self.circuit = circuit
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import numpy as np
2+
from qiskit import QuantumCircuit, QuantumRegister
3+
from qiskit.circuit.library import XXPlusYYGate
4+
5+
from .base_initialstate import InitialState # type: ignore
6+
7+
8+
class LessThanK(InitialState):
9+
def __init__(self, k: int) -> None:
10+
if not LessThanK.is_power_of_two_or_between_2_and_8(k):
11+
raise ValueError("k must be a power of two or between 2 and 8")
12+
self.k = k
13+
self.N_qubits = int(np.ceil(np.log2(self.k)))
14+
15+
def create_circuit(self) -> None:
16+
if self.k == 3:
17+
self.circuit = self.k3()
18+
elif self.k == 5:
19+
self.circuit = self.k5()
20+
elif self.k == 6:
21+
self.circuit = self.k6()
22+
elif self.k == 7:
23+
self.circuit = self.k7()
24+
else:
25+
self.circuit = self.power_of_two()
26+
27+
def is_power_of_two_or_between_2_and_8(k):
28+
# Check if k is between 2 and 8
29+
if 2 <= k <= 8:
30+
return True
31+
32+
# Check if k is a power of two
33+
# A number is a power of two if it has exactly one bit set, i.e., k & (k - 1) == 0 and k > 0
34+
if k > 0 and (k & (k - 1)) == 0:
35+
return True
36+
37+
return False
38+
39+
def power_of_two(self) -> QuantumCircuit:
40+
q = QuantumRegister(self.N_qubits)
41+
circuit = QuantumCircuit(q)
42+
circuit.h(q)
43+
return circuit
44+
45+
def k3(self) -> QuantumCircuit:
46+
q = QuantumRegister(self.N_qubits)
47+
circuit = QuantumCircuit(q)
48+
theta = np.arccos(1 / np.sqrt(3)) * 2
49+
phi = np.pi / 2
50+
beta = -np.pi / 2
51+
circuit.ry(theta, 1)
52+
gate = XXPlusYYGate(phi, beta)
53+
circuit.append(gate, [0, 1])
54+
return circuit
55+
56+
def k5(self) -> QuantumCircuit:
57+
q = QuantumRegister(self.N_qubits)
58+
circuit = QuantumCircuit(q)
59+
theta = np.arcsin(1 / np.sqrt(5)) * 2
60+
circuit.ry(theta, 0)
61+
circuit.ch(0, [1, 2], ctrl_state=0)
62+
return circuit
63+
64+
def k6(self) -> QuantumCircuit:
65+
q = QuantumRegister(self.N_qubits)
66+
circuit = QuantumCircuit(q)
67+
theta = np.pi / 2
68+
phi = np.arccos(1 / np.sqrt(3)) * 2
69+
gamma = np.pi / 2
70+
beta = -np.pi / 2
71+
circuit.ry(theta, 2)
72+
circuit.ry(phi, 1)
73+
gate = XXPlusYYGate(gamma, beta)
74+
circuit.append(gate, [0, 1])
75+
return circuit
76+
77+
def k7(self) -> QuantumCircuit:
78+
q = QuantumRegister(self.N_qubits)
79+
circuit = QuantumCircuit(q)
80+
delta = np.arcsin(1 / np.sqrt(7)) * 2
81+
theta = np.pi / 2
82+
phi = np.arccos(1 / np.sqrt(3)) * 2
83+
gamma = np.pi / 2
84+
beta = -np.pi / 2
85+
circuit.ry(delta, 0)
86+
circuit.cx(0, 1)
87+
circuit.cry(theta, 0, 2, ctrl_state=0)
88+
circuit.cry(phi, 0, 1, ctrl_state=0)
89+
gate = XXPlusYYGate(gamma, beta)
90+
circuit.append(gate, [0, 1])
91+
return circuit
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import numpy as np
2+
3+
4+
from .base_initialstate import InitialState
5+
from .dicke_initialstate import Dicke
6+
from .dicke1_2_initialstate import Dicke1_2
7+
from .lessthank_initialstate import LessThanK
8+
from .tensor_initialstate import Tensor
9+
10+
11+
class MaxKCutFeasible(InitialState):
12+
def __init__(
13+
self, k_cuts: int, problem_encoding: str, color_encoding: str = "LessThanK"
14+
) -> None:
15+
self.k_cuts = k_cuts
16+
self.problem_encoding = problem_encoding
17+
self.color_encoding = color_encoding
18+
19+
if not problem_encoding in ["onehot", "binary"]:
20+
raise ValueError('case must be in ["onehot", "binary"]')
21+
if problem_encoding == "binary":
22+
if k_cuts == 6 and (color_encoding not in ["Dicke1_2", "LessThanK"]):
23+
raise ValueError('color_encoding must be in ["LessThanK", "Dicke1_2"]')
24+
self.color_encoding = color_encoding
25+
26+
if self.k_cuts == 3:
27+
self.infeasible = ["11"]
28+
elif self.k_cuts == 5:
29+
if self.color_encoding == "max_balanced":
30+
self.infeasible = ["100", "111", "101"]
31+
else:
32+
self.infeasible = ["101", "110", "111"]
33+
elif self.k_cuts == 6:
34+
if self.color_encoding in ["Dicke1_2", "max_balanced"]:
35+
self.infeasible = ["000", "111"]
36+
else:
37+
self.infeasible = ["110", "111"]
38+
elif self.k_cuts == 7:
39+
self.infeasible = ["111"]
40+
41+
def create_circuit(self) -> None:
42+
if self.problem_encoding == "binary":
43+
self.k_bits = int(np.ceil(np.log2(self.k_cuts)))
44+
self.num_V = self.N_qubits / self.k_bits
45+
46+
if not self.num_V.is_integer():
47+
raise ValueError(
48+
"Total qubits="
49+
+ str(self.N_qubits)
50+
+ " is not a multiple of "
51+
+ str(self.k_bits)
52+
)
53+
if self.k_cuts == 6 and self.color_encoding == "Dicke1_2":
54+
circ_one_node = Dicke1_2()
55+
else:
56+
circ_one_node = LessThanK(self.k_cuts)
57+
58+
elif self.problem_encoding == "onehot":
59+
self.num_V = self.N_qubits / self.k_cuts
60+
61+
if not self.num_V.is_integer():
62+
raise ValueError(
63+
"Total qubits="
64+
+ str(self.N_qubits)
65+
+ " is not a multiple of "
66+
+ str(self.k_cuts)
67+
)
68+
self.num_V = int(self.num_V)
69+
70+
circ_one_node = Dicke(1)
71+
circ_one_node.setNumQubits(self.k_cuts)
72+
73+
self.num_V = int(self.num_V)
74+
self.tensor = Tensor(circ_one_node, self.num_V)
75+
76+
self.tensor.create_circuit()
77+
self.circuit = self.tensor.circuit
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import numpy as np
2+
from copy import deepcopy
3+
4+
from qiskit import QuantumRegister, QuantumCircuit
5+
6+
from .base_initialstate import InitialState
7+
8+
9+
class Tensor(InitialState):
10+
def __init__(self, subcircuit: InitialState, num: int) -> None:
11+
"""
12+
Args:
13+
subcircuit (InitialState): the circuit that is to be tensorised
14+
#subN_qubits (int): number of qubits of the subpart
15+
"""
16+
self.num = num
17+
self.subcircuit = subcircuit
18+
self.N_qubits = self.num * self.subcircuit.N_qubits
19+
20+
def create_circuit(self) -> None:
21+
self.subcircuit.create_circuit()
22+
self.circuit = self.subcircuit.circuit
23+
for v in range(self.num - 1):
24+
self.subcircuit.create_circuit() # self.subcircuit.circuit.qregs)
25+
self.circuit.tensor(self.subcircuit.circuit, inplace=True)

qaoa/mixers/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
22
from .xy_mixer import XY
33
from .x_mixer import X
44
from .grover_mixer import Grover
5+
from .xy_tensor import XYTensor
6+
from .maxkcut_grover_mixer import MaxKCutGrover
7+
from .maxkcut_lx_mixer import MaxKCutLX

qaoa/mixers/grover_mixer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from qiskit.circuit import Parameter
22
from qiskit.circuit.library import PhaseGate
33

4-
from qaoa.mixers.base_mixer import Mixer
4+
from .base_mixer import Mixer
55
from qaoa.initialstates.base_initialstate import InitialState
66

77

qaoa/mixers/maxkcut_grover_mixer.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import numpy as np
2+
3+
from qaoa.mixers import Mixer, Grover
4+
from qaoa.initialstates import Dicke, Plus
5+
6+
from qaoa.initialstates.dicke1_2_initialstate import Dicke1_2
7+
from qaoa.initialstates.lessthank_initialstate import LessThanK
8+
from qaoa.initialstates.tensor_initialstate import Tensor
9+
10+
11+
class MaxKCutGrover(Mixer):
12+
def __init__(
13+
self, k_cuts: int, problem_encoding: str, color_encoding: str, tensorized: bool
14+
) -> None:
15+
if (k_cuts < 2) or (k_cuts > 8):
16+
raise ValueError(
17+
"k_cuts must be 2 or more, and is not implemented for k_cuts > 8"
18+
)
19+
if not problem_encoding in ["onehot", "binary"]:
20+
raise ValueError('case must be in ["onehot", "binary"]')
21+
self.k_cuts = k_cuts
22+
self.problem_encoding = problem_encoding
23+
self.color_encoding = color_encoding
24+
self.tensorized = tensorized
25+
26+
if (self.problem_encoding == "binary") and self.is_power_of_two():
27+
print(
28+
"k_cuts is a power of two. You might want to use the X-mixer instead."
29+
)
30+
31+
# for k=6, max_balanced == Dicke1_2
32+
if k_cuts == 6 and (
33+
color_encoding not in ["max_balanced", "Dicke1_2", "LessThanK"]
34+
):
35+
raise ValueError(
36+
'color_encoding must be in ["LessThanK", "Dicke1_2", max_balanced]'
37+
)
38+
39+
def is_power_of_two(self) -> bool:
40+
"""
41+
Return True if self.k_cuts is a power of two, False otherwise.
42+
"""
43+
if self.k_cuts > 0 and (self.k_cuts & (self.k_cuts - 1)) == 0:
44+
return True
45+
return False
46+
47+
def set_numV(self, k):
48+
num_V = self.N_qubits / k
49+
50+
if not num_V.is_integer():
51+
raise ValueError(
52+
"Total qubits=" + str(self.N_qubits) + " is not a multiple of " + str(k)
53+
)
54+
55+
self.num_V = int(num_V)
56+
57+
def create_circuit(self) -> None:
58+
if self.problem_encoding == "binary":
59+
self.k_bits = int(np.ceil(np.log2(self.k_cuts)))
60+
self.set_numV(self.k_bits)
61+
62+
if self.is_power_of_two():
63+
circ_one_node = Plus()
64+
circ_one_node.N_qubits = self.k_bits
65+
elif self.k_cuts == 6 and self.color_encoding in [
66+
"max_balanced",
67+
"Dicke1_2",
68+
]:
69+
circ_one_node = Dicke1_2()
70+
else:
71+
circ_one_node = LessThanK(self.k_cuts)
72+
73+
elif self.problem_encoding == "onehot":
74+
self.set_numV(self.k_cuts)
75+
76+
circ_one_node = Dicke(1)
77+
circ_one_node.setNumQubits(self.k_cuts)
78+
79+
if self.tensorized:
80+
gm = Grover(circ_one_node)
81+
82+
tensor_gm = Tensor(gm, self.num_V)
83+
84+
tensor_gm.create_circuit()
85+
self.circuit = tensor_gm.circuit
86+
else:
87+
tensor_feas = Tensor(circ_one_node, self.num_V)
88+
89+
gm = Grover(tensor_feas)
90+
91+
gm.create_circuit()
92+
self.circuit = gm.circuit

0 commit comments

Comments
 (0)