Skip to content

Commit b8b860b

Browse files
committed
update QuantumRandomAccessEncoding
1 parent ac33114 commit b8b860b

File tree

2 files changed

+63
-36
lines changed

2 files changed

+63
-36
lines changed

qiskit_optimization/algorithms/qrao/encoding_commutation_verifier.py

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2023.
3+
# (C) Copyright IBM 2023, 2024.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -14,7 +14,8 @@
1414

1515
from __future__ import annotations
1616

17-
from qiskit.primitives import BaseEstimator
17+
from qiskit.passmanager import BasePassManager
18+
from qiskit.primitives import BaseEstimatorV1, BaseEstimatorV2
1819

1920
from qiskit_optimization.exceptions import QiskitOptimizationError
2021

@@ -24,14 +25,21 @@
2425
class EncodingCommutationVerifier:
2526
"""Class for verifying that the relaxation commutes with the objective function."""
2627

27-
def __init__(self, encoding: QuantumRandomAccessEncoding, estimator: BaseEstimator):
28+
def __init__(
29+
self,
30+
encoding: QuantumRandomAccessEncoding,
31+
estimator: BaseEstimatorV1 | BaseEstimatorV2,
32+
passmanager: BasePassManager | None = None,
33+
):
2834
"""
2935
Args:
3036
encoding: The encoding to verify.
3137
estimator: The estimator to use for the verification.
38+
passmanager: The pass manager to tranpile the circuits
3239
"""
3340
self._encoding = encoding
3441
self._estimator = estimator
42+
self._passmanager = passmanager
3543

3644
def __len__(self) -> int:
3745
return 2**self._encoding.num_vars
@@ -48,6 +56,8 @@ def __getitem__(self, i: int) -> tuple[str, float, float]:
4856
str_dvars = f"{i:0{encoding.num_vars}b}"
4957
dvars = [int(b) for b in str_dvars]
5058
encoded_bitstr_qc = encoding.state_preparation_circuit(dvars)
59+
if self._passmanager:
60+
encoded_bitstr_qc = self._passmanager.run(encoded_bitstr_qc)
5161

5262
# Evaluate the original objective function
5363
problem = encoding.problem
@@ -58,13 +68,24 @@ def __getitem__(self, i: int) -> tuple[str, float, float]:
5868
encoded_op = encoding.qubit_op
5969
offset = encoding.offset
6070

61-
job = self._estimator.run([encoded_bitstr_qc], [encoded_op])
62-
63-
try:
64-
encoded_obj_val = job.result().values[0] + offset
65-
except Exception as exc:
66-
raise QiskitOptimizationError(
67-
"The primitive job to verify commutation failed!"
68-
) from exc
69-
70-
return (str_dvars, obj_val, encoded_obj_val)
71+
if isinstance(self._estimator, BaseEstimatorV1):
72+
job = self._estimator.run([encoded_bitstr_qc], [encoded_op])
73+
74+
try:
75+
encoded_obj_val = job.result().values[0] + offset
76+
except Exception as exc:
77+
raise QiskitOptimizationError(
78+
"The primitive job to verify commutation failed!"
79+
) from exc
80+
else: # BaseEstimatorV2
81+
job = self._estimator.run([(encoded_bitstr_qc, encoded_op)])
82+
83+
try:
84+
result = job.result()
85+
encoded_obj_val = result[0].data.evs.item() + offset
86+
except Exception as exc:
87+
raise QiskitOptimizationError(
88+
"The primitive job to verify commutation failed!"
89+
) from exc
90+
91+
return str_dvars, obj_val, encoded_obj_val

test/algorithms/qrao/test_quantum_random_access_encoding.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# This code is part of a Qiskit project.
22
#
3-
# (C) Copyright IBM 2023.
3+
# (C) Copyright IBM 2023, 2024.
44
#
55
# This code is licensed under the Apache License, Version 2.0. You may
66
# obtain a copy of this license in the LICENSE.txt file in the root directory
@@ -13,22 +13,21 @@
1313
"""Tests for QuantumRandomAccessEncoding"""
1414
import itertools
1515
import unittest
16-
from test.optimization_test_case import QiskitOptimizationTestCase
16+
from test import QiskitOptimizationTestCase
1717

18-
from ddt import ddt, data, unpack
19-
import numpy as np
2018
import networkx as nx
21-
19+
import numpy as np
20+
from ddt import data, ddt, unpack
2221
from qiskit.circuit import QuantumCircuit
23-
from qiskit.primitives import Estimator
22+
from qiskit.primitives import Estimator, StatevectorEstimator
2423
from qiskit.quantum_info import SparsePauliOp
2524

2625
from qiskit_optimization.algorithms.qrao import (
2726
EncodingCommutationVerifier,
2827
QuantumRandomAccessEncoding,
2928
)
30-
from qiskit_optimization.problems import QuadraticProgram, QuadraticObjective
3129
from qiskit_optimization.applications import Maxcut
30+
from qiskit_optimization.problems import QuadraticObjective, QuadraticProgram
3231

3332

3433
class TestQuantumRandomAccessEncoding(QiskitOptimizationTestCase):
@@ -252,17 +251,20 @@ def test_qrac_unsupported_encoding(self):
252251
class TestEncodingCommutationVerifier(QiskitOptimizationTestCase):
253252
"""Tests for EncodingCommutationVerifier."""
254253

255-
def check_problem_commutation(self, problem: QuadraticProgram, max_vars_per_qubit: int):
254+
def check_problem_commutation(
255+
self, problem: QuadraticProgram, max_vars_per_qubit: int, version: str
256+
):
256257
"""Utility function to check that the problem commutes with its encoding"""
257258
encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=max_vars_per_qubit)
258259
encoding.encode(problem)
259-
estimator = Estimator()
260+
estimator = Estimator() if version == "v1" else StatevectorEstimator()
260261
verifier = EncodingCommutationVerifier(encoding, estimator)
261262
self.assertEqual(len(verifier), 2**encoding.num_vars)
262263
for _, obj_val, encoded_obj_val in verifier:
263264
np.testing.assert_allclose(obj_val, encoded_obj_val, atol=1e-5)
264265

265-
def test_encoding_commutation_verifier(self):
266+
@data("v1", "v2")
267+
def test_encoding_commutation_verifier(self, version):
266268
"""Test EncodingCommutationVerifier"""
267269
problem = QuadraticProgram()
268270
problem.binary_var("x")
@@ -272,11 +274,11 @@ def test_encoding_commutation_verifier(self):
272274

273275
encoding = QuantumRandomAccessEncoding(max_vars_per_qubit=3)
274276
encoding.encode(problem)
275-
self.check_problem_commutation(problem, 3)
277+
self.check_problem_commutation(problem, 3, version)
276278

277-
@data(*itertools.product([1, 2, 3], ["minimize", "maximize"]))
279+
@data(*itertools.product([1, 2, 3], ["minimize", "maximize"], ["v1", "v2"]))
278280
@unpack
279-
def test_one_qubit_qrac(self, max_vars_per_qubit, task):
281+
def test_one_qubit_qrac(self, max_vars_per_qubit, task, version):
280282
"""Test commutation of single qubit QRAC with non-uniform weights, degree 1 terms"""
281283

282284
problem = QuadraticProgram()
@@ -287,15 +289,17 @@ def test_one_qubit_qrac(self, max_vars_per_qubit, task):
287289
problem.minimize(linear=obj)
288290
else:
289291
problem.maximize(linear=obj)
290-
self.check_problem_commutation(problem, max_vars_per_qubit)
292+
self.check_problem_commutation(problem, max_vars_per_qubit, version)
291293

292294
@data(
293295
*itertools.product(
294-
[1, 2, 3], [QuadraticObjective.Sense.MINIMIZE, QuadraticObjective.Sense.MAXIMIZE]
296+
[1, 2, 3],
297+
[QuadraticObjective.Sense.MINIMIZE, QuadraticObjective.Sense.MAXIMIZE],
298+
["v1", "v2"],
295299
)
296300
)
297301
@unpack
298-
def test_uniform_weights_degree_2(self, max_vars_per_qubit, task):
302+
def test_uniform_weights_degree_2(self, max_vars_per_qubit, task, version):
299303
"""Test problem commutation with degree 2 terms"""
300304
# Note that the variable embedding has some qubits with 1, 2, and 3 qubits
301305
elist = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0), (0, 3), (1, 4), (2, 4)]
@@ -306,25 +310,27 @@ def test_uniform_weights_degree_2(self, max_vars_per_qubit, task):
306310
maxcut = Maxcut(graph)
307311
problem = maxcut.to_quadratic_program()
308312
problem.objective.sense = task
309-
self.check_problem_commutation(problem, max_vars_per_qubit)
313+
self.check_problem_commutation(problem, max_vars_per_qubit, version)
310314

311-
@data(1, 2, 3)
312-
def test_random_unweighted_maxcut(self, max_vars_per_qubit):
315+
@data(*itertools.product([1, 2, 3], ["v1", "v2"]))
316+
@unpack
317+
def test_random_unweighted_maxcut(self, max_vars_per_qubit, version):
313318
"""Test problem commutation with random unweighted MaxCut"""
314319
graph = nx.random_regular_graph(3, 8)
315320
maxcut = Maxcut(graph)
316321
problem = maxcut.to_quadratic_program()
317-
self.check_problem_commutation(problem, max_vars_per_qubit)
322+
self.check_problem_commutation(problem, max_vars_per_qubit, version)
318323

319-
@data(1, 2, 3)
320-
def test_random_weighted_maxcut(self, max_vars_per_qubit):
324+
@data(*itertools.product([1, 2, 3], ["v1", "v2"]))
325+
@unpack
326+
def test_random_weighted_maxcut(self, max_vars_per_qubit, version):
321327
"""Test problem commutation with random weighted MaxCut"""
322328
graph = nx.random_regular_graph(3, 8)
323329
for w, v in graph.edges:
324330
graph[w][v]["weight"] = np.random.randint(1, 10)
325331
maxcut = Maxcut(graph)
326332
problem = maxcut.to_quadratic_program()
327-
self.check_problem_commutation(problem, max_vars_per_qubit)
333+
self.check_problem_commutation(problem, max_vars_per_qubit, version)
328334

329335

330336
if __name__ == "__main__":

0 commit comments

Comments
 (0)