Skip to content

Commit 49c16a9

Browse files
Copilotfgfuchs
andcommitted
feat: upgrade to qiskit 2.3.0 - update imports, fix bugs, add tests
Co-authored-by: fgfuchs <2428162+fgfuchs@users.noreply.github.com>
1 parent b818905 commit 49c16a9

File tree

6 files changed

+232
-10
lines changed

6 files changed

+232
-10
lines changed

qaoa/mixers/grover_mixer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def __init__(self, subcircuit: InitialState) -> None:
3232
self.mixer_param = Parameter("x_beta")
3333

3434
def create_circuit(self):
35-
"""
35+
r"""
3636
Constructs the Grover mixer circuit using the subcircuit.
3737
3838
Given feasible states f \in F,

qaoa/mixers/maxkcut_grover_mixer.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ def create_circuit(self) -> None:
130130
tensor_feas = Tensor(circ_one_node, self.num_V)
131131

132132
gm = Grover(tensor_feas)
133+
gm.setNumQubits(tensor_feas.N_qubits)
133134

134135
gm.create_circuit()
135136
self.circuit = gm.circuit

qaoa/qaoa.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
)
1414
from qiskit.circuit import Parameter
1515
try:
16-
from qiskit.primitives import Sampler
17-
except ImportError:
1816
from qiskit_aer.primitives import Sampler
17+
except ImportError:
18+
from qiskit.primitives import StatevectorSampler as Sampler
1919
from qiskit_algorithms.optimizers import COBYLA
2020

21-
from qiskit_aer import Aer
21+
from qiskit_aer import AerSimulator, Aer
2222

2323
from qaoa.initialstates import InitialState
2424
from qaoa.mixers import Mixer
@@ -198,7 +198,7 @@ def __init__(
198198
problem,
199199
mixer,
200200
initialstate,
201-
backend=Aer.get_backend("qasm_simulator"),
201+
backend=AerSimulator(),
202202
noisemodel=None,
203203
optimizer=[COBYLA, {}], # optimizer, options
204204
precision=None,

setup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
"matplotlib",
1919
"networkx",
2020
"jupyter",
21-
"qiskit==1.1.1",
22-
"qiskit-aer",
23-
"qiskit-algorithms",
21+
"qiskit>=2.3.0",
22+
"qiskit-aer>=0.17.0",
23+
"qiskit-algorithms>=0.4.0",
2424
"qiskit-finance",
2525
"pylatexenc",
2626
],

unittests/test_multiangle_gridsearch.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
sys.path.append("../")
77
from qaoa import QAOA
88
from qaoa import problems, mixers, initialstates
9-
from qiskit_aer import Aer
9+
from qiskit_aer import AerSimulator
1010

1111

1212
class TestMultiAngleGridSearch(unittest.TestCase):
1313
"""Test grid search works for both vanilla and multi-angle QAOA."""
1414

1515
def setUp(self):
16-
self.backend = Aer.get_backend("qasm_simulator")
16+
self.backend = AerSimulator()
1717
self.angles = {"gamma": [0, 2 * np.pi, 5], "beta": [0, 2 * np.pi, 5]}
1818

1919
def test_vanilla_grid_search_sequential(self):
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
"""
2+
Integration tests verifying that the QAOA library works correctly with
3+
Qiskit >= 2.x.
4+
5+
These tests cover:
6+
1. Qiskit version verification (>= 2.0)
7+
2. Correct Sampler import (qiskit.primitives.Sampler was removed in Qiskit 2.x)
8+
3. End-to-end QAOA optimization on a small MaxCut problem
9+
4. All key components: problems, mixers, initial states, transpile
10+
5. Core Qiskit 2.x API elements used by the library
11+
"""
12+
13+
import unittest
14+
15+
import numpy as np
16+
import networkx as nx
17+
18+
import qiskit
19+
20+
21+
class TestQiskitVersion(unittest.TestCase):
22+
"""Verify that the installed Qiskit version is >= 2.0."""
23+
24+
def test_qiskit_version_is_2_or_higher(self):
25+
major = int(qiskit.__version__.split(".")[0])
26+
self.assertGreaterEqual(
27+
major,
28+
2,
29+
f"Qiskit {qiskit.__version__} is installed, but >= 2.0.0 is required.",
30+
)
31+
32+
33+
class TestQiskitImports(unittest.TestCase):
34+
"""Verify that all Qiskit symbols used by the library can be imported."""
35+
36+
def test_core_qiskit_imports(self):
37+
from qiskit import (
38+
QuantumCircuit,
39+
QuantumRegister,
40+
ClassicalRegister,
41+
AncillaRegister,
42+
transpile,
43+
)
44+
from qiskit.circuit import Parameter
45+
from qiskit.circuit.library import (
46+
PhaseGate,
47+
RYGate,
48+
XXPlusYYGate,
49+
PauliEvolutionGate,
50+
)
51+
from qiskit.quantum_info import SparsePauliOp, Pauli, Statevector
52+
53+
def test_sampler_import(self):
54+
"""qiskit.primitives.Sampler was removed in Qiskit 2.x; the fallback
55+
to qiskit_aer.primitives.Sampler must work."""
56+
try:
57+
from qiskit_aer.primitives import Sampler
58+
except ImportError:
59+
from qiskit.primitives import StatevectorSampler as Sampler
60+
self.assertIsNotNone(Sampler)
61+
62+
def test_aer_simulator_import(self):
63+
from qiskit_aer import AerSimulator
64+
backend = AerSimulator()
65+
self.assertIsNotNone(backend)
66+
67+
def test_qiskit_algorithms_optimizers(self):
68+
from qiskit_algorithms.optimizers import COBYLA, SPSA
69+
self.assertIsNotNone(COBYLA)
70+
self.assertIsNotNone(SPSA)
71+
72+
def test_qaoa_package_import(self):
73+
import qaoa
74+
from qaoa import QAOA
75+
from qaoa import problems, mixers, initialstates
76+
77+
78+
class TestQAOAEndToEnd(unittest.TestCase):
79+
"""End-to-end QAOA test on a small MaxCut instance using Qiskit 2.x."""
80+
81+
def setUp(self):
82+
from qiskit_aer import AerSimulator
83+
self.backend = AerSimulator()
84+
# 3-node path graph: optimal MaxCut = 2
85+
self.G = nx.path_graph(3)
86+
self.angles = {"gamma": [0, 2 * np.pi, 5], "beta": [0, np.pi, 5]}
87+
88+
def test_maxcut_optimize_depth1(self):
89+
"""QAOA at depth 1 should find a positive approximation ratio for MaxCut."""
90+
from qaoa import QAOA
91+
from qaoa import problems, mixers, initialstates
92+
93+
problem = problems.MaxCut(self.G)
94+
mixer = mixers.X()
95+
init = initialstates.Plus()
96+
97+
qaoa = QAOA(
98+
problem,
99+
mixer,
100+
init,
101+
backend=self.backend,
102+
shots=512,
103+
)
104+
qaoa.optimize(depth=1, angles=self.angles)
105+
106+
best_exp = qaoa.get_Exp(depth=1)
107+
# For a path graph with 3 nodes the optimum is -2 (cost stored negated).
108+
# A good QAOA should achieve at least -1 (approximation ratio >= 0.5).
109+
self.assertLessEqual(best_exp, -1.0)
110+
111+
def test_circuit_creation_and_transpile(self):
112+
"""Verify that the parameterised circuit is created and transpiled correctly."""
113+
from qaoa import QAOA
114+
from qaoa import problems, mixers, initialstates
115+
from qiskit import QuantumCircuit
116+
117+
problem = problems.MaxCut(self.G)
118+
mixer = mixers.X()
119+
init = initialstates.Plus()
120+
121+
qaoa = QAOA(
122+
problem,
123+
mixer,
124+
init,
125+
backend=self.backend,
126+
)
127+
qaoa.createParameterizedCircuit(depth=1)
128+
129+
self.assertIsInstance(qaoa.parameterized_circuit, QuantumCircuit)
130+
# After transpilation the circuit should have no free parameters
131+
# (they are bound at runtime via parameter_binds).
132+
self.assertGreater(len(qaoa.parameterized_circuit.parameters), 0)
133+
134+
def test_hist_method(self):
135+
"""The hist() helper should return a non-empty measurement histogram."""
136+
from qaoa import QAOA
137+
from qaoa import problems, mixers, initialstates
138+
139+
problem = problems.MaxCut(self.G)
140+
qaoa = QAOA(
141+
problem,
142+
mixers.X(),
143+
initialstates.Plus(),
144+
backend=self.backend,
145+
shots=256,
146+
)
147+
qaoa.optimize(depth=1, angles=self.angles)
148+
angles = qaoa.get_angles(depth=1)
149+
hist = qaoa.hist(angles, shots=256)
150+
151+
self.assertIsInstance(hist, dict)
152+
self.assertGreater(len(hist), 0)
153+
total_shots = sum(hist.values())
154+
self.assertEqual(total_shots, 256)
155+
156+
157+
class TestQAOAComponents(unittest.TestCase):
158+
"""Verify individual QAOA components work with Qiskit 2.x."""
159+
160+
def _make_graph(self):
161+
G = nx.Graph()
162+
G.add_nodes_from([0, 1])
163+
G.add_weighted_edges_from([(0, 1, 1.0)])
164+
return G
165+
166+
def test_maxcut_problem_circuit(self):
167+
from qaoa.problems import MaxCut
168+
from qiskit.circuit import QuantumCircuit
169+
170+
problem = MaxCut(self._make_graph())
171+
problem.create_circuit()
172+
self.assertIsInstance(problem.circuit, QuantumCircuit)
173+
self.assertEqual(len(problem.circuit.parameters), 1)
174+
175+
def test_x_mixer_circuit(self):
176+
from qaoa.mixers import X
177+
from qiskit.circuit import QuantumCircuit
178+
179+
mixer = X()
180+
mixer.setNumQubits(2)
181+
mixer.create_circuit()
182+
self.assertIsInstance(mixer.circuit, QuantumCircuit)
183+
184+
def test_xy_mixer_circuit(self):
185+
from qaoa.mixers import XY
186+
from qiskit.circuit import QuantumCircuit
187+
188+
mixer = XY(topology=[[0, 1]]) # explicit edge topology for 2-qubit ring
189+
mixer.setNumQubits(2)
190+
mixer.create_circuit()
191+
self.assertIsInstance(mixer.circuit, QuantumCircuit)
192+
193+
def test_plus_initialstate_circuit(self):
194+
from qaoa.initialstates import Plus
195+
from qiskit.circuit import QuantumCircuit
196+
197+
init = Plus()
198+
init.setNumQubits(2)
199+
init.create_circuit()
200+
self.assertIsInstance(init.circuit, QuantumCircuit)
201+
202+
def test_transpile_with_aer_simulator(self):
203+
"""transpile() should work with AerSimulator in Qiskit 2.x."""
204+
from qiskit import QuantumCircuit, transpile
205+
from qiskit.circuit import Parameter
206+
from qiskit_aer import AerSimulator
207+
208+
backend = AerSimulator()
209+
qc = QuantumCircuit(2, 2)
210+
theta = Parameter("theta")
211+
qc.h(0)
212+
qc.rz(theta, 0)
213+
qc.cx(0, 1)
214+
qc.measure([0, 1], [0, 1])
215+
216+
transpiled = transpile(qc, backend)
217+
self.assertIsInstance(transpiled, QuantumCircuit)
218+
219+
220+
if __name__ == "__main__":
221+
unittest.main()

0 commit comments

Comments
 (0)