From 8a042d9bef8839db052d3237c3f9d97dbbdd7c29 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Fri, 11 Nov 2022 19:10:57 +0900 Subject: [PATCH 01/19] let MinimumEigenOptimizer allow new primitive-based algorithms --- .../algorithms/minimum_eigen_optimizer.py | 50 ++- .../algorithms/optimization_algorithm.py | 37 +- .../legacy/test_min_eigen_optimizer.py | 387 ++++++++++++++++++ test/algorithms/test_min_eigen_optimizer.py | 100 ++--- 4 files changed, 498 insertions(+), 76 deletions(-) create mode 100644 test/algorithms/legacy/test_min_eigen_optimizer.py diff --git a/qiskit_optimization/algorithms/minimum_eigen_optimizer.py b/qiskit_optimization/algorithms/minimum_eigen_optimizer.py index 0a86bba28..e8dc4b870 100644 --- a/qiskit_optimization/algorithms/minimum_eigen_optimizer.py +++ b/qiskit_optimization/algorithms/minimum_eigen_optimizer.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2020, 2021. +# (C) Copyright IBM 2020, 2022. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,24 +11,38 @@ # that they have been altered from the originals. """A wrapper for minimum eigen solvers to be used within the optimization module.""" -from typing import Optional, Union, List, cast +from typing import List, Optional, Union, cast import numpy as np +from qiskit.algorithms.minimum_eigen_solvers import MinimumEigensolver as LegacyMinimumEigensolver +from qiskit.algorithms.minimum_eigen_solvers import ( + MinimumEigensolverResult as LegacyMinimumEigensolverResult, +) +from qiskit.algorithms.minimum_eigensolvers import ( + NumPyMinimumEigensolver, + NumPyMinimumEigensolverResult, + SamplingMinimumEigensolver, + SamplingMinimumEigensolverResult, +) +from qiskit.opflow import OperatorBase, PauliOp, PauliSumOp -from qiskit.algorithms import MinimumEigensolver, MinimumEigensolverResult -from qiskit.opflow import OperatorBase +from ..converters.quadratic_program_to_qubo import QuadraticProgramConverter, QuadraticProgramToQubo +from ..deprecation import DeprecatedType, warn_deprecated +from ..exceptions import QiskitOptimizationError +from ..problems.quadratic_program import QuadraticProgram, Variable from .optimization_algorithm import ( - OptimizationResultStatus, OptimizationAlgorithm, OptimizationResult, + OptimizationResultStatus, SolutionSample, ) -from ..exceptions import QiskitOptimizationError -from ..converters.quadratic_program_to_qubo import ( - QuadraticProgramToQubo, - QuadraticProgramConverter, -) -from ..problems.quadratic_program import QuadraticProgram, Variable + +MinimumEigensolver = Union[ + SamplingMinimumEigensolver, NumPyMinimumEigensolver, LegacyMinimumEigensolver +] +MinimumEigensolverResult = Union[ + SamplingMinimumEigensolverResult, NumPyMinimumEigensolverResult, LegacyMinimumEigensolverResult +] class MinimumEigenOptimizationResult(OptimizationResult): @@ -137,11 +151,18 @@ def __init__( TypeError: When one of converters has an invalid type. QiskitOptimizationError: When the minimum eigensolver does not return an eigenstate. """ - + if isinstance(min_eigen_solver, LegacyMinimumEigensolver): + warn_deprecated( + "0.5.0", + DeprecatedType.ARGUMENT, + f"min_eigen_solver as {LegacyMinimumEigensolver.__name__}", + new_name=f"min_eigen_solver as {SamplingMinimumEigensolver.__name__} " + f"or {NumPyMinimumEigensolver.__name__}", + ) if not min_eigen_solver.supports_aux_operators(): raise QiskitOptimizationError( "Given MinimumEigensolver does not return the eigenstate " - + "and is not supported by the MinimumEigenOptimizer." + "and is not supported by the MinimumEigenOptimizer." ) self._min_eigen_solver = min_eigen_solver self._penalty = penalty @@ -206,6 +227,9 @@ def _solve_internal( # only try to solve non-empty Ising Hamiltonians eigen_result: Optional[MinimumEigensolverResult] = None if operator.num_qubits > 0: + # NumPyEigensolver does not accept PauliOp but PauliSumOp + if isinstance(operator, PauliOp): + operator = PauliSumOp.from_list([(operator.primitive.to_label(), operator.coeff)]) # approximate ground state of operator using min eigen solver eigen_result = self._min_eigen_solver.compute_minimum_eigenvalue(operator) # analyze results diff --git a/qiskit_optimization/algorithms/optimization_algorithm.py b/qiskit_optimization/algorithms/optimization_algorithm.py index 050d78ca5..4b8fb3080 100644 --- a/qiskit_optimization/algorithms/optimization_algorithm.py +++ b/qiskit_optimization/algorithms/optimization_algorithm.py @@ -15,14 +15,16 @@ from abc import ABC, abstractmethod from dataclasses import dataclass from enum import Enum -from typing import List, Union, Any, Optional, Dict, Type, Tuple, cast +from typing import Any, Dict, List, Optional, Tuple, Type, Union, cast from warnings import warn import numpy as np +from qiskit.opflow import DictStateFn, StateFn +from qiskit.quantum_info import Statevector +from qiskit.result import QuasiDistribution -from qiskit.opflow import StateFn, DictStateFn +from ..converters.quadratic_program_to_qubo import QuadraticProgramConverter, QuadraticProgramToQubo from ..exceptions import QiskitOptimizationError -from ..converters.quadratic_program_to_qubo import QuadraticProgramToQubo, QuadraticProgramConverter from ..problems.quadratic_program import QuadraticProgram, Variable @@ -518,7 +520,7 @@ def _interpret_samples( @staticmethod def _eigenvector_to_solutions( - eigenvector: Union[dict, np.ndarray, StateFn], + eigenvector: Union[QuasiDistribution, Statevector, dict, np.ndarray, StateFn], qubo: QuadraticProgram, min_probability: float = 1e-6, ) -> List[SolutionSample]: @@ -566,7 +568,25 @@ def generate_solution(bitstr, qubo, probability): ) solutions = [] - if isinstance(eigenvector, dict): + if isinstance(eigenvector, QuasiDistribution): + probabilities = eigenvector.binary_probabilities() + # iterate over all samples + for bitstr, sampling_probability in probabilities.items(): + # add the bitstring, if the sampling probability exceeds the threshold + if sampling_probability >= min_probability: + solutions.append(generate_solution(bitstr, qubo, sampling_probability)) + + elif isinstance(eigenvector, Statevector): + probabilities = eigenvector.probabilities() + num_qubits = eigenvector.num_qubits + # iterate over all states and their sampling probabilities + for i, sampling_probability in enumerate(probabilities): + # add the i-th state if the sampling probability exceeds the threshold + if sampling_probability >= min_probability: + bitstr = f"{i:b}".rjust(num_qubits, "0") + solutions.append(generate_solution(bitstr, qubo, sampling_probability)) + + elif isinstance(eigenvector, dict): # When eigenvector is a dict, square the values since the values are normalized. # See https://github.com/Qiskit/qiskit-terra/pull/5496 for more details. probabilities = {bitstr: val**2 for (bitstr, val) in eigenvector.items()} @@ -579,7 +599,6 @@ def generate_solution(bitstr, qubo, probability): elif isinstance(eigenvector, np.ndarray): num_qubits = int(np.log2(eigenvector.size)) probabilities = np.abs(eigenvector * eigenvector.conj()) - # iterate over all states and their sampling probabilities for i, sampling_probability in enumerate(probabilities): # add the i-th state if the sampling probability exceeds the threshold @@ -588,6 +607,8 @@ def generate_solution(bitstr, qubo, probability): solutions.append(generate_solution(bitstr, qubo, sampling_probability)) else: - raise TypeError("Unsupported format of eigenvector. Provide a dict or numpy.ndarray.") - + raise TypeError( + f"Eigenvector should be QuasiDistribution, Statevector, dict or numpy.ndarray. " + f"But, it was {type(eigenvector)}." + ) return solutions diff --git a/test/algorithms/legacy/test_min_eigen_optimizer.py b/test/algorithms/legacy/test_min_eigen_optimizer.py new file mode 100644 index 000000000..94c397bdd --- /dev/null +++ b/test/algorithms/legacy/test_min_eigen_optimizer.py @@ -0,0 +1,387 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test Min Eigen Optimizer with the legacy MinimumEigensolver""" + +import unittest +from test.optimization_test_case import QiskitOptimizationTestCase + +from test.runtime.fake_vqeruntime import FakeVQERuntimeProvider, FakeQAOARuntimeProvider + +import numpy as np +from ddt import data, ddt +from qiskit import BasicAer +from qiskit.algorithms import QAOA, VQE, NumPyMinimumEigensolver +from qiskit.algorithms.optimizers import COBYLA, SPSA +from qiskit.circuit.library import TwoLocal +from qiskit.providers.basicaer import QasmSimulatorPy +from qiskit.utils import QuantumInstance, algorithm_globals +import qiskit_optimization.optionals as _optionals +from qiskit_optimization.algorithms import ( + CplexOptimizer, + MinimumEigenOptimizer, + MinimumEigenOptimizationResult, +) +from qiskit_optimization.algorithms.optimization_algorithm import ( + OptimizationResultStatus, +) +from qiskit_optimization.converters import ( + InequalityToEquality, + IntegerToBinary, + LinearEqualityToPenalty, + MaximizeToMinimize, + QuadraticProgramToQubo, +) +from qiskit_optimization.problems import QuadraticProgram +from qiskit_optimization.runtime import VQEProgram, QAOAProgram + + +@ddt +class TestMinEigenOptimizer(QiskitOptimizationTestCase): + """Min Eigen Optimizer Tests.""" + + def setUp(self): + super().setUp() + + # setup minimum eigen solvers + self.min_eigen_solvers = {} + + # exact eigen solver + self.min_eigen_solvers["exact"] = NumPyMinimumEigensolver() + + # QAOA + optimizer = COBYLA() + self.min_eigen_solvers["qaoa"] = QAOA(optimizer=optimizer) + # simulators + self.sv_simulator = QuantumInstance( + BasicAer.get_backend("statevector_simulator"), + seed_simulator=123, + seed_transpiler=123, + ) + self.qasm_simulator = QuantumInstance( + BasicAer.get_backend("qasm_simulator"), + seed_simulator=123, + seed_transpiler=123, + ) + # test minimize + self.op_minimize = QuadraticProgram() + self.op_minimize.integer_var(0, 3, "x") + self.op_minimize.binary_var("y") + self.op_minimize.minimize(linear={"x": 1, "y": 2}) + self.op_minimize.linear_constraint(linear={"x": 1, "y": 1}, sense=">=", rhs=1, name="xy") + + # test maximize + self.op_maximize = QuadraticProgram() + self.op_maximize.integer_var(0, 3, "x") + self.op_maximize.binary_var("y") + self.op_maximize.maximize(linear={"x": 1, "y": 2}) + self.op_maximize.linear_constraint(linear={"x": 1, "y": 1}, sense="<=", rhs=1, name="xy") + + # test bit ordering + self.op_ordering = QuadraticProgram("bit ordering") + self.op_ordering.binary_var("x") + self.op_ordering.binary_var("y") + self.op_ordering.minimize(linear={"x": 1, "y": -2}) + + @data( + ("exact", None, "op_ip1.lp"), + ("qaoa", "statevector_simulator", "op_ip1.lp"), + ("qaoa", "qasm_simulator", "op_ip1.lp"), + ) + @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.") + def test_min_eigen_optimizer(self, config): + """Min Eigen Optimizer Test""" + try: + # unpack configuration + min_eigen_solver_name, backend, filename = config + + # get minimum eigen solver + min_eigen_solver = self.min_eigen_solvers[min_eigen_solver_name] + if backend: + min_eigen_solver.quantum_instance = BasicAer.get_backend(backend) + + # construct minimum eigen optimizer + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) + + # load optimization problem + problem = QuadraticProgram() + lp_file = self.get_resource_path(filename, "algorithms/resources") + problem.read_from_lp_file(lp_file) + + # solve problem with cplex + cplex = CplexOptimizer(cplex_parameters={"threads": 1, "randomseed": 1}) + cplex_result = cplex.solve(problem) + + # solve problem + result = min_eigen_optimizer.solve(problem) + self.assertIsNotNone(result) + + # analyze results + self.assertAlmostEqual(cplex_result.fval, result.fval) + + # check that eigensolver result is present + self.assertIsNotNone(result.min_eigen_solver_result) + except RuntimeError as ex: + self.fail(str(ex)) + + @data( + ("op_ip1.lp", -470, 12, OptimizationResultStatus.SUCCESS), + ("op_ip1.lp", np.inf, None, OptimizationResultStatus.FAILURE), + ) + @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.") + def test_min_eigen_optimizer_with_filter(self, config): + """Min Eigen Optimizer Test""" + try: + # unpack configuration + filename, lowerbound, fval, status = config + + # get minimum eigen solver + min_eigen_solver = NumPyMinimumEigensolver() + + # set filter + # pylint: disable=unused-argument + def filter_criterion(x, v, aux): + return v > lowerbound + + min_eigen_solver.filter_criterion = filter_criterion + + # construct minimum eigen optimizer + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) + + # load optimization problem + problem = QuadraticProgram() + lp_file = self.get_resource_path(filename, "algorithms/resources") + problem.read_from_lp_file(lp_file) + + # solve problem + result = min_eigen_optimizer.solve(problem) + self.assertIsNotNone(result) + + # analyze results + self.assertAlmostEqual(fval, result.fval) + self.assertEqual(status, result.status) + + # check that eigensolver result is present + self.assertIsNotNone(result.min_eigen_solver_result) + except RuntimeError as ex: + self.fail(str(ex)) + + def test_converter_list(self): + """Test converter list""" + op = QuadraticProgram() + op.integer_var(0, 3, "x") + op.binary_var("y") + + op.maximize(linear={"x": 1, "y": 2}) + op.linear_constraint(linear={"x": 1, "y": 1}, sense="LE", rhs=3, name="xy_leq") + min_eigen_solver = NumPyMinimumEigensolver() + # a single converter + qp2qubo = QuadraticProgramToQubo() + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver, converters=qp2qubo) + result = min_eigen_optimizer.solve(op) + self.assertEqual(result.fval, 4) + # a list of converters + ineq2eq = InequalityToEquality() + int2bin = IntegerToBinary() + penalize = LinearEqualityToPenalty() + max2min = MaximizeToMinimize() + converters = [ineq2eq, int2bin, penalize, max2min] + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver, converters=converters) + result = min_eigen_optimizer.solve(op) + self.assertEqual(result.fval, 4) + with self.assertRaises(TypeError): + invalid = [qp2qubo, "invalid converter"] + MinimumEigenOptimizer(min_eigen_solver, converters=invalid) + + def test_samples_numpy_eigen_solver(self): + """Test samples for NumPyMinimumEigensolver""" + # test minimize + min_eigen_solver = NumPyMinimumEigensolver() + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) + result = min_eigen_optimizer.solve(self.op_minimize) + opt_sol = 1 + success = OptimizationResultStatus.SUCCESS + self.assertEqual(result.fval, opt_sol) + self.assertEqual(len(result.samples), 1) + np.testing.assert_array_almost_equal(result.samples[0].x, [1, 0]) + self.assertAlmostEqual(result.samples[0].fval, opt_sol) + self.assertAlmostEqual(result.samples[0].probability, 1.0) + self.assertEqual(result.samples[0].status, success) + self.assertEqual(len(result.raw_samples), 1) + np.testing.assert_array_almost_equal(result.raw_samples[0].x, [1, 0, 0, 0, 0]) + self.assertAlmostEqual(result.raw_samples[0].fval, opt_sol) + self.assertAlmostEqual(result.raw_samples[0].probability, 1.0) + self.assertEqual(result.raw_samples[0].status, success) + # test maximize + min_eigen_solver = NumPyMinimumEigensolver() + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) + result = min_eigen_optimizer.solve(self.op_maximize) + opt_sol = 2 + self.assertEqual(result.fval, opt_sol) + self.assertEqual(len(result.samples), 1) + np.testing.assert_array_almost_equal(result.samples[0].x, [0, 1]) + self.assertAlmostEqual(result.samples[0].fval, opt_sol) + self.assertAlmostEqual(result.samples[0].probability, 1.0) + self.assertEqual(result.samples[0].status, success) + self.assertEqual(len(result.raw_samples), 1) + np.testing.assert_array_almost_equal(result.raw_samples[0].x, [0, 0, 1, 0]) + # optimizer internally deals with minimization problem + self.assertAlmostEqual( + self.op_maximize.objective.sense.value * result.raw_samples[0].fval, opt_sol + ) + self.assertAlmostEqual(result.raw_samples[0].probability, 1.0) + self.assertEqual(result.raw_samples[0].status, success) + + @data("sv", "qasm") + def test_samples_qaoa(self, simulator): + """Test samples for QAOA""" + # test minimize + algorithm_globals.random_seed = 4 + quantum_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator + qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + min_eigen_optimizer = MinimumEigenOptimizer(qaoa) + result = min_eigen_optimizer.solve(self.op_minimize) + success = OptimizationResultStatus.SUCCESS + opt_sol = 1 + self.assertAlmostEqual(sum(s.probability for s in result.samples), 1) + self.assertAlmostEqual(sum(s.probability for s in result.raw_samples), 1) + self.assertAlmostEqual(min(s.fval for s in result.samples), 0) + self.assertAlmostEqual(min(s.fval for s in result.samples if s.status == success), opt_sol) + self.assertAlmostEqual(min(s.fval for s in result.raw_samples), opt_sol) + for sample in result.raw_samples: + self.assertEqual(sample.status, success) + np.testing.assert_array_almost_equal(result.x, [1, 0]) + self.assertAlmostEqual(result.fval, result.samples[0].fval) + self.assertEqual(result.status, result.samples[0].status) + self.assertAlmostEqual(result.samples[0].fval, opt_sol) + self.assertEqual(result.samples[0].status, success) + np.testing.assert_array_almost_equal(result.raw_samples[0].x, [1, 0, 0, 0, 0]) + self.assertAlmostEqual(result.raw_samples[0].fval, opt_sol) + self.assertEqual(result.raw_samples[0].status, success) + # test maximize + opt_sol = 2 + qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + min_eigen_optimizer = MinimumEigenOptimizer(qaoa) + result = min_eigen_optimizer.solve(self.op_maximize) + self.assertAlmostEqual(sum(s.probability for s in result.samples), 1) + self.assertAlmostEqual(sum(s.probability for s in result.raw_samples), 1) + self.assertAlmostEqual(max(s.fval for s in result.samples), 5) + self.assertAlmostEqual(max(s.fval for s in result.samples if s.status == success), opt_sol) + # optimizer internally deals with minimization problem + self.assertAlmostEqual( + max(self.op_maximize.objective.sense.value * s.fval for s in result.raw_samples), + opt_sol, + ) + for sample in result.raw_samples: + self.assertEqual(sample.status, success) + np.testing.assert_array_almost_equal(result.x, [0, 1]) + self.assertEqual(result.fval, opt_sol) + self.assertEqual(result.status, success) + np.testing.assert_array_almost_equal(result.samples[0].x, [0, 1]) + self.assertAlmostEqual(result.samples[0].fval, opt_sol) + self.assertEqual(result.samples[0].status, success) + np.testing.assert_array_almost_equal(result.raw_samples[0].x, [0, 0, 1, 0]) + # optimizer internally deals with minimization problem + self.assertAlmostEqual( + self.op_maximize.objective.sense.value * result.raw_samples[0].fval, opt_sol + ) + self.assertEqual(result.raw_samples[0].status, success) + # test bit ordering + opt_sol = -2 + qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + min_eigen_optimizer = MinimumEigenOptimizer(qaoa) + result = min_eigen_optimizer.solve(self.op_ordering) + self.assertEqual(result.fval, opt_sol) + np.testing.assert_array_almost_equal(result.x, [0, 1]) + self.assertEqual(result.status, success) + result.raw_samples.sort(key=lambda x: x.probability, reverse=True) + np.testing.assert_array_almost_equal(result.x, result.raw_samples[0].x) + self.assertAlmostEqual(sum(s.probability for s in result.samples), 1, delta=1e-5) + self.assertAlmostEqual(sum(s.probability for s in result.raw_samples), 1, delta=1e-5) + self.assertAlmostEqual(min(s.fval for s in result.samples), -2) + self.assertAlmostEqual(min(s.fval for s in result.samples if s.status == success), opt_sol) + self.assertAlmostEqual(min(s.fval for s in result.raw_samples), opt_sol) + for sample in result.raw_samples: + self.assertEqual(sample.status, success) + np.testing.assert_array_almost_equal(result.samples[0].x, [0, 1]) + self.assertAlmostEqual(result.samples[0].fval, opt_sol) + self.assertEqual(result.samples[0].status, success) + np.testing.assert_array_almost_equal(result.raw_samples[0].x, [0, 1]) + self.assertAlmostEqual(result.raw_samples[0].fval, opt_sol) + self.assertEqual(result.raw_samples[0].status, success) + + @data("sv", "qasm") + def test_samples_vqe(self, simulator): + """Test samples for VQE""" + # test minimize + algorithm_globals.random_seed = 1 + quantum_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator + opt_sol = -2 + success = OptimizationResultStatus.SUCCESS + optimizer = SPSA(maxiter=100) + ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") + vqe_mes = VQE(ry_ansatz, optimizer=optimizer, quantum_instance=quantum_instance) + vqe = MinimumEigenOptimizer(vqe_mes) + results = vqe.solve(self.op_ordering) + self.assertEqual(results.fval, opt_sol) + np.testing.assert_array_almost_equal(results.x, [0, 1]) + self.assertEqual(results.status, success) + results.raw_samples.sort(key=lambda x: x.probability, reverse=True) + np.testing.assert_array_almost_equal(results.x, results.raw_samples[0].x) + self.assertAlmostEqual(sum(s.probability for s in results.samples), 1, delta=1e-5) + self.assertAlmostEqual(sum(s.probability for s in results.raw_samples), 1, delta=1e-5) + self.assertAlmostEqual(min(s.fval for s in results.samples), -2) + self.assertAlmostEqual(min(s.fval for s in results.samples if s.status == success), opt_sol) + self.assertAlmostEqual(min(s.fval for s in results.raw_samples), opt_sol) + for sample in results.raw_samples: + self.assertEqual(sample.status, success) + np.testing.assert_array_almost_equal(results.samples[0].x, [0, 1]) + self.assertAlmostEqual(results.samples[0].fval, opt_sol) + self.assertEqual(results.samples[0].status, success) + np.testing.assert_array_almost_equal(results.raw_samples[0].x, [0, 1]) + self.assertAlmostEqual(results.raw_samples[0].fval, opt_sol) + self.assertEqual(results.raw_samples[0].status, success) + + @data("vqe", "qaoa") + def test_runtime(self, subroutine): + """Test vqe and qaoa runtime""" + optimizer = {"name": "SPSA", "maxiter": 100} + backend = QasmSimulatorPy() + + if subroutine == "vqe": + ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") + initial_point = np.random.default_rng(42).random(ry_ansatz.num_parameters) + solver = VQEProgram( + ansatz=ry_ansatz, + optimizer=optimizer, + initial_point=initial_point, + backend=backend, + provider=FakeVQERuntimeProvider(), + ) + else: + reps = 2 + initial_point = np.random.default_rng(42).random(2 * reps) + solver = QAOAProgram( + optimizer=optimizer, + reps=reps, + initial_point=initial_point, + backend=backend, + provider=FakeQAOARuntimeProvider(), + ) + + opt = MinimumEigenOptimizer(solver) + result = opt.solve(self.op_ordering) + self.assertIsInstance(result, MinimumEigenOptimizationResult) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/algorithms/test_min_eigen_optimizer.py b/test/algorithms/test_min_eigen_optimizer.py index 257fe7fe4..e8193623a 100644 --- a/test/algorithms/test_min_eigen_optimizer.py +++ b/test/algorithms/test_min_eigen_optimizer.py @@ -10,30 +10,29 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" Test Min Eigen Optimizer """ +""" Test Min Eigen Optimizer with the primitive-based minimum eigensolver """ import unittest from test.optimization_test_case import QiskitOptimizationTestCase - -from test.runtime.fake_vqeruntime import FakeVQERuntimeProvider, FakeQAOARuntimeProvider +from test.runtime.fake_vqeruntime import FakeQAOARuntimeProvider, FakeVQERuntimeProvider import numpy as np -from ddt import data, ddt -from qiskit import BasicAer -from qiskit.algorithms import QAOA, VQE, NumPyMinimumEigensolver +from ddt import data, ddt, unpack +from qiskit.algorithms.minimum_eigensolvers import QAOA, NumPyMinimumEigensolver, SamplingVQE +from qiskit.algorithms.minimum_eigen_solvers import VQE from qiskit.algorithms.optimizers import COBYLA, SPSA from qiskit.circuit.library import TwoLocal +from qiskit.primitives import Sampler from qiskit.providers.basicaer import QasmSimulatorPy -from qiskit.utils import QuantumInstance, algorithm_globals +from qiskit.utils import algorithm_globals + import qiskit_optimization.optionals as _optionals from qiskit_optimization.algorithms import ( CplexOptimizer, - MinimumEigenOptimizer, MinimumEigenOptimizationResult, + MinimumEigenOptimizer, ) -from qiskit_optimization.algorithms.optimization_algorithm import ( - OptimizationResultStatus, -) +from qiskit_optimization.algorithms.optimization_algorithm import OptimizationResultStatus from qiskit_optimization.converters import ( InequalityToEquality, IntegerToBinary, @@ -42,7 +41,7 @@ QuadraticProgramToQubo, ) from qiskit_optimization.problems import QuadraticProgram -from qiskit_optimization.runtime import VQEProgram, QAOAProgram +from qiskit_optimization.runtime import QAOAClient, VQEClient @ddt @@ -52,26 +51,14 @@ class TestMinEigenOptimizer(QiskitOptimizationTestCase): def setUp(self): super().setUp() + self._seed = 123 + # setup minimum eigen solvers - self.min_eigen_solvers = {} - - # exact eigen solver - self.min_eigen_solvers["exact"] = NumPyMinimumEigensolver() - - # QAOA - optimizer = COBYLA() - self.min_eigen_solvers["qaoa"] = QAOA(optimizer=optimizer) - # simulators - self.sv_simulator = QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - seed_simulator=123, - seed_transpiler=123, - ) - self.qasm_simulator = QuantumInstance( - BasicAer.get_backend("qasm_simulator"), - seed_simulator=123, - seed_transpiler=123, - ) + self.min_eigen_solvers = { + "exact": NumPyMinimumEigensolver(), + "qaoa": QAOA(sampler=Sampler(), optimizer=COBYLA()), + } + # test minimize self.op_minimize = QuadraticProgram() self.op_minimize.integer_var(0, 3, "x") @@ -94,20 +81,19 @@ def setUp(self): @data( ("exact", None, "op_ip1.lp"), - ("qaoa", "statevector_simulator", "op_ip1.lp"), - ("qaoa", "qasm_simulator", "op_ip1.lp"), + ("qaoa", None, "op_ip1.lp"), + ("qaoa", 10000, "op_ip1.lp"), ) + @unpack @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.") - def test_min_eigen_optimizer(self, config): + def test_min_eigen_optimizer(self, min_eigen_solver_name, shots, filename): """Min Eigen Optimizer Test""" try: - # unpack configuration - min_eigen_solver_name, backend, filename = config - # get minimum eigen solver min_eigen_solver = self.min_eigen_solvers[min_eigen_solver_name] - if backend: - min_eigen_solver.quantum_instance = BasicAer.get_backend(backend) + if min_eigen_solver_name == "qaoa": + min_eigen_solver.sampler.options.shots = shots + min_eigen_solver.sampler.options.seed = self._seed # construct minimum eigen optimizer min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) @@ -137,13 +123,11 @@ def test_min_eigen_optimizer(self, config): ("op_ip1.lp", -470, 12, OptimizationResultStatus.SUCCESS), ("op_ip1.lp", np.inf, None, OptimizationResultStatus.FAILURE), ) + @unpack @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.") - def test_min_eigen_optimizer_with_filter(self, config): + def test_min_eigen_optimizer_with_filter(self, filename, lowerbound, fval, status): """Min Eigen Optimizer Test""" try: - # unpack configuration - filename, lowerbound, fval, status = config - # get minimum eigen solver min_eigen_solver = NumPyMinimumEigensolver() @@ -241,13 +225,12 @@ def test_samples_numpy_eigen_solver(self): self.assertAlmostEqual(result.raw_samples[0].probability, 1.0) self.assertEqual(result.raw_samples[0].status, success) - @data("sv", "qasm") - def test_samples_qaoa(self, simulator): + def test_samples_qaoa(self): """Test samples for QAOA""" # test minimize algorithm_globals.random_seed = 4 - quantum_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator - qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + sampler = Sampler() + qaoa = QAOA(sampler=sampler, optimizer=COBYLA(), reps=2) min_eigen_optimizer = MinimumEigenOptimizer(qaoa) result = min_eigen_optimizer.solve(self.op_minimize) success = OptimizationResultStatus.SUCCESS @@ -269,7 +252,7 @@ def test_samples_qaoa(self, simulator): self.assertEqual(result.raw_samples[0].status, success) # test maximize opt_sol = 2 - qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + qaoa = QAOA(sampler=sampler, optimizer=COBYLA(), reps=2) min_eigen_optimizer = MinimumEigenOptimizer(qaoa) result = min_eigen_optimizer.solve(self.op_maximize) self.assertAlmostEqual(sum(s.probability for s in result.samples), 1) @@ -297,7 +280,7 @@ def test_samples_qaoa(self, simulator): self.assertEqual(result.raw_samples[0].status, success) # test bit ordering opt_sol = -2 - qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + qaoa = QAOA(sampler=sampler, optimizer=COBYLA(), reps=2) min_eigen_optimizer = MinimumEigenOptimizer(qaoa) result = min_eigen_optimizer.solve(self.op_ordering) self.assertEqual(result.fval, opt_sol) @@ -319,17 +302,16 @@ def test_samples_qaoa(self, simulator): self.assertAlmostEqual(result.raw_samples[0].fval, opt_sol) self.assertEqual(result.raw_samples[0].status, success) - @data("sv", "qasm") - def test_samples_vqe(self, simulator): + def test_samples_vqe(self): """Test samples for VQE""" # test minimize algorithm_globals.random_seed = 1 - quantum_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator opt_sol = -2 success = OptimizationResultStatus.SUCCESS optimizer = SPSA(maxiter=100) ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") - vqe_mes = VQE(ry_ansatz, optimizer=optimizer, quantum_instance=quantum_instance) + sampler = Sampler() + vqe_mes = SamplingVQE(sampler, ry_ansatz, optimizer=optimizer) vqe = MinimumEigenOptimizer(vqe_mes) results = vqe.solve(self.op_ordering) self.assertEqual(results.fval, opt_sol) @@ -360,7 +342,7 @@ def test_runtime(self, subroutine): if subroutine == "vqe": ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") initial_point = np.random.default_rng(42).random(ry_ansatz.num_parameters) - solver = VQEProgram( + solver = VQEClient( ansatz=ry_ansatz, optimizer=optimizer, initial_point=initial_point, @@ -370,7 +352,7 @@ def test_runtime(self, subroutine): else: reps = 2 initial_point = np.random.default_rng(42).random(2 * reps) - solver = QAOAProgram( + solver = QAOAClient( optimizer=optimizer, reps=reps, initial_point=initial_point, @@ -382,6 +364,14 @@ def test_runtime(self, subroutine): result = opt.solve(self.op_ordering) self.assertIsInstance(result, MinimumEigenOptimizationResult) + def test_deprecation(self): + """Test deprecation warning""" + optimizer = SPSA(maxiter=100) + ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") + vqe_mes = VQE(ry_ansatz, optimizer=optimizer, quantum_instance=QasmSimulatorPy()) + with self.assertWarns(DeprecationWarning): + _ = MinimumEigenOptimizer(vqe_mes) + if __name__ == "__main__": unittest.main() From 7795bbc8bdf6c0dab73e5be28eefde8387f42d75 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Fri, 11 Nov 2022 19:35:57 +0900 Subject: [PATCH 02/19] update other optimizers --- README.md | 11 +++++----- .../algorithms/admm_optimizer.py | 16 +++++++------- .../recursive_minimum_eigen_optimizer.py | 22 +++++++------------ .../algorithms/warm_start_qaoa_optimizer.py | 17 +++++--------- 4 files changed, 26 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 5e55d38ce..cedd16911 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,9 @@ from docplex.mp.model import Model from qiskit_optimization.algorithms import MinimumEigenOptimizer from qiskit_optimization.translators import from_docplex_mp -from qiskit.utils import algorithm_globals, QuantumInstance -from qiskit import BasicAer -from qiskit.algorithms import QAOA +from qiskit.utils import algorithm_globals +from qiskit.primitives import Sampler +from qiskit.algorithms.minimum_eigensolvers import QAOA from qiskit.algorithms.optimizers import SPSA # Generate a graph of 4 nodes @@ -97,9 +97,8 @@ seed = 1234 algorithm_globals.random_seed = seed spsa = SPSA(maxiter=250) -backend = BasicAer.get_backend('qasm_simulator') -q_i = QuantumInstance(backend=backend, seed_simulator=seed, seed_transpiler=seed) -qaoa = QAOA(optimizer=spsa, reps=5, quantum_instance=q_i) +sampler = Sampler() +qaoa = QAOA(sampler=sampler, optimizer=spsa, reps=5) algorithm = MinimumEigenOptimizer(qaoa) result = algorithm.solve(problem) print(result.prettyprint()) # prints solution, x=[1, 0, 1, 0], the cost, fval=4 diff --git a/qiskit_optimization/algorithms/admm_optimizer.py b/qiskit_optimization/algorithms/admm_optimizer.py index 40c3911e7..881e05be7 100644 --- a/qiskit_optimization/algorithms/admm_optimizer.py +++ b/qiskit_optimization/algorithms/admm_optimizer.py @@ -17,21 +17,21 @@ from typing import List, Optional, Tuple, cast import numpy as np -from qiskit.algorithms import NumPyMinimumEigensolver +from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver +from ..converters import MaximizeToMinimize +from ..problems.constraint import Constraint +from ..problems.linear_constraint import LinearConstraint +from ..problems.linear_expression import LinearExpression +from ..problems.quadratic_program import QuadraticProgram +from ..problems.variable import Variable, VarType from .minimum_eigen_optimizer import MinimumEigenOptimizer from .optimization_algorithm import ( - OptimizationResultStatus, OptimizationAlgorithm, OptimizationResult, + OptimizationResultStatus, ) from .slsqp_optimizer import SlsqpOptimizer -from ..problems.constraint import Constraint -from ..problems.linear_constraint import LinearConstraint -from ..problems.linear_expression import LinearExpression -from ..problems.quadratic_program import QuadraticProgram -from ..problems.variable import VarType, Variable -from ..converters import MaximizeToMinimize UPDATE_RHO_BY_TEN_PERCENT = 0 UPDATE_RHO_BY_RESIDUALS = 1 diff --git a/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py b/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py index 26c1184a6..737fc56d9 100644 --- a/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py +++ b/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py @@ -14,28 +14,22 @@ from copy import deepcopy from enum import Enum -from typing import Optional, Union, List, Tuple, Dict, cast +from typing import Dict, List, Optional, Tuple, Union, cast import numpy as np -from qiskit.algorithms import NumPyMinimumEigensolver +from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver from qiskit.utils.validation import validate_min -from .minimum_eigen_optimizer import ( - MinimumEigenOptimizer, - MinimumEigenOptimizationResult, -) +from ..converters.quadratic_program_to_qubo import QuadraticProgramConverter, QuadraticProgramToQubo +from ..exceptions import QiskitOptimizationError +from ..problems import Variable +from ..problems.quadratic_program import QuadraticProgram +from .minimum_eigen_optimizer import MinimumEigenOptimizationResult, MinimumEigenOptimizer from .optimization_algorithm import ( - OptimizationResultStatus, OptimizationAlgorithm, OptimizationResult, + OptimizationResultStatus, ) -from ..converters.quadratic_program_to_qubo import ( - QuadraticProgramToQubo, - QuadraticProgramConverter, -) -from ..exceptions import QiskitOptimizationError -from ..problems import Variable -from ..problems.quadratic_program import QuadraticProgram class IntermediateResult(Enum): diff --git a/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py b/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py index 98016cbdd..62dbbe9a5 100644 --- a/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py +++ b/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 2022. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -14,26 +14,19 @@ import copy from abc import ABC, abstractmethod -from typing import Optional, List, Union, Dict, Tuple, cast +from typing import Dict, List, Optional, Tuple, Union, cast import numpy as np from qiskit import QuantumCircuit -from qiskit.algorithms import QAOA +from qiskit.algorithms.minimum_eigensolvers import QAOA from qiskit.circuit import Parameter -from .minimum_eigen_optimizer import ( - MinimumEigenOptimizer, - MinimumEigenOptimizationResult, -) -from .optimization_algorithm import ( - OptimizationAlgorithm, - OptimizationResultStatus, - SolutionSample, -) from ..converters.quadratic_program_converter import QuadraticProgramConverter from ..exceptions import QiskitOptimizationError from ..problems.quadratic_program import QuadraticProgram from ..problems.variable import VarType +from .minimum_eigen_optimizer import MinimumEigenOptimizationResult, MinimumEigenOptimizer +from .optimization_algorithm import OptimizationAlgorithm, OptimizationResultStatus, SolutionSample class BaseAggregator(ABC): From c52c4b5ae9bf09c5693f52bd2048e96b7ac4a33e Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Fri, 11 Nov 2022 22:19:28 +0900 Subject: [PATCH 03/19] update other tests --- qiskit_optimization/deprecation.py | 11 +- .../legacy/test_min_eigen_optimizer.py | 10 + .../legacy/test_recursive_optimization.py | 201 ++++++++++++++++++ .../algorithms/legacy/test_warm_start_qaoa.py | 134 ++++++++++++ test/algorithms/test_min_eigen_optimizer.py | 9 - .../algorithms/test_recursive_optimization.py | 29 ++- test/algorithms/test_warm_start_qaoa.py | 21 +- test/converters/test_converters.py | 14 +- 8 files changed, 380 insertions(+), 49 deletions(-) create mode 100644 test/algorithms/legacy/test_recursive_optimization.py create mode 100644 test/algorithms/legacy/test_warm_start_qaoa.py mode change 100755 => 100644 test/algorithms/test_recursive_optimization.py diff --git a/qiskit_optimization/deprecation.py b/qiskit_optimization/deprecation.py index c87ab4f7c..c14b7d72d 100644 --- a/qiskit_optimization/deprecation.py +++ b/qiskit_optimization/deprecation.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 2022. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -421,3 +421,12 @@ def deprecate_function( return _deprecate_object( version, DeprecatedType.FUNCTION, new_type, new_name, additional_msg, stack_level ) + + +def clear_deprecated_objects() -> None: + """Clear deprecated object cache + + Returns: + None + """ + _DEPRECATED_OBJECTS.clear() diff --git a/test/algorithms/legacy/test_min_eigen_optimizer.py b/test/algorithms/legacy/test_min_eigen_optimizer.py index 94c397bdd..e4e7e529d 100644 --- a/test/algorithms/legacy/test_min_eigen_optimizer.py +++ b/test/algorithms/legacy/test_min_eigen_optimizer.py @@ -43,6 +43,7 @@ ) from qiskit_optimization.problems import QuadraticProgram from qiskit_optimization.runtime import VQEProgram, QAOAProgram +from qiskit_optimization.deprecation import clear_deprecated_objects @ddt @@ -382,6 +383,15 @@ def test_runtime(self, subroutine): result = opt.solve(self.op_ordering) self.assertIsInstance(result, MinimumEigenOptimizationResult) + def test_deprecation(self): + """Test deprecation warning""" + clear_deprecated_objects() + optimizer = SPSA(maxiter=100) + ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") + vqe_mes = VQE(ry_ansatz, optimizer=optimizer, quantum_instance=QasmSimulatorPy()) + with self.assertWarns(DeprecationWarning): + _ = MinimumEigenOptimizer(vqe_mes) + if __name__ == "__main__": unittest.main() diff --git a/test/algorithms/legacy/test_recursive_optimization.py b/test/algorithms/legacy/test_recursive_optimization.py new file mode 100644 index 000000000..efe8399e6 --- /dev/null +++ b/test/algorithms/legacy/test_recursive_optimization.py @@ -0,0 +1,201 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Test Recursive Min Eigen Optimizer with legacy MinimumEigensolver.""" + +import unittest +from test import QiskitOptimizationTestCase + +import numpy as np + +from qiskit import BasicAer +from qiskit.utils import algorithm_globals, QuantumInstance + +from qiskit.algorithms import NumPyMinimumEigensolver, QAOA + +import qiskit_optimization.optionals as _optionals +from qiskit_optimization.algorithms import ( + MinimumEigenOptimizer, + CplexOptimizer, + RecursiveMinimumEigenOptimizer, + WarmStartQAOAOptimizer, + SlsqpOptimizer, +) +from qiskit_optimization.algorithms.recursive_minimum_eigen_optimizer import ( + IntermediateResult, +) +from qiskit_optimization.problems import QuadraticProgram +from qiskit_optimization.converters import ( + IntegerToBinary, + InequalityToEquality, + LinearEqualityToPenalty, + QuadraticProgramToQubo, +) + + +class TestRecursiveMinEigenOptimizer(QiskitOptimizationTestCase): + """Recursive Min Eigen Optimizer Tests.""" + + @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.") + def test_recursive_min_eigen_optimizer(self): + """Test the recursive minimum eigen optimizer.""" + filename = "op_ip1.lp" + # get minimum eigen solver + min_eigen_solver = NumPyMinimumEigensolver() + + # construct minimum eigen optimizer + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, min_num_vars=4 + ) + + # load optimization problem + problem = QuadraticProgram() + lp_file = self.get_resource_path(filename, "algorithms/resources") + problem.read_from_lp_file(lp_file) + + # solve problem with cplex + cplex = CplexOptimizer() + cplex_result = cplex.solve(problem) + + # solve problem + result = recursive_min_eigen_optimizer.solve(problem) + + # analyze results + np.testing.assert_array_almost_equal(cplex_result.x, result.x, 4) + self.assertAlmostEqual(cplex_result.fval, result.fval) + + @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.") + def test_recursive_history(self): + """Tests different options for history.""" + filename = "op_ip1.lp" + # load optimization problem + problem = QuadraticProgram() + lp_file = self.get_resource_path(filename, "algorithms/resources") + problem.read_from_lp_file(lp_file) + + # get minimum eigen solver + min_eigen_solver = NumPyMinimumEigensolver() + + # construct minimum eigen optimizer + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) + + # no history + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, + min_num_vars=4, + history=IntermediateResult.NO_ITERATIONS, + ) + result = recursive_min_eigen_optimizer.solve(problem) + self.assertIsNotNone(result.replacements) + self.assertIsNotNone(result.history) + self.assertIsNotNone(result.history[0]) + self.assertEqual(len(result.history[0]), 0) + self.assertIsNone(result.history[1]) + + # only last iteration in the history + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, + min_num_vars=4, + history=IntermediateResult.LAST_ITERATION, + ) + result = recursive_min_eigen_optimizer.solve(problem) + self.assertIsNotNone(result.replacements) + self.assertIsNotNone(result.history) + self.assertIsNotNone(result.history[0]) + self.assertEqual(len(result.history[0]), 0) + self.assertIsNotNone(result.history[1]) + + # full history + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, + min_num_vars=4, + history=IntermediateResult.ALL_ITERATIONS, + ) + result = recursive_min_eigen_optimizer.solve(problem) + self.assertIsNotNone(result.replacements) + self.assertIsNotNone(result.history) + self.assertIsNotNone(result.history[0]) + self.assertGreater(len(result.history[0]), 1) + self.assertIsNotNone(result.history[1]) + + @unittest.skipIf(not _optionals.HAS_CPLEX, "CPLEX not available.") + def test_recursive_warm_qaoa(self): + """Test the recursive optimizer with warm start qaoa.""" + seed = 1234 + algorithm_globals.random_seed = seed + backend = BasicAer.get_backend("statevector_simulator") + qaoa = QAOA( + quantum_instance=QuantumInstance( + backend=backend, seed_simulator=seed, seed_transpiler=seed + ), + reps=1, + ) + warm_qaoa = WarmStartQAOAOptimizer( + pre_solver=SlsqpOptimizer(), relax_for_pre_solver=True, qaoa=qaoa + ) + + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer(warm_qaoa, min_num_vars=4) + + # load optimization problem + problem = QuadraticProgram() + lp_file = self.get_resource_path("op_ip1.lp", "algorithms/resources") + problem.read_from_lp_file(lp_file) + + # solve problem with cplex + cplex = CplexOptimizer(cplex_parameters={"threads": 1, "randomseed": 1}) + cplex_result = cplex.solve(problem) + + # solve problem + result = recursive_min_eigen_optimizer.solve(problem) + + # analyze results + np.testing.assert_array_almost_equal(cplex_result.x, result.x, 4) + self.assertAlmostEqual(cplex_result.fval, result.fval) + + def test_converter_list(self): + """Test converter list""" + op = QuadraticProgram() + op.integer_var(0, 3, "x") + op.binary_var("y") + + op.maximize(linear={"x": 1, "y": 2}) + op.linear_constraint(linear={"y": 1, "x": 1}, sense="LE", rhs=3, name="xy_leq") + + # construct minimum eigen optimizer + min_eigen_solver = NumPyMinimumEigensolver() + min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) + # a single converter + qp2qubo = QuadraticProgramToQubo() + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, min_num_vars=2, converters=qp2qubo + ) + result = recursive_min_eigen_optimizer.solve(op) + self.assertEqual(result.fval, 4) + # a list of converters + ineq2eq = InequalityToEquality() + int2bin = IntegerToBinary() + penalize = LinearEqualityToPenalty() + converters = [ineq2eq, int2bin, penalize] + recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( + min_eigen_optimizer, min_num_vars=2, converters=converters + ) + result = recursive_min_eigen_optimizer.solve(op) + self.assertEqual(result.fval, 4) + # invalid converters + with self.assertRaises(TypeError): + invalid = [qp2qubo, "invalid converter"] + RecursiveMinimumEigenOptimizer(min_eigen_optimizer, min_num_vars=2, converters=invalid) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/algorithms/legacy/test_warm_start_qaoa.py b/test/algorithms/legacy/test_warm_start_qaoa.py new file mode 100644 index 000000000..550cf0cc8 --- /dev/null +++ b/test/algorithms/legacy/test_warm_start_qaoa.py @@ -0,0 +1,134 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2021, 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test warm start QAOA optimizer with legacy MinimumEigensolver. """ + +import unittest +from test import QiskitOptimizationTestCase + +import numpy as np + +from docplex.mp.model import Model +from qiskit import BasicAer +from qiskit.algorithms import QAOA + +import qiskit_optimization.optionals as _optionals +from qiskit_optimization.algorithms import SlsqpOptimizer +from qiskit_optimization.algorithms.goemans_williamson_optimizer import ( + GoemansWilliamsonOptimizer, +) +from qiskit_optimization.algorithms.warm_start_qaoa_optimizer import ( + MeanAggregator, + WarmStartQAOAOptimizer, +) +from qiskit_optimization.applications.max_cut import Maxcut +from qiskit_optimization.translators import from_docplex_mp + + +class TestWarmStartQAOAOptimizer(QiskitOptimizationTestCase): + """Tests for the warm start QAOA optimizer.""" + + @unittest.skipIf(not _optionals.HAS_CVXPY, "CVXPY not available.") + def test_max_cut(self): + """Basic test on the max cut problem.""" + graph = np.array( + [ + [0.0, 1.0, 2.0, 0.0], + [1.0, 0.0, 1.0, 0.0], + [2.0, 1.0, 0.0, 1.0], + [0.0, 0.0, 1.0, 0.0], + ] + ) + + presolver = GoemansWilliamsonOptimizer(num_cuts=10) + problem = Maxcut(graph).to_quadratic_program() + + backend = BasicAer.get_backend("statevector_simulator") + qaoa = QAOA(quantum_instance=backend, reps=1) + aggregator = MeanAggregator() + optimizer = WarmStartQAOAOptimizer( + pre_solver=presolver, + relax_for_pre_solver=False, + qaoa=qaoa, + epsilon=0.25, + num_initial_solutions=10, + aggregator=aggregator, + ) + result_warm = optimizer.solve(problem) + + self.assertIsNotNone(result_warm) + self.assertIsNotNone(result_warm.x) + np.testing.assert_almost_equal([0, 0, 1, 0], result_warm.x, 3) + self.assertIsNotNone(result_warm.fval) + np.testing.assert_almost_equal(4, result_warm.fval, 3) + + def test_constrained_binary(self): + """Constrained binary optimization problem.""" + model = Model() + v = model.binary_var(name="v") + w = model.binary_var(name="w") + # pylint:disable=invalid-name + t = model.binary_var(name="t") + + model.minimize(v + w + t) + model.add_constraint(2 * v + 10 * w + t <= 3, "cons1") + model.add_constraint(v + w + t >= 2, "cons2") + + problem = from_docplex_mp(model) + + backend = BasicAer.get_backend("statevector_simulator") + qaoa = QAOA(quantum_instance=backend, reps=1) + aggregator = MeanAggregator() + optimizer = WarmStartQAOAOptimizer( + pre_solver=SlsqpOptimizer(), + relax_for_pre_solver=True, + qaoa=qaoa, + epsilon=0.25, + aggregator=aggregator, + ) + result_warm = optimizer.solve(problem) + + self.assertIsNotNone(result_warm) + self.assertIsNotNone(result_warm.x) + np.testing.assert_almost_equal([1, 0, 1], result_warm.x, 3) + self.assertIsNotNone(result_warm.fval) + np.testing.assert_almost_equal(2, result_warm.fval, 3) + + def test_simple_qubo(self): + """Test on a simple QUBO problem.""" + model = Model() + # pylint:disable=invalid-name + u = model.binary_var(name="u") + v = model.binary_var(name="v") + + model.minimize((u - v + 2) ** 2) + problem = from_docplex_mp(model) + + backend = BasicAer.get_backend("statevector_simulator") + qaoa = QAOA(quantum_instance=backend, reps=1) + optimizer = WarmStartQAOAOptimizer( + pre_solver=SlsqpOptimizer(), + relax_for_pre_solver=True, + qaoa=qaoa, + epsilon=0.25, + ) + result_warm = optimizer.solve(problem) + + self.assertIsNotNone(result_warm) + self.assertIsNotNone(result_warm.x) + np.testing.assert_almost_equal([0, 1], result_warm.x, 3) + self.assertIsNotNone(result_warm.fval) + np.testing.assert_almost_equal(1, result_warm.fval, 3) + + +if __name__ == "__main__": + unittest.main() diff --git a/test/algorithms/test_min_eigen_optimizer.py b/test/algorithms/test_min_eigen_optimizer.py index e8193623a..a5b518e78 100644 --- a/test/algorithms/test_min_eigen_optimizer.py +++ b/test/algorithms/test_min_eigen_optimizer.py @@ -19,7 +19,6 @@ import numpy as np from ddt import data, ddt, unpack from qiskit.algorithms.minimum_eigensolvers import QAOA, NumPyMinimumEigensolver, SamplingVQE -from qiskit.algorithms.minimum_eigen_solvers import VQE from qiskit.algorithms.optimizers import COBYLA, SPSA from qiskit.circuit.library import TwoLocal from qiskit.primitives import Sampler @@ -364,14 +363,6 @@ def test_runtime(self, subroutine): result = opt.solve(self.op_ordering) self.assertIsInstance(result, MinimumEigenOptimizationResult) - def test_deprecation(self): - """Test deprecation warning""" - optimizer = SPSA(maxiter=100) - ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") - vqe_mes = VQE(ry_ansatz, optimizer=optimizer, quantum_instance=QasmSimulatorPy()) - with self.assertWarns(DeprecationWarning): - _ = MinimumEigenOptimizer(vqe_mes) - if __name__ == "__main__": unittest.main() diff --git a/test/algorithms/test_recursive_optimization.py b/test/algorithms/test_recursive_optimization.py old mode 100755 new mode 100644 index 53e206b7b..ec8c6d9cc --- a/test/algorithms/test_recursive_optimization.py +++ b/test/algorithms/test_recursive_optimization.py @@ -10,36 +10,33 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -"""Test Recursive Min Eigen Optimizer.""" +"""Test Recursive Min Eigen Optimizer with the primitive-based minimum eigensolver.""" import unittest from test import QiskitOptimizationTestCase import numpy as np - -from qiskit import BasicAer -from qiskit.utils import algorithm_globals, QuantumInstance - -from qiskit.algorithms import NumPyMinimumEigensolver, QAOA +from qiskit.algorithms.minimum_eigensolvers import QAOA, NumPyMinimumEigensolver +from qiskit.algorithms.optimizers import SLSQP +from qiskit.primitives import Sampler +from qiskit.utils import algorithm_globals import qiskit_optimization.optionals as _optionals from qiskit_optimization.algorithms import ( - MinimumEigenOptimizer, CplexOptimizer, + MinimumEigenOptimizer, RecursiveMinimumEigenOptimizer, - WarmStartQAOAOptimizer, SlsqpOptimizer, + WarmStartQAOAOptimizer, ) -from qiskit_optimization.algorithms.recursive_minimum_eigen_optimizer import ( - IntermediateResult, -) -from qiskit_optimization.problems import QuadraticProgram +from qiskit_optimization.algorithms.recursive_minimum_eigen_optimizer import IntermediateResult from qiskit_optimization.converters import ( - IntegerToBinary, InequalityToEquality, + IntegerToBinary, LinearEqualityToPenalty, QuadraticProgramToQubo, ) +from qiskit_optimization.problems import QuadraticProgram class TestRecursiveMinEigenOptimizer(QiskitOptimizationTestCase): @@ -133,11 +130,9 @@ def test_recursive_warm_qaoa(self): """Test the recursive optimizer with warm start qaoa.""" seed = 1234 algorithm_globals.random_seed = seed - backend = BasicAer.get_backend("statevector_simulator") qaoa = QAOA( - quantum_instance=QuantumInstance( - backend=backend, seed_simulator=seed, seed_transpiler=seed - ), + sampler=Sampler(), + optimizer=SLSQP(), reps=1, ) warm_qaoa = WarmStartQAOAOptimizer( diff --git a/test/algorithms/test_warm_start_qaoa.py b/test/algorithms/test_warm_start_qaoa.py index 39f0dba0c..be83a392b 100644 --- a/test/algorithms/test_warm_start_qaoa.py +++ b/test/algorithms/test_warm_start_qaoa.py @@ -10,22 +10,20 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" Test warm start QAOA optimizer. """ +""" Test warm start QAOA optimizer with the primitive-based minimum eigensolver. """ import unittest from test import QiskitOptimizationTestCase import numpy as np - from docplex.mp.model import Model -from qiskit import BasicAer -from qiskit.algorithms import QAOA +from qiskit.algorithms.minimum_eigensolvers import QAOA +from qiskit.algorithms.optimizers import SLSQP +from qiskit.primitives.sampler import Sampler import qiskit_optimization.optionals as _optionals from qiskit_optimization.algorithms import SlsqpOptimizer -from qiskit_optimization.algorithms.goemans_williamson_optimizer import ( - GoemansWilliamsonOptimizer, -) +from qiskit_optimization.algorithms.goemans_williamson_optimizer import GoemansWilliamsonOptimizer from qiskit_optimization.algorithms.warm_start_qaoa_optimizer import ( MeanAggregator, WarmStartQAOAOptimizer, @@ -52,8 +50,7 @@ def test_max_cut(self): presolver = GoemansWilliamsonOptimizer(num_cuts=10) problem = Maxcut(graph).to_quadratic_program() - backend = BasicAer.get_backend("statevector_simulator") - qaoa = QAOA(quantum_instance=backend, reps=1) + qaoa = QAOA(sampler=Sampler(), optimizer=SLSQP(), reps=1) aggregator = MeanAggregator() optimizer = WarmStartQAOAOptimizer( pre_solver=presolver, @@ -85,8 +82,7 @@ def test_constrained_binary(self): problem = from_docplex_mp(model) - backend = BasicAer.get_backend("statevector_simulator") - qaoa = QAOA(quantum_instance=backend, reps=1) + qaoa = QAOA(sampler=Sampler(), optimizer=SLSQP(), reps=1) aggregator = MeanAggregator() optimizer = WarmStartQAOAOptimizer( pre_solver=SlsqpOptimizer(), @@ -113,8 +109,7 @@ def test_simple_qubo(self): model.minimize((u - v + 2) ** 2) problem = from_docplex_mp(model) - backend = BasicAer.get_backend("statevector_simulator") - qaoa = QAOA(quantum_instance=backend, reps=1) + qaoa = QAOA(sampler=Sampler(), optimizer=SLSQP(), reps=1) optimizer = WarmStartQAOAOptimizer( pre_solver=SlsqpOptimizer(), relax_for_pre_solver=True, diff --git a/test/converters/test_converters.py b/test/converters/test_converters.py index 7290f3877..8086166b0 100644 --- a/test/converters/test_converters.py +++ b/test/converters/test_converters.py @@ -17,15 +17,12 @@ import numpy as np from docplex.mp.model import Model -from qiskit.algorithms import NumPyMinimumEigensolver -from qiskit.opflow import Z, I +from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver +from qiskit.opflow import I, Z + import qiskit_optimization.optionals as _optionals -from qiskit_optimization import QuadraticProgram, QiskitOptimizationError -from qiskit_optimization.algorithms import ( - MinimumEigenOptimizer, - CplexOptimizer, - ADMMOptimizer, -) +from qiskit_optimization import QiskitOptimizationError, QuadraticProgram +from qiskit_optimization.algorithms import ADMMOptimizer, CplexOptimizer, MinimumEigenOptimizer from qiskit_optimization.algorithms.admm_optimizer import ADMMParameters from qiskit_optimization.converters import ( InequalityToEquality, @@ -36,7 +33,6 @@ from qiskit_optimization.problems import Constraint, Variable from qiskit_optimization.translators import from_docplex_mp - QUBIT_OP_MAXIMIZE_SAMPLE = ( -199999.5 * (I ^ I ^ I ^ Z) + -399999.5 * (I ^ I ^ Z ^ I) From f5db21d43f15721bca64dfb851daa3e54e0f027c Mon Sep 17 00:00:00 2001 From: a-matsuo Date: Tue, 15 Nov 2022 20:22:24 +0900 Subject: [PATCH 04/19] add sampler --- .../algorithms/grover_optimizer.py | 120 +++++++++++++----- 1 file changed, 91 insertions(+), 29 deletions(-) diff --git a/qiskit_optimization/algorithms/grover_optimizer.py b/qiskit_optimization/algorithms/grover_optimizer.py index d6a75cde0..7f883eac0 100644 --- a/qiskit_optimization/algorithms/grover_optimizer.py +++ b/qiskit_optimization/algorithms/grover_optimizer.py @@ -16,6 +16,7 @@ import math from copy import deepcopy from typing import Optional, Dict, Union, List, cast +import warnings import numpy as np @@ -24,6 +25,7 @@ from qiskit.utils import QuantumInstance, algorithm_globals from qiskit.algorithms.amplitude_amplifiers.grover import Grover from qiskit.circuit.library import QuadraticForm +from qiskit.primitives import BaseSampler from qiskit.providers import Backend from qiskit.quantum_info import partial_trace from .optimization_algorithm import ( @@ -36,6 +38,7 @@ QuadraticProgramToQubo, QuadraticProgramConverter, ) +from ..exceptions import QiskitOptimizationError from ..problems import Variable from ..problems.quadratic_program import QuadraticProgram @@ -54,6 +57,7 @@ def __init__( Union[QuadraticProgramConverter, List[QuadraticProgramConverter]] ] = None, penalty: Optional[float] = None, + sampler: Optional[BaseSampler] = None, ) -> None: """ Args: @@ -66,6 +70,7 @@ def __init__( :class:`~qiskit_optimization.converters.QuadraticProgramToQubo` will be used. penalty: The penalty factor used in the default :class:`~qiskit_optimization.converters.QuadraticProgramToQubo` converter + sampler: A Sampler to use for sampling the results of the circuits. Raises: TypeError: When there one of converters is an invalid type. @@ -73,13 +78,27 @@ def __init__( self._num_value_qubits = num_value_qubits self._num_key_qubits = 0 self._n_iterations = num_iterations - self._quantum_instance = None # type: Optional[QuantumInstance] self._circuit_results = {} # type: dict + self._converters = self._prepare_converters(converters, penalty) + if quantum_instance is not None and sampler is not None: + raise ValueError("Only one of quantum_instance or sampler can be passed, not both!") + + self._quantum_instance = None # type: Optional[QuantumInstance] if quantum_instance is not None: self.quantum_instance = quantum_instance - - self._converters = self._prepare_converters(converters, penalty) + warnings.warn( + "The quantum_instance argument has been superseded by the sampler argument. " + "This argument will be deprecated in a future release and subsequently " + "removed after that.", + category=PendingDeprecationWarning, + stacklevel=2, + ) + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=PendingDeprecationWarning) + self.quantum_instance = quantum_instance + + self._sampler = sampler @property def quantum_instance(self) -> QuantumInstance: @@ -88,6 +107,13 @@ def quantum_instance(self) -> QuantumInstance: Returns: The quantum instance used in the algorithm. """ + warnings.warn( + "The quantum_instance argument has been superseded by the sampler argument. " + "This argument will be deprecated in a future release and subsequently " + "removed after that.", + category=PendingDeprecationWarning, + stacklevel=2, + ) return self._quantum_instance @quantum_instance.setter @@ -97,6 +123,13 @@ def quantum_instance(self, quantum_instance: Union[Backend, QuantumInstance]) -> Args: quantum_instance: The quantum instance to be used in the algorithm. """ + warnings.warn( + "The GroverOptimizer.quantum_instance setter is pending deprecation. " + "This property will be deprecated in a future release and subsequently " + "removed after that.", + category=PendingDeprecationWarning, + stacklevel=2, + ) if isinstance(quantum_instance, Backend): self._quantum_instance = QuantumInstance(quantum_instance) else: @@ -165,8 +198,11 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: AttributeError: If the quantum instance has not been set. QiskitOptimizationError: If the problem is incompatible with the optimizer. """ - if self.quantum_instance is None: - raise AttributeError("The quantum instance or backend has not been set.") + if self._sampler is None and self._quantum_instance is None: + raise ValueError("A quantum instance or sampler must be provided.") + + if self._quantum_instance is not None and self._sampler is not None: + raise ValueError("Only one of quantum_instance or sampler can be passed, not both!") self._verify_compatibility(problem) @@ -199,7 +235,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: # Initialize oracle helper object. qr_key_value = QuantumRegister(self._num_key_qubits + self._num_value_qubits) orig_constant = problem_.objective.constant - measurement = not self.quantum_instance.is_statevector + measurement = not (self.quantum_instance and self.quantum_instance.is_statevector) oracle, is_good_state = self._get_oracle(qr_key_value) while not optimum_found: @@ -234,6 +270,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: k = int(outcome[0:n_key], 2) v = outcome[n_key : n_key + n_value] int_v = self._bin_to_int(v, n_value) + threshold + print(f'int_v: {int_v}') logger.info("Outcome: %s", outcome) logger.info("Value Q(x): %s", int_v) # If the value is an improvement, we update the iteration parameters (e.g. oracle). @@ -246,15 +283,23 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: threshold = optimum_value # trace out work qubits and store samples - if self._quantum_instance.is_statevector: - indices = list(range(n_key, len(outcome))) - rho = partial_trace(self._circuit_results, indices) - self._circuit_results = cast(Dict, np.diag(rho.data) ** 0.5) - else: + if self._sampler: + print('cccc',self._circuit_results) self._circuit_results = { i[-1 * n_key :]: v for i, v in self._circuit_results.items() } - + else: + if self._quantum_instance.is_statevector: + indices = list(range(n_key, len(outcome))) + rho = partial_trace(self._circuit_results, indices) + self._circuit_results = cast(Dict, np.diag(rho.data) ** 0.5) + else: + print(self._circuit_results) + self._circuit_results = { + i[-1 * n_key :]: v for i, v in self._circuit_results.items() + } + print(self._circuit_results) + print(f'circuit_results: {self._circuit_results}') raw_samples = self._eigenvector_to_solutions( self._circuit_results, problem_init ) @@ -320,24 +365,41 @@ def _measure(self, circuit: QuantumCircuit) -> str: def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: """Gets probabilities from a given backend.""" # Execute job and filter results. - result = self.quantum_instance.execute(qc) - if self.quantum_instance.is_statevector: - state = result.get_statevector(qc) - if not isinstance(state, np.ndarray): - state = state.data - keys = [ - bin(i)[2::].rjust(int(np.log2(len(state))), "0")[::-1] for i in range(0, len(state)) - ] - probs = [abs(a) ** 2 for a in state] - total = math.fsum(probs) - probs = [p / total for p in probs] - hist = {key: prob for key, prob in zip(keys, probs) if prob > 0} - self._circuit_results = state + if self._sampler is not None: + job = self._sampler.run([qc]) + + try: + result = job.result() + except Exception as exc: + raise QiskitOptimizationError("Sampler job failed.") from exc + #print(result) + quasi_dist = result.quasi_dists[0] + bit_length = (len(quasi_dist)-1).bit_length() + hist = {f"{i:0{bit_length}b}"[::-1]: v for i, v in quasi_dist.items()} + # print(probs) + # print([v for i, v in quasi_dist.items() if v<0]) + self._circuit_results = {f"{i:0{bit_length}b}": v ** 0.5 for i, v in quasi_dist.items() if not np.isclose(v,0)} + #print(hist) + #print('bbbbbb',self._circuit_results) else: - state = result.get_counts(qc) - shots = self.quantum_instance.run_config.shots - hist = {key[::-1]: val / shots for key, val in sorted(state.items()) if val > 0} - self._circuit_results = {b: (v / shots) ** 0.5 for (b, v) in state.items()} + result = self.quantum_instance.execute(qc) + if self.quantum_instance.is_statevector: + state = result.get_statevector(qc) + if not isinstance(state, np.ndarray): + state = state.data + keys = [ + bin(i)[2::].rjust(int(np.log2(len(state))), "0")[::-1] for i in range(0, len(state)) + ] + probs = [abs(a) ** 2 for a in state] + total = math.fsum(probs) + probs = [p / total for p in probs] + hist = {key: prob for key, prob in zip(keys, probs) if prob > 0} + self._circuit_results = state + else: + state = result.get_counts(qc) + shots = self.quantum_instance.run_config.shots + hist = {key[::-1]: val / shots for key, val in sorted(state.items()) if val > 0} + self._circuit_results = {b: (v / shots) ** 0.5 for (b, v) in state.items()} return hist @staticmethod From a38840be70a3d9496653eb63ffb14ab0dcfdc789 Mon Sep 17 00:00:00 2001 From: a-matsuo Date: Wed, 16 Nov 2022 22:07:28 +0900 Subject: [PATCH 05/19] added unittests --- .../algorithms/grover_optimizer.py | 10 -- test/algorithms/test_grover_optimizer.py | 114 +++++++++++++----- 2 files changed, 85 insertions(+), 39 deletions(-) diff --git a/qiskit_optimization/algorithms/grover_optimizer.py b/qiskit_optimization/algorithms/grover_optimizer.py index 7f883eac0..770fa54a4 100644 --- a/qiskit_optimization/algorithms/grover_optimizer.py +++ b/qiskit_optimization/algorithms/grover_optimizer.py @@ -270,7 +270,6 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: k = int(outcome[0:n_key], 2) v = outcome[n_key : n_key + n_value] int_v = self._bin_to_int(v, n_value) + threshold - print(f'int_v: {int_v}') logger.info("Outcome: %s", outcome) logger.info("Value Q(x): %s", int_v) # If the value is an improvement, we update the iteration parameters (e.g. oracle). @@ -284,7 +283,6 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: # trace out work qubits and store samples if self._sampler: - print('cccc',self._circuit_results) self._circuit_results = { i[-1 * n_key :]: v for i, v in self._circuit_results.items() } @@ -294,12 +292,9 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: rho = partial_trace(self._circuit_results, indices) self._circuit_results = cast(Dict, np.diag(rho.data) ** 0.5) else: - print(self._circuit_results) self._circuit_results = { i[-1 * n_key :]: v for i, v in self._circuit_results.items() } - print(self._circuit_results) - print(f'circuit_results: {self._circuit_results}') raw_samples = self._eigenvector_to_solutions( self._circuit_results, problem_init ) @@ -372,15 +367,10 @@ def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: result = job.result() except Exception as exc: raise QiskitOptimizationError("Sampler job failed.") from exc - #print(result) quasi_dist = result.quasi_dists[0] bit_length = (len(quasi_dist)-1).bit_length() hist = {f"{i:0{bit_length}b}"[::-1]: v for i, v in quasi_dist.items()} - # print(probs) - # print([v for i, v in quasi_dist.items() if v<0]) self._circuit_results = {f"{i:0{bit_length}b}": v ** 0.5 for i, v in quasi_dist.items() if not np.isclose(v,0)} - #print(hist) - #print('bbbbbb',self._circuit_results) else: result = self.quantum_instance.execute(qc) if self.quantum_instance.is_statevector: diff --git a/test/algorithms/test_grover_optimizer.py b/test/algorithms/test_grover_optimizer.py index a029590fa..96bc2c93a 100644 --- a/test/algorithms/test_grover_optimizer.py +++ b/test/algorithms/test_grover_optimizer.py @@ -20,6 +20,7 @@ from docplex.mp.model import Model from qiskit.utils import QuantumInstance, algorithm_globals, optionals from qiskit.algorithms import NumPyMinimumEigensolver +from qiskit.primitives import Sampler from qiskit_optimization.algorithms import ( GroverOptimizer, MinimumEigenOptimizer, @@ -58,6 +59,33 @@ def setUp(self): ) self.n_iter = 8 + def _prepare_grover_optimizer( + self, num_value_qubits, num_iterations, simulator, converters=None + ): + """Prepare GroverOptimizer.""" + if simulator == "statevector": + grover_optimizer = GroverOptimizer( + num_value_qubits=num_value_qubits, + num_iterations=num_iterations, + converters=converters, + quantum_instance=self.sv_simulator, + ) + elif simulator == "qasm": + grover_optimizer = GroverOptimizer( + num_value_qubits=num_value_qubits, + num_iterations=num_iterations, + converters=converters, + quantum_instance=self.qasm_simulator, + ) + else: + grover_optimizer = GroverOptimizer( + num_value_qubits=num_value_qubits, + num_iterations=num_iterations, + converters=converters, + sampler=Sampler(), + ) + return grover_optimizer + def validate_results(self, problem, results): """Validate the results object returned by GroverOptimizer.""" # Get expected value. @@ -71,7 +99,8 @@ def validate_results(self, problem, results): results.fval, problem.objective.sense.value * results.intermediate_fval ) - def test_qubo_gas_int_zero(self): + @data("statevector", "qasm", "sampler") + def test_qubo_gas_int_zero(self, simulator): """Test for when the answer is zero.""" # Input. @@ -82,13 +111,17 @@ def test_qubo_gas_int_zero(self): op = from_docplex_mp(model) # Will not find a negative, should return 0. - gmf = GroverOptimizer(1, num_iterations=1, quantum_instance=self.sv_simulator) - results = gmf.solve(op) + # gmf = GroverOptimizer(1, num_iterations=1, quantum_instance=self.sv_simulator) + grover_optimizer = self._prepare_grover_optimizer( + num_value_qubits=1, num_iterations=1, simulator=simulator + ) + results = grover_optimizer.solve(op) np.testing.assert_array_almost_equal(results.x, [0, 0]) self.assertEqual(results.fval, 0.0) self.assertAlmostEqual(results.fval, results.intermediate_fval) - def test_qubo_gas_int_simple(self): + @data("statevector", "qasm", "sampler") + def test_qubo_gas_int_simple(self, simulator): """Test for simple case, with 2 linear coeffs and no quadratic coeffs or constants.""" # Input. @@ -99,15 +132,19 @@ def test_qubo_gas_int_simple(self): op = from_docplex_mp(model) # Get the optimum key and value. - gmf = GroverOptimizer(4, num_iterations=self.n_iter, quantum_instance=self.sv_simulator) - results = gmf.solve(op) + # gmf = GroverOptimizer(4, num_iterations=self.n_iter, quantum_instance=self.sv_simulator) + grover_optimizer = self._prepare_grover_optimizer( + num_value_qubits=4, num_iterations=self.n_iter, simulator=simulator + ) + results = grover_optimizer.solve(op) self.validate_results(op, results) self.assertIsNotNone(results.operation_counts) self.assertEqual(results.n_input_qubits, 2) self.assertEqual(results.n_output_qubits, 4) - def test_qubo_gas_int_simple_maximize(self): + @data("statevector", "qasm", "sampler") + def test_qubo_gas_int_simple_maximize(self, simulator): """Test for simple case, but with maximization.""" # Input. @@ -118,11 +155,14 @@ def test_qubo_gas_int_simple_maximize(self): op = from_docplex_mp(model) # Get the optimum key and value. - gmf = GroverOptimizer(4, num_iterations=self.n_iter, quantum_instance=self.sv_simulator) - results = gmf.solve(op) + # gmf = GroverOptimizer(4, num_iterations=self.n_iter, quantum_instance=self.sv_simulator) + grover_optimizer = self._prepare_grover_optimizer( + num_value_qubits=4, num_iterations=self.n_iter, simulator=simulator + ) + results = grover_optimizer.solve(op) self.validate_results(op, results) - @data("sv", "qasm") + @data("statevector", "qasm", "sampler") def test_qubo_gas_int_paper_example(self, simulator): """ Test the example from https://arxiv.org/abs/1912.04088 using the state vector simulator @@ -138,12 +178,16 @@ def test_qubo_gas_int_paper_example(self, simulator): op = from_docplex_mp(model) # Get the optimum key and value. - q_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator - gmf = GroverOptimizer(6, num_iterations=self.n_iter, quantum_instance=q_instance) - results = gmf.solve(op) + # q_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator + # gmf = GroverOptimizer(6, num_iterations=self.n_iter, quantum_instance=q_instance) + grover_optimizer = self._prepare_grover_optimizer( + num_value_qubits=6, num_iterations=self.n_iter, simulator=simulator + ) + results = grover_optimizer.solve(op) self.validate_results(op, results) - def test_converter_list(self): + @data("statevector", "qasm", "sampler") + def test_converter_list(self, simulator): """Test converters list""" # Input. @@ -156,13 +200,16 @@ def test_converter_list(self): # Get the optimum key and value. # a single converter. qp2qubo = QuadraticProgramToQubo() - gmf = GroverOptimizer( - 4, - num_iterations=self.n_iter, - quantum_instance=self.sv_simulator, - converters=qp2qubo, + # gmf = GroverOptimizer( + # 4, + # num_iterations=self.n_iter, + # quantum_instance=self.sv_simulator, + # converters=qp2qubo, + # ) + grover_optimizer = self._prepare_grover_optimizer( + num_value_qubits=4, num_iterations=self.n_iter, simulator=simulator ) - results = gmf.solve(op) + results = grover_optimizer.solve(op) self.validate_results(op, results) # a list of converters @@ -171,13 +218,19 @@ def test_converter_list(self): penalize = LinearEqualityToPenalty() max2min = MaximizeToMinimize() converters = [ineq2eq, int2bin, penalize, max2min] - gmf = GroverOptimizer( - 4, + # gmf = GroverOptimizer( + # 4, + # num_iterations=self.n_iter, + # quantum_instance=self.sv_simulator, + # converters=converters, + # ) + grover_optimizer = self._prepare_grover_optimizer( + num_value_qubits=4, num_iterations=self.n_iter, - quantum_instance=self.sv_simulator, + simulator=simulator, converters=converters, ) - results = gmf.solve(op) + results = grover_optimizer.solve(op) self.validate_results(op, results) # invalid converters with self.assertRaises(TypeError): @@ -189,7 +242,7 @@ def test_converter_list(self): converters=invalid, ) - @data("sv", "qasm") + @data("statevector", "qasm", "sampler") def test_samples_and_raw_samples(self, simulator): """Test samples and raw_samples""" algorithm_globals.random_seed = 2 @@ -198,9 +251,12 @@ def test_samples_and_raw_samples(self, simulator): op.binary_var("y") op.minimize(linear={"x": 1, "y": 2}) op.linear_constraint(linear={"x": 1, "y": 1}, sense=">=", rhs=1, name="xy") - q_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator - grover_optimizer = GroverOptimizer( - 8, num_iterations=self.n_iter, quantum_instance=q_instance + # q_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator + # grover_optimizer = GroverOptimizer( + # 8, num_iterations=self.n_iter, quantum_instance=q_instance + # ) + grover_optimizer = self._prepare_grover_optimizer( + num_value_qubits=8, num_iterations=self.n_iter, simulator=simulator ) opt_sol = 1 success = OptimizationResultStatus.SUCCESS @@ -221,7 +277,7 @@ def test_samples_and_raw_samples(self, simulator): self.assertEqual(results.status, results.raw_samples[0].status) np.testing.assert_array_almost_equal([1, 0, 0, 0, 0], results.raw_samples[0].x) - @data("sv", "qasm") + @data("statevector", "qasm", "sampler") def test_bit_ordering(self, simulator): """Test bit ordering""" # test minimize From d52ae2f697c153d796f14c62fe8623abd0baf7d5 Mon Sep 17 00:00:00 2001 From: a-matsuo Date: Thu, 17 Nov 2022 11:18:46 +0900 Subject: [PATCH 06/19] finalized the codes and unittests --- .../algorithms/grover_optimizer.py | 37 +++++++----- ...grover-opt-primitive-de82d051d6cee2e4.yaml | 9 +++ test/algorithms/test_grover_optimizer.py | 59 ++++++------------- 3 files changed, 50 insertions(+), 55 deletions(-) create mode 100644 releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml diff --git a/qiskit_optimization/algorithms/grover_optimizer.py b/qiskit_optimization/algorithms/grover_optimizer.py index 770fa54a4..66dec73dd 100644 --- a/qiskit_optimization/algorithms/grover_optimizer.py +++ b/qiskit_optimization/algorithms/grover_optimizer.py @@ -73,6 +73,7 @@ def __init__( sampler: A Sampler to use for sampling the results of the circuits. Raises: + ValueError: If both a quantum instance and sampler are set. TypeError: When there one of converters is an invalid type. """ self._num_value_qubits = num_value_qubits @@ -86,7 +87,6 @@ def __init__( self._quantum_instance = None # type: Optional[QuantumInstance] if quantum_instance is not None: - self.quantum_instance = quantum_instance warnings.warn( "The quantum_instance argument has been superseded by the sampler argument. " "This argument will be deprecated in a future release and subsequently " @@ -195,6 +195,8 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: The result of the optimizer applied to the problem. Raises: + ValueError: If a quantum instance or a sampler has not been provided. + ValueError: If both a quantum instance and sampler are set. AttributeError: If the quantum instance has not been set. QiskitOptimizationError: If the problem is incompatible with the optimizer. """ @@ -235,7 +237,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: # Initialize oracle helper object. qr_key_value = QuantumRegister(self._num_key_qubits + self._num_value_qubits) orig_constant = problem_.objective.constant - measurement = not (self.quantum_instance and self.quantum_instance.is_statevector) + measurement = not (self._quantum_instance and self._quantum_instance.is_statevector) oracle, is_good_state = self._get_oracle(qr_key_value) while not optimum_found: @@ -352,12 +354,12 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: def _measure(self, circuit: QuantumCircuit) -> str: """Get probabilities from the given backend, and picks a random outcome.""" - probs = self._get_probs(circuit) + probs = self._get_prob_dist(circuit) logger.info("Frequencies: %s", probs) # Pick a random outcome. return algorithm_globals.random.choice(list(probs.keys()), 1, p=list(probs.values()))[0] - def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: + def _get_prob_dist(self, qc: QuantumCircuit) -> Dict[str, float]: """Gets probabilities from a given backend.""" # Execute job and filter results. if self._sampler is not None: @@ -368,29 +370,36 @@ def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: except Exception as exc: raise QiskitOptimizationError("Sampler job failed.") from exc quasi_dist = result.quasi_dists[0] - bit_length = (len(quasi_dist)-1).bit_length() - hist = {f"{i:0{bit_length}b}"[::-1]: v for i, v in quasi_dist.items()} - self._circuit_results = {f"{i:0{bit_length}b}": v ** 0.5 for i, v in quasi_dist.items() if not np.isclose(v,0)} + bit_length = (len(quasi_dist) - 1).bit_length() + prob_dist = {f"{i:0{bit_length}b}"[::-1]: v for i, v in quasi_dist.items()} + self._circuit_results = { + f"{i:0{bit_length}b}": v**0.5 + for i, v in quasi_dist.items() + if not np.isclose(v, 0) + } else: - result = self.quantum_instance.execute(qc) - if self.quantum_instance.is_statevector: + result = self._quantum_instance.execute(qc) + if self._quantum_instance.is_statevector: state = result.get_statevector(qc) if not isinstance(state, np.ndarray): state = state.data keys = [ - bin(i)[2::].rjust(int(np.log2(len(state))), "0")[::-1] for i in range(0, len(state)) + bin(i)[2::].rjust(int(np.log2(len(state))), "0")[::-1] + for i in range(0, len(state)) ] probs = [abs(a) ** 2 for a in state] total = math.fsum(probs) probs = [p / total for p in probs] - hist = {key: prob for key, prob in zip(keys, probs) if prob > 0} + prob_dist = {key: prob for key, prob in zip(keys, probs) if prob > 0} self._circuit_results = state else: state = result.get_counts(qc) - shots = self.quantum_instance.run_config.shots - hist = {key[::-1]: val / shots for key, val in sorted(state.items()) if val > 0} + shots = self._quantum_instance.run_config.shots + prob_dist = { + key[::-1]: val / shots for key, val in sorted(state.items()) if val > 0 + } self._circuit_results = {b: (v / shots) ** 0.5 for (b, v) in state.items()} - return hist + return prob_dist @staticmethod def _bin_to_int(v: str, num_value_bits: int) -> int: diff --git a/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml b/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml new file mode 100644 index 000000000..032867adf --- /dev/null +++ b/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + The :class:`~.GroverOptimizer` class has a new keyword argument, ``sampler`` which is + used to run the algorithm using an instance of the :class:`~.BaseSampler` + interface to calculate the results. This new argument supersedes the + the ``quantum_instance`` argument and accordingly, ``quantum_instance`` + is pending deprecation and will be deprecated and subsequently removed in + future releases. diff --git a/test/algorithms/test_grover_optimizer.py b/test/algorithms/test_grover_optimizer.py index 96bc2c93a..6edff0d5b 100644 --- a/test/algorithms/test_grover_optimizer.py +++ b/test/algorithms/test_grover_optimizer.py @@ -64,19 +64,21 @@ def _prepare_grover_optimizer( ): """Prepare GroverOptimizer.""" if simulator == "statevector": - grover_optimizer = GroverOptimizer( - num_value_qubits=num_value_qubits, - num_iterations=num_iterations, - converters=converters, - quantum_instance=self.sv_simulator, - ) + with self.assertWarns(PendingDeprecationWarning): + grover_optimizer = GroverOptimizer( + num_value_qubits=num_value_qubits, + num_iterations=num_iterations, + converters=converters, + quantum_instance=self.sv_simulator, + ) elif simulator == "qasm": - grover_optimizer = GroverOptimizer( - num_value_qubits=num_value_qubits, - num_iterations=num_iterations, - converters=converters, - quantum_instance=self.qasm_simulator, - ) + with self.assertWarns(PendingDeprecationWarning): + grover_optimizer = GroverOptimizer( + num_value_qubits=num_value_qubits, + num_iterations=num_iterations, + converters=converters, + quantum_instance=self.qasm_simulator, + ) else: grover_optimizer = GroverOptimizer( num_value_qubits=num_value_qubits, @@ -111,7 +113,6 @@ def test_qubo_gas_int_zero(self, simulator): op = from_docplex_mp(model) # Will not find a negative, should return 0. - # gmf = GroverOptimizer(1, num_iterations=1, quantum_instance=self.sv_simulator) grover_optimizer = self._prepare_grover_optimizer( num_value_qubits=1, num_iterations=1, simulator=simulator ) @@ -132,7 +133,6 @@ def test_qubo_gas_int_simple(self, simulator): op = from_docplex_mp(model) # Get the optimum key and value. - # gmf = GroverOptimizer(4, num_iterations=self.n_iter, quantum_instance=self.sv_simulator) grover_optimizer = self._prepare_grover_optimizer( num_value_qubits=4, num_iterations=self.n_iter, simulator=simulator ) @@ -155,7 +155,6 @@ def test_qubo_gas_int_simple_maximize(self, simulator): op = from_docplex_mp(model) # Get the optimum key and value. - # gmf = GroverOptimizer(4, num_iterations=self.n_iter, quantum_instance=self.sv_simulator) grover_optimizer = self._prepare_grover_optimizer( num_value_qubits=4, num_iterations=self.n_iter, simulator=simulator ) @@ -178,8 +177,6 @@ def test_qubo_gas_int_paper_example(self, simulator): op = from_docplex_mp(model) # Get the optimum key and value. - # q_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator - # gmf = GroverOptimizer(6, num_iterations=self.n_iter, quantum_instance=q_instance) grover_optimizer = self._prepare_grover_optimizer( num_value_qubits=6, num_iterations=self.n_iter, simulator=simulator ) @@ -200,12 +197,6 @@ def test_converter_list(self, simulator): # Get the optimum key and value. # a single converter. qp2qubo = QuadraticProgramToQubo() - # gmf = GroverOptimizer( - # 4, - # num_iterations=self.n_iter, - # quantum_instance=self.sv_simulator, - # converters=qp2qubo, - # ) grover_optimizer = self._prepare_grover_optimizer( num_value_qubits=4, num_iterations=self.n_iter, simulator=simulator ) @@ -218,12 +209,6 @@ def test_converter_list(self, simulator): penalize = LinearEqualityToPenalty() max2min = MaximizeToMinimize() converters = [ineq2eq, int2bin, penalize, max2min] - # gmf = GroverOptimizer( - # 4, - # num_iterations=self.n_iter, - # quantum_instance=self.sv_simulator, - # converters=converters, - # ) grover_optimizer = self._prepare_grover_optimizer( num_value_qubits=4, num_iterations=self.n_iter, @@ -235,11 +220,8 @@ def test_converter_list(self, simulator): # invalid converters with self.assertRaises(TypeError): invalid = [qp2qubo, "invalid converter"] - GroverOptimizer( - 4, - num_iterations=self.n_iter, - quantum_instance=self.sv_simulator, - converters=invalid, + grover_optimizer = self._prepare_grover_optimizer( + 4, num_iterations=self.n_iter, simulator=simulator, converters=invalid ) @data("statevector", "qasm", "sampler") @@ -251,10 +233,6 @@ def test_samples_and_raw_samples(self, simulator): op.binary_var("y") op.minimize(linear={"x": 1, "y": 2}) op.linear_constraint(linear={"x": 1, "y": 1}, sense=">=", rhs=1, name="xy") - # q_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator - # grover_optimizer = GroverOptimizer( - # 8, num_iterations=self.n_iter, quantum_instance=q_instance - # ) grover_optimizer = self._prepare_grover_optimizer( num_value_qubits=8, num_iterations=self.n_iter, simulator=simulator ) @@ -282,7 +260,6 @@ def test_bit_ordering(self, simulator): """Test bit ordering""" # test minimize algorithm_globals.random_seed = 2 - q_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator mdl = Model("docplex model") x = mdl.binary_var("x") y = mdl.binary_var("y") @@ -290,8 +267,8 @@ def test_bit_ordering(self, simulator): op = from_docplex_mp(mdl) opt_sol = -2 success = OptimizationResultStatus.SUCCESS - grover_optimizer = GroverOptimizer( - 3, num_iterations=self.n_iter, quantum_instance=q_instance + grover_optimizer = self._prepare_grover_optimizer( + num_value_qubits=3, num_iterations=self.n_iter, simulator=simulator ) results = grover_optimizer.solve(op) self.assertEqual(results.fval, opt_sol) From 666e6638291fd472fdb4e2ebe367da593f681431 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Fri, 18 Nov 2022 17:56:09 +0900 Subject: [PATCH 07/19] update docstring --- qiskit_optimization/__init__.py | 4 ++-- .../algorithms/minimum_eigen_optimizer.py | 12 ++---------- .../algorithms/recursive_minimum_eigen_optimizer.py | 4 ++-- qiskit_optimization/deprecation.py | 11 +---------- test/algorithms/legacy/test_min_eigen_optimizer.py | 9 --------- 5 files changed, 7 insertions(+), 33 deletions(-) diff --git a/qiskit_optimization/__init__.py b/qiskit_optimization/__init__.py index 5753d7c90..0e7203764 100644 --- a/qiskit_optimization/__init__.py +++ b/qiskit_optimization/__init__.py @@ -26,10 +26,10 @@ A uniform interface as well as automatic conversion between different problem representations allows users to solve problems using a large set of algorithms, from variational quantum algorithms, such as the Quantum Approximate Optimization Algorithm -(:class:`~qiskit.algorithms.QAOA`), to +(:class:`~qiskit.algorithms.minimum_eigensolver.QAOA`), to `Grover Adaptive Search `_ (:class:`~algorithms.GroverOptimizer`), leveraging -fundamental :mod:`~qiskit.algorithms` provided by Qiskit Terra. Furthermore, the modular design +fundamental :mod:`~qiskit.algorithms.minimum_eigensolver` provided by Qiskit Terra. Furthermore, the modular design of the optimization module allows it to be easily extended and facilitates rapid development and testing of new algorithms. Compatible classical optimizers are also provided for testing, validation, and benchmarking. diff --git a/qiskit_optimization/algorithms/minimum_eigen_optimizer.py b/qiskit_optimization/algorithms/minimum_eigen_optimizer.py index e8dc4b870..193790bd5 100644 --- a/qiskit_optimization/algorithms/minimum_eigen_optimizer.py +++ b/qiskit_optimization/algorithms/minimum_eigen_optimizer.py @@ -27,7 +27,6 @@ from qiskit.opflow import OperatorBase, PauliOp, PauliSumOp from ..converters.quadratic_program_to_qubo import QuadraticProgramConverter, QuadraticProgramToQubo -from ..deprecation import DeprecatedType, warn_deprecated from ..exceptions import QiskitOptimizationError from ..problems.quadratic_program import QuadraticProgram, Variable from .optimization_algorithm import ( @@ -115,7 +114,7 @@ class MinimumEigenOptimizer(OptimizationAlgorithm): .. code-block:: - from qiskit.algorithms import QAOA + from qiskit.algorithms.minimum_eigensolver import QAOA from qiskit_optimization.problems import QuadraticProgram from qiskit_optimization.algorithms import MinimumEigenOptimizer problem = QuadraticProgram() @@ -151,14 +150,7 @@ def __init__( TypeError: When one of converters has an invalid type. QiskitOptimizationError: When the minimum eigensolver does not return an eigenstate. """ - if isinstance(min_eigen_solver, LegacyMinimumEigensolver): - warn_deprecated( - "0.5.0", - DeprecatedType.ARGUMENT, - f"min_eigen_solver as {LegacyMinimumEigensolver.__name__}", - new_name=f"min_eigen_solver as {SamplingMinimumEigensolver.__name__} " - f"or {NumPyMinimumEigensolver.__name__}", - ) + if not min_eigen_solver.supports_aux_operators(): raise QiskitOptimizationError( "Given MinimumEigensolver does not return the eigenstate " diff --git a/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py b/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py index 737fc56d9..c3149f3fc 100644 --- a/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py +++ b/qiskit_optimization/algorithms/recursive_minimum_eigen_optimizer.py @@ -117,7 +117,7 @@ class RecursiveMinimumEigenOptimizer(OptimizationAlgorithm): .. code-block:: python - from qiskit.algorithms import QAOA + from qiskit.algorithms.minimum_eigensolver import QAOA from qiskit_optimization.problems import QuadraticProgram from qiskit_optimization.algorithms import ( MinimumEigenOptimizer, RecursiveMinimumEigenOptimizer @@ -161,7 +161,7 @@ def __init__( min_num_vars_optimizer: This optimizer is used after the recursive scheme for the problem with the remaining variables. Default value is :class:`~qiskit_optimization.algorithms.MinimumEigenOptimizer` created on top of - :class:`~qiskit.algorithms.minimum_eigen_solver.NumPyMinimumEigensolver`. + :class:`~qiskit.algorithms.minimum_eigensolver.NumPyMinimumEigensolver`. penalty: The factor that is used to scale the penalty terms corresponding to linear equality constraints. history: Whether the intermediate results are stored. diff --git a/qiskit_optimization/deprecation.py b/qiskit_optimization/deprecation.py index c14b7d72d..c87ab4f7c 100644 --- a/qiskit_optimization/deprecation.py +++ b/qiskit_optimization/deprecation.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021, 2022. +# (C) Copyright IBM 2021. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -421,12 +421,3 @@ def deprecate_function( return _deprecate_object( version, DeprecatedType.FUNCTION, new_type, new_name, additional_msg, stack_level ) - - -def clear_deprecated_objects() -> None: - """Clear deprecated object cache - - Returns: - None - """ - _DEPRECATED_OBJECTS.clear() diff --git a/test/algorithms/legacy/test_min_eigen_optimizer.py b/test/algorithms/legacy/test_min_eigen_optimizer.py index e4e7e529d..9ab08863a 100644 --- a/test/algorithms/legacy/test_min_eigen_optimizer.py +++ b/test/algorithms/legacy/test_min_eigen_optimizer.py @@ -383,15 +383,6 @@ def test_runtime(self, subroutine): result = opt.solve(self.op_ordering) self.assertIsInstance(result, MinimumEigenOptimizationResult) - def test_deprecation(self): - """Test deprecation warning""" - clear_deprecated_objects() - optimizer = SPSA(maxiter=100) - ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") - vqe_mes = VQE(ry_ansatz, optimizer=optimizer, quantum_instance=QasmSimulatorPy()) - with self.assertWarns(DeprecationWarning): - _ = MinimumEigenOptimizer(vqe_mes) - if __name__ == "__main__": unittest.main() From b76fe7ff76bcc246529576c35e1821d761aa937e Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Fri, 18 Nov 2022 18:31:24 +0900 Subject: [PATCH 08/19] add reno --- qiskit_optimization/__init__.py | 3 ++- .../algorithms/warm_start_qaoa_optimizer.py | 3 ++- qiskit_optimization/deprecation.py | 2 +- ...d-primitives-support-31af39549b5e66e3.yaml | 24 +++++++++++++++++++ ...grover-opt-primitive-de82d051d6cee2e4.yaml | 9 ------- .../legacy/test_min_eigen_optimizer.py | 1 - .../algorithms/legacy/test_warm_start_qaoa.py | 2 +- 7 files changed, 30 insertions(+), 14 deletions(-) create mode 100644 releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml delete mode 100644 releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml diff --git a/qiskit_optimization/__init__.py b/qiskit_optimization/__init__.py index 0e7203764..23c9443cd 100644 --- a/qiskit_optimization/__init__.py +++ b/qiskit_optimization/__init__.py @@ -29,7 +29,8 @@ (:class:`~qiskit.algorithms.minimum_eigensolver.QAOA`), to `Grover Adaptive Search `_ (:class:`~algorithms.GroverOptimizer`), leveraging -fundamental :mod:`~qiskit.algorithms.minimum_eigensolver` provided by Qiskit Terra. Furthermore, the modular design +fundamental :mod:`~qiskit.algorithms.minimum_eigensolver` provided by Qiskit Terra. +Furthermore, the modular design of the optimization module allows it to be easily extended and facilitates rapid development and testing of new algorithms. Compatible classical optimizers are also provided for testing, validation, and benchmarking. diff --git a/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py b/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py index 62dbbe9a5..c244df1f5 100644 --- a/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py +++ b/qiskit_optimization/algorithms/warm_start_qaoa_optimizer.py @@ -18,6 +18,7 @@ import numpy as np from qiskit import QuantumCircuit +from qiskit.algorithms import QAOA as LegacyQAOA from qiskit.algorithms.minimum_eigensolvers import QAOA from qiskit.circuit import Parameter @@ -202,7 +203,7 @@ def __init__( self, pre_solver: OptimizationAlgorithm, relax_for_pre_solver: bool, - qaoa: QAOA, + qaoa: Union[QAOA, LegacyQAOA], epsilon: float = 0.25, num_initial_solutions: int = 1, warm_start_factory: Optional[WarmStartQAOAFactory] = None, diff --git a/qiskit_optimization/deprecation.py b/qiskit_optimization/deprecation.py index c87ab4f7c..772d9791d 100644 --- a/qiskit_optimization/deprecation.py +++ b/qiskit_optimization/deprecation.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 2022. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml new file mode 100644 index 000000000..8167084f6 --- /dev/null +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -0,0 +1,24 @@ +--- +features: + - | + The :class:`~.GroverOptimizer` class has a new keyword argument, ``sampler`` which is + used to run the algorithm using an instance of the :class:`~.BaseSampler` + interface to calculate the results. This new argument supersedes + the ``quantum_instance`` argument and accordingly, ``quantum_instance`` + is pending deprecation and will be deprecated and subsequently removed in + future releases. + - | + The :class:`~.MinimumEigenOptimizer` class takes the primitives-based algorithms + (``qiskit.algorithms.minimum_eigensolvers.SamplingVQE`` and + ``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver``) + as ``min_eigen_solver`` argument. + The conventional algorithms ``qiskit.algorithms.minimum_eigen_solvers.MinimumEigensolver`` + is pending deprecation and will be deprecated and subsequently removed in future releases. + Note that ``qiskit.algorithms.minimum_eigensolvers.SamplingVQE`` supersedes + ``qiskit.algorithms.VQE`` for :class:`~.MinimumEigenOptimizer`. + - | + The :class:`~.WarmStartQAOAOptimizer` class takes the primitives-based QAOA + (``qiskit.algorithms.minimum_eigensolvers.QAOA``) as ``qaoa`` argument. + The conventional algorithm ``qiskit.algorithms.minimum_eigen_solvers.QAOA`` + is pending deprecation and will be deprecated + and subsequently removed in future releases. diff --git a/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml b/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml deleted file mode 100644 index 032867adf..000000000 --- a/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml +++ /dev/null @@ -1,9 +0,0 @@ ---- -features: - - | - The :class:`~.GroverOptimizer` class has a new keyword argument, ``sampler`` which is - used to run the algorithm using an instance of the :class:`~.BaseSampler` - interface to calculate the results. This new argument supersedes the - the ``quantum_instance`` argument and accordingly, ``quantum_instance`` - is pending deprecation and will be deprecated and subsequently removed in - future releases. diff --git a/test/algorithms/legacy/test_min_eigen_optimizer.py b/test/algorithms/legacy/test_min_eigen_optimizer.py index 9ab08863a..94c397bdd 100644 --- a/test/algorithms/legacy/test_min_eigen_optimizer.py +++ b/test/algorithms/legacy/test_min_eigen_optimizer.py @@ -43,7 +43,6 @@ ) from qiskit_optimization.problems import QuadraticProgram from qiskit_optimization.runtime import VQEProgram, QAOAProgram -from qiskit_optimization.deprecation import clear_deprecated_objects @ddt diff --git a/test/algorithms/legacy/test_warm_start_qaoa.py b/test/algorithms/legacy/test_warm_start_qaoa.py index 550cf0cc8..c94aae4e3 100644 --- a/test/algorithms/legacy/test_warm_start_qaoa.py +++ b/test/algorithms/legacy/test_warm_start_qaoa.py @@ -10,7 +10,7 @@ # copyright notice, and modified files need to carry a notice indicating # that they have been altered from the originals. -""" Test warm start QAOA optimizer with legacy MinimumEigensolver. """ +""" Test warm start QAOA optimizer with legacy QAOA. """ import unittest from test import QiskitOptimizationTestCase From e99c75a6378c30221853584978d0039bd0e76a84 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Mon, 21 Nov 2022 21:55:33 +0900 Subject: [PATCH 09/19] update reno, requirements.txt, and tests - Catch deprecation messages in tests --- .../notes/add-primitives-support-31af39549b5e66e3.yaml | 5 ++++- requirements.txt | 2 +- test/algorithms/test_grover_optimizer.py | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml index 8167084f6..262fa4628 100644 --- a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -1,4 +1,7 @@ --- +prelude: + - | + features: - | The :class:`~.GroverOptimizer` class has a new keyword argument, ``sampler`` which is @@ -9,7 +12,7 @@ features: future releases. - | The :class:`~.MinimumEigenOptimizer` class takes the primitives-based algorithms - (``qiskit.algorithms.minimum_eigensolvers.SamplingVQE`` and + (``qiskit.algorithms.minimum_eigensolvers.SamplingMinimumEigensolver`` and ``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver``) as ``min_eigen_solver`` argument. The conventional algorithms ``qiskit.algorithms.minimum_eigen_solvers.MinimumEigensolver`` diff --git a/requirements.txt b/requirements.txt index 7f3226a7c..ce1998c58 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -qiskit-terra>=0.20.0 +qiskit-terra>=0.22.0 scipy>=1.4 numpy>=1.17 docplex>=2.21.207 diff --git a/test/algorithms/test_grover_optimizer.py b/test/algorithms/test_grover_optimizer.py index 6edff0d5b..a39c4f202 100644 --- a/test/algorithms/test_grover_optimizer.py +++ b/test/algorithms/test_grover_optimizer.py @@ -19,7 +19,7 @@ from ddt import data, ddt from docplex.mp.model import Model from qiskit.utils import QuantumInstance, algorithm_globals, optionals -from qiskit.algorithms import NumPyMinimumEigensolver +from qiskit.algorithms.minimum_eigensolvers import NumPyMinimumEigensolver from qiskit.primitives import Sampler from qiskit_optimization.algorithms import ( GroverOptimizer, From d5678490b3d1d5849d93b42f22cd2363ebc66bf9 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Tue, 22 Nov 2022 00:12:56 +0900 Subject: [PATCH 10/19] Updates tests to suppress warnings - catch PendingDeprecationWarnings of legacy tests - replace a deprecated networkx function in test_sk_model - replace a deprecated matplotlib function in bin_packing --- .../applications/bin_packing.py | 2 +- ...d-primitives-support-31af39549b5e66e3.yaml | 4 +- requirements.txt | 2 +- .../legacy/test_min_eigen_optimizer.py | 93 +++++++++++-------- .../legacy/test_recursive_optimization.py | 43 +++++---- .../algorithms/legacy/test_warm_start_qaoa.py | 18 ++-- test/applications/test_sk_model.py | 6 +- 7 files changed, 102 insertions(+), 66 deletions(-) diff --git a/qiskit_optimization/applications/bin_packing.py b/qiskit_optimization/applications/bin_packing.py index 65155673d..73320145c 100755 --- a/qiskit_optimization/applications/bin_packing.py +++ b/qiskit_optimization/applications/bin_packing.py @@ -118,7 +118,7 @@ def get_figure(self, result: Union[OptimizationResult, np.ndarray]) -> Figure: """ import matplotlib.pyplot as plt - colors = plt.cm.get_cmap("jet", len(self._weights)) + colors = plt.colormaps.get_cmap("jet").resampled(len(self._weights)) items_in_bins = self.interpret(result) num_bins = len(items_in_bins) fig, axes = plt.subplots() diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml index 262fa4628..611562745 100644 --- a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -1,6 +1,6 @@ --- -prelude: - - | +prelude: > + Introduction of new algorithms (TODO) features: - | diff --git a/requirements.txt b/requirements.txt index ce1998c58..08bb1b3b0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -qiskit-terra>=0.22.0 +qiskit-terra>=0.22.* scipy>=1.4 numpy>=1.17 docplex>=2.21.207 diff --git a/test/algorithms/legacy/test_min_eigen_optimizer.py b/test/algorithms/legacy/test_min_eigen_optimizer.py index 94c397bdd..e6e99109f 100644 --- a/test/algorithms/legacy/test_min_eigen_optimizer.py +++ b/test/algorithms/legacy/test_min_eigen_optimizer.py @@ -42,7 +42,7 @@ QuadraticProgramToQubo, ) from qiskit_optimization.problems import QuadraticProgram -from qiskit_optimization.runtime import VQEProgram, QAOAProgram +from qiskit_optimization.runtime import VQEClient, QAOAClient @ddt @@ -56,22 +56,23 @@ def setUp(self): self.min_eigen_solvers = {} # exact eigen solver - self.min_eigen_solvers["exact"] = NumPyMinimumEigensolver() - - # QAOA - optimizer = COBYLA() - self.min_eigen_solvers["qaoa"] = QAOA(optimizer=optimizer) - # simulators - self.sv_simulator = QuantumInstance( - BasicAer.get_backend("statevector_simulator"), - seed_simulator=123, - seed_transpiler=123, - ) - self.qasm_simulator = QuantumInstance( - BasicAer.get_backend("qasm_simulator"), - seed_simulator=123, - seed_transpiler=123, - ) + with self.assertWarns(PendingDeprecationWarning): + self.min_eigen_solvers["exact"] = NumPyMinimumEigensolver() + + # QAOA + optimizer = COBYLA() + self.min_eigen_solvers["qaoa"] = QAOA(optimizer=optimizer) + # simulators + self.sv_simulator = QuantumInstance( + BasicAer.get_backend("statevector_simulator"), + seed_simulator=123, + seed_transpiler=123, + ) + self.qasm_simulator = QuantumInstance( + BasicAer.get_backend("qasm_simulator"), + seed_simulator=123, + seed_transpiler=123, + ) # test minimize self.op_minimize = QuadraticProgram() self.op_minimize.integer_var(0, 3, "x") @@ -122,7 +123,8 @@ def test_min_eigen_optimizer(self, config): cplex_result = cplex.solve(problem) # solve problem - result = min_eigen_optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(problem) self.assertIsNotNone(result) # analyze results @@ -145,7 +147,8 @@ def test_min_eigen_optimizer_with_filter(self, config): filename, lowerbound, fval, status = config # get minimum eigen solver - min_eigen_solver = NumPyMinimumEigensolver() + with self.assertWarns(PendingDeprecationWarning): + min_eigen_solver = NumPyMinimumEigensolver() # set filter # pylint: disable=unused-argument @@ -163,7 +166,8 @@ def filter_criterion(x, v, aux): problem.read_from_lp_file(lp_file) # solve problem - result = min_eigen_optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(problem) self.assertIsNotNone(result) # analyze results @@ -183,11 +187,13 @@ def test_converter_list(self): op.maximize(linear={"x": 1, "y": 2}) op.linear_constraint(linear={"x": 1, "y": 1}, sense="LE", rhs=3, name="xy_leq") - min_eigen_solver = NumPyMinimumEigensolver() + with self.assertWarns(PendingDeprecationWarning): + min_eigen_solver = NumPyMinimumEigensolver() # a single converter qp2qubo = QuadraticProgramToQubo() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver, converters=qp2qubo) - result = min_eigen_optimizer.solve(op) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(op) self.assertEqual(result.fval, 4) # a list of converters ineq2eq = InequalityToEquality() @@ -196,7 +202,8 @@ def test_converter_list(self): max2min = MaximizeToMinimize() converters = [ineq2eq, int2bin, penalize, max2min] min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver, converters=converters) - result = min_eigen_optimizer.solve(op) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(op) self.assertEqual(result.fval, 4) with self.assertRaises(TypeError): invalid = [qp2qubo, "invalid converter"] @@ -205,9 +212,11 @@ def test_converter_list(self): def test_samples_numpy_eigen_solver(self): """Test samples for NumPyMinimumEigensolver""" # test minimize - min_eigen_solver = NumPyMinimumEigensolver() + with self.assertWarns(PendingDeprecationWarning): + min_eigen_solver = NumPyMinimumEigensolver() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) - result = min_eigen_optimizer.solve(self.op_minimize) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(self.op_minimize) opt_sol = 1 success = OptimizationResultStatus.SUCCESS self.assertEqual(result.fval, opt_sol) @@ -222,9 +231,11 @@ def test_samples_numpy_eigen_solver(self): self.assertAlmostEqual(result.raw_samples[0].probability, 1.0) self.assertEqual(result.raw_samples[0].status, success) # test maximize - min_eigen_solver = NumPyMinimumEigensolver() + with self.assertWarns(PendingDeprecationWarning): + min_eigen_solver = NumPyMinimumEigensolver() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) - result = min_eigen_optimizer.solve(self.op_maximize) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(self.op_maximize) opt_sol = 2 self.assertEqual(result.fval, opt_sol) self.assertEqual(len(result.samples), 1) @@ -247,9 +258,11 @@ def test_samples_qaoa(self, simulator): # test minimize algorithm_globals.random_seed = 4 quantum_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator - qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + with self.assertWarns(PendingDeprecationWarning): + qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) min_eigen_optimizer = MinimumEigenOptimizer(qaoa) - result = min_eigen_optimizer.solve(self.op_minimize) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(self.op_minimize) success = OptimizationResultStatus.SUCCESS opt_sol = 1 self.assertAlmostEqual(sum(s.probability for s in result.samples), 1) @@ -269,9 +282,11 @@ def test_samples_qaoa(self, simulator): self.assertEqual(result.raw_samples[0].status, success) # test maximize opt_sol = 2 - qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + with self.assertWarns(PendingDeprecationWarning): + qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) min_eigen_optimizer = MinimumEigenOptimizer(qaoa) - result = min_eigen_optimizer.solve(self.op_maximize) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(self.op_maximize) self.assertAlmostEqual(sum(s.probability for s in result.samples), 1) self.assertAlmostEqual(sum(s.probability for s in result.raw_samples), 1) self.assertAlmostEqual(max(s.fval for s in result.samples), 5) @@ -297,9 +312,11 @@ def test_samples_qaoa(self, simulator): self.assertEqual(result.raw_samples[0].status, success) # test bit ordering opt_sol = -2 - qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) + with self.assertWarns(PendingDeprecationWarning): + qaoa = QAOA(optimizer=COBYLA(), quantum_instance=quantum_instance, reps=2) min_eigen_optimizer = MinimumEigenOptimizer(qaoa) - result = min_eigen_optimizer.solve(self.op_ordering) + with self.assertWarns(PendingDeprecationWarning): + result = min_eigen_optimizer.solve(self.op_ordering) self.assertEqual(result.fval, opt_sol) np.testing.assert_array_almost_equal(result.x, [0, 1]) self.assertEqual(result.status, success) @@ -329,9 +346,11 @@ def test_samples_vqe(self, simulator): success = OptimizationResultStatus.SUCCESS optimizer = SPSA(maxiter=100) ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") - vqe_mes = VQE(ry_ansatz, optimizer=optimizer, quantum_instance=quantum_instance) + with self.assertWarns(PendingDeprecationWarning): + vqe_mes = VQE(ry_ansatz, optimizer=optimizer, quantum_instance=quantum_instance) vqe = MinimumEigenOptimizer(vqe_mes) - results = vqe.solve(self.op_ordering) + with self.assertWarns(PendingDeprecationWarning): + results = vqe.solve(self.op_ordering) self.assertEqual(results.fval, opt_sol) np.testing.assert_array_almost_equal(results.x, [0, 1]) self.assertEqual(results.status, success) @@ -360,7 +379,7 @@ def test_runtime(self, subroutine): if subroutine == "vqe": ry_ansatz = TwoLocal(5, "ry", "cz", reps=3, entanglement="full") initial_point = np.random.default_rng(42).random(ry_ansatz.num_parameters) - solver = VQEProgram( + solver = VQEClient( ansatz=ry_ansatz, optimizer=optimizer, initial_point=initial_point, @@ -370,7 +389,7 @@ def test_runtime(self, subroutine): else: reps = 2 initial_point = np.random.default_rng(42).random(2 * reps) - solver = QAOAProgram( + solver = QAOAClient( optimizer=optimizer, reps=reps, initial_point=initial_point, diff --git a/test/algorithms/legacy/test_recursive_optimization.py b/test/algorithms/legacy/test_recursive_optimization.py index efe8399e6..675f8419a 100644 --- a/test/algorithms/legacy/test_recursive_optimization.py +++ b/test/algorithms/legacy/test_recursive_optimization.py @@ -50,7 +50,8 @@ def test_recursive_min_eigen_optimizer(self): """Test the recursive minimum eigen optimizer.""" filename = "op_ip1.lp" # get minimum eigen solver - min_eigen_solver = NumPyMinimumEigensolver() + with self.assertWarns(PendingDeprecationWarning): + min_eigen_solver = NumPyMinimumEigensolver() # construct minimum eigen optimizer min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) @@ -68,7 +69,8 @@ def test_recursive_min_eigen_optimizer(self): cplex_result = cplex.solve(problem) # solve problem - result = recursive_min_eigen_optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result = recursive_min_eigen_optimizer.solve(problem) # analyze results np.testing.assert_array_almost_equal(cplex_result.x, result.x, 4) @@ -84,7 +86,8 @@ def test_recursive_history(self): problem.read_from_lp_file(lp_file) # get minimum eigen solver - min_eigen_solver = NumPyMinimumEigensolver() + with self.assertWarns(PendingDeprecationWarning): + min_eigen_solver = NumPyMinimumEigensolver() # construct minimum eigen optimizer min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) @@ -95,7 +98,8 @@ def test_recursive_history(self): min_num_vars=4, history=IntermediateResult.NO_ITERATIONS, ) - result = recursive_min_eigen_optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result = recursive_min_eigen_optimizer.solve(problem) self.assertIsNotNone(result.replacements) self.assertIsNotNone(result.history) self.assertIsNotNone(result.history[0]) @@ -108,7 +112,8 @@ def test_recursive_history(self): min_num_vars=4, history=IntermediateResult.LAST_ITERATION, ) - result = recursive_min_eigen_optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result = recursive_min_eigen_optimizer.solve(problem) self.assertIsNotNone(result.replacements) self.assertIsNotNone(result.history) self.assertIsNotNone(result.history[0]) @@ -121,7 +126,8 @@ def test_recursive_history(self): min_num_vars=4, history=IntermediateResult.ALL_ITERATIONS, ) - result = recursive_min_eigen_optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result = recursive_min_eigen_optimizer.solve(problem) self.assertIsNotNone(result.replacements) self.assertIsNotNone(result.history) self.assertIsNotNone(result.history[0]) @@ -134,12 +140,13 @@ def test_recursive_warm_qaoa(self): seed = 1234 algorithm_globals.random_seed = seed backend = BasicAer.get_backend("statevector_simulator") - qaoa = QAOA( - quantum_instance=QuantumInstance( - backend=backend, seed_simulator=seed, seed_transpiler=seed - ), - reps=1, - ) + with self.assertWarns(PendingDeprecationWarning): + qaoa = QAOA( + quantum_instance=QuantumInstance( + backend=backend, seed_simulator=seed, seed_transpiler=seed + ), + reps=1, + ) warm_qaoa = WarmStartQAOAOptimizer( pre_solver=SlsqpOptimizer(), relax_for_pre_solver=True, qaoa=qaoa ) @@ -156,7 +163,8 @@ def test_recursive_warm_qaoa(self): cplex_result = cplex.solve(problem) # solve problem - result = recursive_min_eigen_optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result = recursive_min_eigen_optimizer.solve(problem) # analyze results np.testing.assert_array_almost_equal(cplex_result.x, result.x, 4) @@ -172,14 +180,16 @@ def test_converter_list(self): op.linear_constraint(linear={"y": 1, "x": 1}, sense="LE", rhs=3, name="xy_leq") # construct minimum eigen optimizer - min_eigen_solver = NumPyMinimumEigensolver() + with self.assertWarns(PendingDeprecationWarning): + min_eigen_solver = NumPyMinimumEigensolver() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) # a single converter qp2qubo = QuadraticProgramToQubo() recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( min_eigen_optimizer, min_num_vars=2, converters=qp2qubo ) - result = recursive_min_eigen_optimizer.solve(op) + with self.assertWarns(PendingDeprecationWarning): + result = recursive_min_eigen_optimizer.solve(op) self.assertEqual(result.fval, 4) # a list of converters ineq2eq = InequalityToEquality() @@ -189,7 +199,8 @@ def test_converter_list(self): recursive_min_eigen_optimizer = RecursiveMinimumEigenOptimizer( min_eigen_optimizer, min_num_vars=2, converters=converters ) - result = recursive_min_eigen_optimizer.solve(op) + with self.assertWarns(PendingDeprecationWarning): + result = recursive_min_eigen_optimizer.solve(op) self.assertEqual(result.fval, 4) # invalid converters with self.assertRaises(TypeError): diff --git a/test/algorithms/legacy/test_warm_start_qaoa.py b/test/algorithms/legacy/test_warm_start_qaoa.py index c94aae4e3..3893f0e23 100644 --- a/test/algorithms/legacy/test_warm_start_qaoa.py +++ b/test/algorithms/legacy/test_warm_start_qaoa.py @@ -53,7 +53,8 @@ def test_max_cut(self): problem = Maxcut(graph).to_quadratic_program() backend = BasicAer.get_backend("statevector_simulator") - qaoa = QAOA(quantum_instance=backend, reps=1) + with self.assertWarns(PendingDeprecationWarning): + qaoa = QAOA(quantum_instance=backend, reps=1) aggregator = MeanAggregator() optimizer = WarmStartQAOAOptimizer( pre_solver=presolver, @@ -63,7 +64,8 @@ def test_max_cut(self): num_initial_solutions=10, aggregator=aggregator, ) - result_warm = optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result_warm = optimizer.solve(problem) self.assertIsNotNone(result_warm) self.assertIsNotNone(result_warm.x) @@ -86,7 +88,8 @@ def test_constrained_binary(self): problem = from_docplex_mp(model) backend = BasicAer.get_backend("statevector_simulator") - qaoa = QAOA(quantum_instance=backend, reps=1) + with self.assertWarns(PendingDeprecationWarning): + qaoa = QAOA(quantum_instance=backend, reps=1) aggregator = MeanAggregator() optimizer = WarmStartQAOAOptimizer( pre_solver=SlsqpOptimizer(), @@ -95,7 +98,8 @@ def test_constrained_binary(self): epsilon=0.25, aggregator=aggregator, ) - result_warm = optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result_warm = optimizer.solve(problem) self.assertIsNotNone(result_warm) self.assertIsNotNone(result_warm.x) @@ -114,14 +118,16 @@ def test_simple_qubo(self): problem = from_docplex_mp(model) backend = BasicAer.get_backend("statevector_simulator") - qaoa = QAOA(quantum_instance=backend, reps=1) + with self.assertWarns(PendingDeprecationWarning): + qaoa = QAOA(quantum_instance=backend, reps=1) optimizer = WarmStartQAOAOptimizer( pre_solver=SlsqpOptimizer(), relax_for_pre_solver=True, qaoa=qaoa, epsilon=0.25, ) - result_warm = optimizer.solve(problem) + with self.assertWarns(PendingDeprecationWarning): + result_warm = optimizer.solve(problem) self.assertIsNotNone(result_warm) self.assertIsNotNone(result_warm.x) diff --git a/test/applications/test_sk_model.py b/test/applications/test_sk_model.py index d4027f091..a6d90e52e 100644 --- a/test/applications/test_sk_model.py +++ b/test/applications/test_sk_model.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2021. +# (C) Copyright IBM 2021, 2022. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -29,8 +29,8 @@ def setUp(self): super().setUp() self._num_of_sites = 2 self._seed = 0 - self._graph = nx.convert_matrix.from_numpy_matrix(np.array([[0, -1], [-1, 0]])) - self._new_disorder_graph = nx.convert_matrix.from_numpy_matrix(np.array([[0, 1], [1, 0]])) + self._graph = nx.convert_matrix.from_numpy_array(np.array([[0, -1], [-1, 0]])) + self._new_disorder_graph = nx.convert_matrix.from_numpy_array(np.array([[0, 1], [1, 0]])) op = QuadraticProgram() for _ in range(2): From 9235e764f7df4cf7f1c62a62ea43069e807dcd49 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Tue, 22 Nov 2022 11:28:12 +0900 Subject: [PATCH 11/19] revert update of matplotlib and networkx --- qiskit_optimization/applications/bin_packing.py | 2 +- test/applications/test_sk_model.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qiskit_optimization/applications/bin_packing.py b/qiskit_optimization/applications/bin_packing.py index 73320145c..65155673d 100755 --- a/qiskit_optimization/applications/bin_packing.py +++ b/qiskit_optimization/applications/bin_packing.py @@ -118,7 +118,7 @@ def get_figure(self, result: Union[OptimizationResult, np.ndarray]) -> Figure: """ import matplotlib.pyplot as plt - colors = plt.colormaps.get_cmap("jet").resampled(len(self._weights)) + colors = plt.cm.get_cmap("jet", len(self._weights)) items_in_bins = self.interpret(result) num_bins = len(items_in_bins) fig, axes = plt.subplots() diff --git a/test/applications/test_sk_model.py b/test/applications/test_sk_model.py index a6d90e52e..921d17a08 100644 --- a/test/applications/test_sk_model.py +++ b/test/applications/test_sk_model.py @@ -29,8 +29,8 @@ def setUp(self): super().setUp() self._num_of_sites = 2 self._seed = 0 - self._graph = nx.convert_matrix.from_numpy_array(np.array([[0, -1], [-1, 0]])) - self._new_disorder_graph = nx.convert_matrix.from_numpy_array(np.array([[0, 1], [1, 0]])) + self._graph = nx.convert_matrix.from_numpy_matrix(np.array([[0, -1], [-1, 0]])) + self._new_disorder_graph = nx.convert_matrix.from_numpy_matrix(np.array([[0, 1], [1, 0]])) op = QuadraticProgram() for _ in range(2): From 2cc10b28cdaaf5f98bf88589ed8909b6005d2b8f Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Tue, 22 Nov 2022 21:45:52 +0900 Subject: [PATCH 12/19] add prelude --- .pylintdict | 1 + .../add-primitives-support-31af39549b5e66e3.yaml | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.pylintdict b/.pylintdict index 5b1d7af55..5378ad385 100644 --- a/.pylintdict +++ b/.pylintdict @@ -126,6 +126,7 @@ nosignatures np num numpy +numpyminimumeigensolver october optimality optimizationresult diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml index 611562745..6ea514ad1 100644 --- a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -1,6 +1,9 @@ --- prelude: > - Introduction of new algorithms (TODO) + Qiskit Optimization 0.5 supports the new algorithms introduced in Qiskit Terra 0.22 + which in turn rely on the `Qiskit Primitives `_. + Qiskit Optimization 0.5 still supports the conventional algorithms based on ``QuantumInstance``. + But they will be deprecated in the future release. features: - | @@ -15,13 +18,14 @@ features: (``qiskit.algorithms.minimum_eigensolvers.SamplingMinimumEigensolver`` and ``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver``) as ``min_eigen_solver`` argument. - The conventional algorithms ``qiskit.algorithms.minimum_eigen_solvers.MinimumEigensolver`` + The conventional algorithm ``qiskit.algorithms.minimum_eigen_solvers.MinimumEigensolver`` is pending deprecation and will be deprecated and subsequently removed in future releases. Note that ``qiskit.algorithms.minimum_eigensolvers.SamplingVQE`` supersedes ``qiskit.algorithms.VQE`` for :class:`~.MinimumEigenOptimizer`. + ``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver`` also supersedes + ``qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver`. - | The :class:`~.WarmStartQAOAOptimizer` class takes the primitives-based QAOA (``qiskit.algorithms.minimum_eigensolvers.QAOA``) as ``qaoa`` argument. The conventional algorithm ``qiskit.algorithms.minimum_eigen_solvers.QAOA`` - is pending deprecation and will be deprecated - and subsequently removed in future releases. + is pending deprecation and will be deprecated and subsequently removed in future releases. From ecdf219d84ae83bb7d9d699ec7e22ef9dc30e8c7 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com> Date: Thu, 24 Nov 2022 12:32:58 +0900 Subject: [PATCH 13/19] Update releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> --- .../notes/add-primitives-support-31af39549b5e66e3.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml index 6ea514ad1..dd5a2fa29 100644 --- a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -2,8 +2,8 @@ prelude: > Qiskit Optimization 0.5 supports the new algorithms introduced in Qiskit Terra 0.22 which in turn rely on the `Qiskit Primitives `_. - Qiskit Optimization 0.5 still supports the conventional algorithms based on ``QuantumInstance``. - But they will be deprecated in the future release. + Qiskit Optimization 0.5 still supports the former algorithms based on ``QuantumInstance``, + but they will be deprecated and then removed, along with the support here, in future releases. features: - | From 4c0b22572d36c00e04c804ae2794ec3d2e0436b0 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Thu, 24 Nov 2022 12:44:49 +0900 Subject: [PATCH 14/19] update reno --- .../notes/add-primitives-support-31af39549b5e66e3.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml index dd5a2fa29..3607235b4 100644 --- a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -18,7 +18,7 @@ features: (``qiskit.algorithms.minimum_eigensolvers.SamplingMinimumEigensolver`` and ``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver``) as ``min_eigen_solver`` argument. - The conventional algorithm ``qiskit.algorithms.minimum_eigen_solvers.MinimumEigensolver`` + The former algorithm ``qiskit.algorithms.minimum_eigen_solvers.MinimumEigensolver`` is pending deprecation and will be deprecated and subsequently removed in future releases. Note that ``qiskit.algorithms.minimum_eigensolvers.SamplingVQE`` supersedes ``qiskit.algorithms.VQE`` for :class:`~.MinimumEigenOptimizer`. @@ -27,5 +27,5 @@ features: - | The :class:`~.WarmStartQAOAOptimizer` class takes the primitives-based QAOA (``qiskit.algorithms.minimum_eigensolvers.QAOA``) as ``qaoa`` argument. - The conventional algorithm ``qiskit.algorithms.minimum_eigen_solvers.QAOA`` + The former algorithm ``qiskit.algorithms.minimum_eigen_solvers.QAOA`` is pending deprecation and will be deprecated and subsequently removed in future releases. From 618204bf2ecb975c7b38c191e2117919563da465 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Thu, 24 Nov 2022 14:41:39 +0900 Subject: [PATCH 15/19] revert grover reno file to avoid sphinx warning --- .../notes/add-primitives-support-31af39549b5e66e3.yaml | 7 ------- .../notes/grover-opt-primitive-de82d051d6cee2e4.yaml | 9 +++++++++ 2 files changed, 9 insertions(+), 7 deletions(-) create mode 100644 releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml index 3607235b4..4a393a37c 100644 --- a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -6,13 +6,6 @@ prelude: > but they will be deprecated and then removed, along with the support here, in future releases. features: - - | - The :class:`~.GroverOptimizer` class has a new keyword argument, ``sampler`` which is - used to run the algorithm using an instance of the :class:`~.BaseSampler` - interface to calculate the results. This new argument supersedes - the ``quantum_instance`` argument and accordingly, ``quantum_instance`` - is pending deprecation and will be deprecated and subsequently removed in - future releases. - | The :class:`~.MinimumEigenOptimizer` class takes the primitives-based algorithms (``qiskit.algorithms.minimum_eigensolvers.SamplingMinimumEigensolver`` and diff --git a/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml b/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml new file mode 100644 index 000000000..032867adf --- /dev/null +++ b/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml @@ -0,0 +1,9 @@ +--- +features: + - | + The :class:`~.GroverOptimizer` class has a new keyword argument, ``sampler`` which is + used to run the algorithm using an instance of the :class:`~.BaseSampler` + interface to calculate the results. This new argument supersedes the + the ``quantum_instance`` argument and accordingly, ``quantum_instance`` + is pending deprecation and will be deprecated and subsequently removed in + future releases. From 8c43a0d66403191a8f47dd246f7f3f02c10016ec Mon Sep 17 00:00:00 2001 From: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> Date: Thu, 24 Nov 2022 09:18:10 -0500 Subject: [PATCH 16/19] Update releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml --- releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml index 4a393a37c..0536aebd4 100644 --- a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -16,7 +16,7 @@ features: Note that ``qiskit.algorithms.minimum_eigensolvers.SamplingVQE`` supersedes ``qiskit.algorithms.VQE`` for :class:`~.MinimumEigenOptimizer`. ``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver`` also supersedes - ``qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver`. + ``qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver``. - | The :class:`~.WarmStartQAOAOptimizer` class takes the primitives-based QAOA (``qiskit.algorithms.minimum_eigensolvers.QAOA``) as ``qaoa`` argument. From 086849256060027ac82baaa47f5e6b79cbab7743 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Thu, 24 Nov 2022 23:46:10 +0900 Subject: [PATCH 17/19] update sample_most_likely --- .../applications/optimization_application.py | 45 +++++++++++++++---- .../test_optimization_application.py | 44 ++++++++++++++++++ 2 files changed, 80 insertions(+), 9 deletions(-) create mode 100644 test/applications/test_optimization_application.py diff --git a/qiskit_optimization/applications/optimization_application.py b/qiskit_optimization/applications/optimization_application.py index 9680695ce..32aae008e 100644 --- a/qiskit_optimization/applications/optimization_application.py +++ b/qiskit_optimization/applications/optimization_application.py @@ -1,6 +1,6 @@ # This code is part of Qiskit. # -# (C) Copyright IBM 2018, 2021. +# (C) Copyright IBM 2018, 2022. # # This code is licensed under the Apache License, Version 2.0. You may # obtain a copy of this license in the LICENSE.txt file in the root directory @@ -11,13 +11,15 @@ # that they have been altered from the originals. """An abstract class for optimization application classes.""" -from typing import Union, Dict -from collections import OrderedDict from abc import ABC, abstractmethod +from collections import OrderedDict +from typing import Dict, Union import numpy as np - from qiskit.opflow import StateFn +from qiskit.quantum_info import Statevector +from qiskit.result import QuasiDistribution + from qiskit_optimization.algorithms import OptimizationResult from qiskit_optimization.problems.quadratic_program import QuadraticProgram @@ -59,25 +61,45 @@ def _result_to_x(self, result: Union[OptimizationResult, np.ndarray]) -> np.ndar return x @staticmethod - def sample_most_likely(state_vector: Union[np.ndarray, Dict]) -> np.ndarray: + def sample_most_likely( + state_vector: Union[QuasiDistribution, Statevector, np.ndarray, Dict] + ) -> np.ndarray: """Compute the most likely binary string from state vector. Args: - state_vector: state vector or counts. + state_vector: state vector or counts or quasi-probabilities. Returns: binary string as numpy.ndarray of ints. + + Raises: + ValueError: if state_vector is not QuasiDistribution, Statevector, + np.ndarray, or dict. """ - if isinstance(state_vector, (OrderedDict, dict)): + if isinstance(state_vector, QuasiDistribution): + probabilities = state_vector.binary_probabilities() + binary_string = max(probabilities.items(), key=lambda kv: kv[1])[0] + x = np.asarray([int(y) for y in reversed(list(binary_string))]) + return x + elif isinstance(state_vector, Statevector): + probabilities = state_vector.probabilities() + n = state_vector.num_qubits + k = np.argmax(np.abs(probabilities)) + x = np.zeros(n) + for i in range(n): + x[i] = k % 2 + k >>= 1 + return x + elif isinstance(state_vector, (OrderedDict, dict)): # get the binary string with the largest count - binary_string = sorted(state_vector.items(), key=lambda kv: kv[1])[-1][0] + binary_string = max(state_vector.items(), key=lambda kv: kv[1])[0] x = np.asarray([int(y) for y in reversed(list(binary_string))]) return x elif isinstance(state_vector, StateFn): binary_string = list(state_vector.sample().keys())[0] x = np.asarray([int(y) for y in reversed(list(binary_string))]) return x - else: + elif isinstance(state_vector, np.ndarray): n = int(np.log2(state_vector.shape[0])) k = np.argmax(np.abs(state_vector)) x = np.zeros(n) @@ -85,3 +107,8 @@ def sample_most_likely(state_vector: Union[np.ndarray, Dict]) -> np.ndarray: x[i] = k % 2 k >>= 1 return x + else: + raise ValueError( + "state vector should be QuasiDistribution, Statevector, ndarray, or dict. " + f"But it is {type(state_vector)}." + ) diff --git a/test/applications/test_optimization_application.py b/test/applications/test_optimization_application.py new file mode 100644 index 000000000..7c5e5f020 --- /dev/null +++ b/test/applications/test_optimization_application.py @@ -0,0 +1,44 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2022. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Test OptimizationApplication class""" + +import unittest +from test.optimization_test_case import QiskitOptimizationTestCase + +import numpy as np +from ddt import data, ddt +from qiskit.opflow import StateFn +from qiskit.result import QuasiDistribution + +from qiskit_optimization.applications import OptimizationApplication + + +@ddt +class TestOptimizationApplication(QiskitOptimizationTestCase): + """Test OptimizationApplication class""" + + @data( + np.array([0, 0, 1, 0]), + StateFn([0, 0, 1, 0]), + {"10": 0.8, "01": 0.2}, + QuasiDistribution({"10": 0.8, "01": 0.2}), + ) + def test_sample_most_likely(self, state_vector): + """Test sample_most_likely""" + + result = OptimizationApplication.sample_most_likely(state_vector) + np.testing.assert_allclose(result, [0, 1]) + + +if __name__ == "__main__": + unittest.main() From 01de8dfc50f44c5430c1a741d7e720387bb89ebc Mon Sep 17 00:00:00 2001 From: Takashi Imamichi Date: Mon, 28 Nov 2022 17:06:13 +0900 Subject: [PATCH 18/19] update reno --- ...d-primitives-support-31af39549b5e66e3.yaml | 20 +++++++++---------- ...grover-opt-primitive-de82d051d6cee2e4.yaml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml index 0536aebd4..94d48700d 100644 --- a/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml +++ b/releasenotes/notes/add-primitives-support-31af39549b5e66e3.yaml @@ -2,23 +2,23 @@ prelude: > Qiskit Optimization 0.5 supports the new algorithms introduced in Qiskit Terra 0.22 which in turn rely on the `Qiskit Primitives `_. - Qiskit Optimization 0.5 still supports the former algorithms based on ``QuantumInstance``, + Qiskit Optimization 0.5 still supports the former algorithms based on :class:`qiskit.utils.QuantumInstance`, but they will be deprecated and then removed, along with the support here, in future releases. features: - | The :class:`~.MinimumEigenOptimizer` class takes the primitives-based algorithms - (``qiskit.algorithms.minimum_eigensolvers.SamplingMinimumEigensolver`` and - ``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver``) + (:class:`qiskit.algorithms.minimum_eigensolvers.SamplingMinimumEigensolver` and + :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver`) as ``min_eigen_solver`` argument. - The former algorithm ``qiskit.algorithms.minimum_eigen_solvers.MinimumEigensolver`` + The former algorithm :class:`qiskit.algorithms.MinimumEigensolver` is pending deprecation and will be deprecated and subsequently removed in future releases. - Note that ``qiskit.algorithms.minimum_eigensolvers.SamplingVQE`` supersedes - ``qiskit.algorithms.VQE`` for :class:`~.MinimumEigenOptimizer`. - ``qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver`` also supersedes - ``qiskit.algorithms.minimum_eigen_solvers.NumPyMinimumEigensolver``. + Note that :class:`qiskit.algorithms.minimum_eigensolvers.SamplingVQE` supersedes + :class:`qiskit.algorithms.VQE` for :class:`~.MinimumEigenOptimizer`. + :class:`qiskit.algorithms.minimum_eigensolvers.NumPyMinimumEigensolver` also supersedes + :class:`qiskit.algorithms.NumPyMinimumEigensolver`. - | The :class:`~.WarmStartQAOAOptimizer` class takes the primitives-based QAOA - (``qiskit.algorithms.minimum_eigensolvers.QAOA``) as ``qaoa`` argument. - The former algorithm ``qiskit.algorithms.minimum_eigen_solvers.QAOA`` + (:class:`qiskit.algorithms.minimum_eigensolvers.QAOA`) as ``qaoa`` argument. + The former algorithm :class:`qiskit.algorithms.QAOA` is pending deprecation and will be deprecated and subsequently removed in future releases. diff --git a/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml b/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml index 032867adf..7aea9b34a 100644 --- a/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml +++ b/releasenotes/notes/grover-opt-primitive-de82d051d6cee2e4.yaml @@ -2,7 +2,7 @@ features: - | The :class:`~.GroverOptimizer` class has a new keyword argument, ``sampler`` which is - used to run the algorithm using an instance of the :class:`~.BaseSampler` + used to run the algorithm using an instance of the :class:`qiskit.primitives.BaseSampler` interface to calculate the results. This new argument supersedes the the ``quantum_instance`` argument and accordingly, ``quantum_instance`` is pending deprecation and will be deprecated and subsequently removed in From 1428776513c5ed844049fbbd144f8509a7805299 Mon Sep 17 00:00:00 2001 From: Takashi Imamichi <31178928+t-imamichi@users.noreply.github.com> Date: Tue, 29 Nov 2022 10:55:55 +0900 Subject: [PATCH 19/19] Apply Steve's suggestions from code review --- qiskit_optimization/algorithms/grover_optimizer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qiskit_optimization/algorithms/grover_optimizer.py b/qiskit_optimization/algorithms/grover_optimizer.py index 66dec73dd..b8359c0c6 100644 --- a/qiskit_optimization/algorithms/grover_optimizer.py +++ b/qiskit_optimization/algorithms/grover_optimizer.py @@ -237,7 +237,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: # Initialize oracle helper object. qr_key_value = QuantumRegister(self._num_key_qubits + self._num_value_qubits) orig_constant = problem_.objective.constant - measurement = not (self._quantum_instance and self._quantum_instance.is_statevector) + measurement = self._quantum_instance is None or not self._quantum_instance.is_statevector oracle, is_good_state = self._get_oracle(qr_key_value) while not optimum_found: @@ -284,7 +284,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: threshold = optimum_value # trace out work qubits and store samples - if self._sampler: + if self._sampler is not None: self._circuit_results = { i[-1 * n_key :]: v for i, v in self._circuit_results.items() }