diff --git a/.pylintdict b/.pylintdict index 0839ba96e..8307ce36f 100644 --- a/.pylintdict +++ b/.pylintdict @@ -104,6 +104,7 @@ qp quadratically quadraticconstraint quadraticprogram +quantuminstance qubit qubits qubo diff --git a/qiskit_optimization/algorithms/grover_optimizer.py b/qiskit_optimization/algorithms/grover_optimizer.py index ca14d64dd..fe38fe4f2 100644 --- a/qiskit_optimization/algorithms/grover_optimizer.py +++ b/qiskit_optimization/algorithms/grover_optimizer.py @@ -59,10 +59,10 @@ def __init__(self, num_value_qubits: int, num_iterations: int = 3, TypeError: When there one of converters is an invalid type. """ self._num_value_qubits = num_value_qubits - self._num_key_qubits = None + self._num_key_qubits = 0 self._n_iterations = num_iterations - self._quantum_instance = None - self._circuit_results = {} # type: ignore + self._quantum_instance = None # type: Optional[QuantumInstance] + self._circuit_results = {} # type: dict if quantum_instance is not None: self.quantum_instance = quantum_instance @@ -128,7 +128,7 @@ def _get_oracle(self, qr_key_value): oracle = QuantumCircuit(qr_key_value, oracle_bit) oracle.z(self._num_key_qubits) # recognize negative values. - def is_good_state(self, measurement): + def is_good_state(measurement): """Check whether ``measurement`` is a good state or not.""" value = measurement[self._num_key_qubits:self._num_key_qubits + self._num_value_qubits] return value[0] == '1' @@ -168,7 +168,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: problem_.objective.linear[i] = -val for (i, j), val in problem_.objective.quadratic.to_dict().items(): problem_.objective.quadratic[i, j] = -val - self._num_key_qubits = len(problem_.objective.linear.to_array()) # type: ignore + self._num_key_qubits = len(problem_.objective.linear.to_array()) # Variables for tracking the optimum. optimum_found = False @@ -209,7 +209,7 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: while not improvement_found: # Determine the number of rotations. loops_with_no_improvement += 1 - rotation_count = int(np.ceil(algorithm_globals.random.uniform(0, m - 1))) + rotation_count = algorithm_globals.random.integers(0, m) rotations += rotation_count # Apply Grover's Algorithm to find values below the threshold. # TODO: Utilize Grover's incremental feature - requires changes to Grover. @@ -237,13 +237,14 @@ def solve(self, problem: QuadraticProgram) -> OptimizationResult: threshold = optimum_value # trace out work qubits and store samples - if self._quantum_instance.is_statevector: # type: ignore + if self._quantum_instance.is_statevector: indices = list(range(n_key, len(outcome))) rho = partial_trace(self._circuit_results, indices) self._circuit_results = np.diag(rho.data) ** 0.5 else: - self._circuit_results = {i[0:n_key]: v for i, - v in self._circuit_results.items()} + self._circuit_results = { + i[-1 * n_key:]: v for i, v in self._circuit_results.items() + } raw_samples = self._eigenvector_to_solutions(self._circuit_results, problem_init) @@ -290,11 +291,9 @@ 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) - freq = sorted(probs.items(), key=lambda x: x[1], reverse=True) + logger.info("Frequencies: %s", probs) # Pick a random outcome. - idx = algorithm_globals.random.choice(len(freq), 1, p=[x[1] for x in freq])[0] - logger.info('Frequencies: %s', freq) - return freq[idx][0] + return algorithm_globals.random.choice(list(probs.keys()), 1, p=list(probs.values()))[0] def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: """Gets probabilities from a given backend.""" @@ -312,8 +311,8 @@ def _get_probs(self, qc: QuantumCircuit) -> Dict[str, float]: else: state = result.get_counts(qc) shots = self.quantum_instance.run_config.shots - hist = {key[::-1]: val / shots for key, val in state.items() if val > 0} - self._circuit_results = {b[::-1]: np.sqrt(v / shots) for (b, v) in state.items()} + 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 diff --git a/qiskit_optimization/algorithms/optimization_algorithm.py b/qiskit_optimization/algorithms/optimization_algorithm.py index 88db2a5af..9afed2edb 100644 --- a/qiskit_optimization/algorithms/optimization_algorithm.py +++ b/qiskit_optimization/algorithms/optimization_algorithm.py @@ -503,22 +503,23 @@ def _eigenvector_to_solutions(eigenvector: Union[dict, np.ndarray, StateFn], TypeError: If the type of eigenvector is not supported. """ if isinstance(eigenvector, DictStateFn): - eigenvector = {bitstr: val ** 2 for (bitstr, val) in eigenvector.primitive.items()} + eigenvector = eigenvector.primitive elif isinstance(eigenvector, StateFn): eigenvector = eigenvector.to_matrix() def generate_solution(bitstr, qubo, probability): - x = np.fromiter(list(bitstr), dtype=int) + x = np.fromiter(list(bitstr[::-1]), dtype=int) fval = qubo.objective.evaluate(x) return SolutionSample(x=x, fval=fval, probability=probability, status=OptimizationResultStatus.SUCCESS) solutions = [] if isinstance(eigenvector, dict): - all_counts = sum(eigenvector.values()) + # 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()} # iterate over all samples - for bitstr, count in eigenvector.items(): - sampling_probability = count / all_counts + 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)) @@ -531,7 +532,7 @@ def generate_solution(bitstr, qubo, probability): 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 = '{:b}'.format(i).rjust(num_qubits, '0')[::-1] + bitstr = "{:b}".format(i).rjust(num_qubits, "0") solutions.append(generate_solution(bitstr, qubo, sampling_probability)) else: diff --git a/qiskit_optimization/problems/quadratic_program.py b/qiskit_optimization/problems/quadratic_program.py index 0a60106d8..b151274f0 100644 --- a/qiskit_optimization/problems/quadratic_program.py +++ b/qiskit_optimization/problems/quadratic_program.py @@ -1153,7 +1153,7 @@ def substitute_variables( Returns: An optimization problem by substituting variables with constants or other variables. If the substitution is valid, `QuadraticProgram.status` is still - `QuadraticProgram.Status.VALIAD`. + `QuadraticProgram.Status.VALID`. Otherwise, it gets `QuadraticProgram.Status.INFEASIBLE`. Raises: @@ -1166,6 +1166,10 @@ def substitute_variables( def to_ising(self) -> Tuple[OperatorBase, float]: """Return the Ising Hamiltonian of this problem. + Variables are mapped to qubits in the same order, i.e., + i-th variable is mapped to i-th qubit. + See https://github.com/Qiskit/qiskit-terra/issues/1148 for details. + Returns: qubit_op: The qubit operator for the problem offset: The constant value in the Ising Hamiltonian. @@ -1265,6 +1269,10 @@ def from_ising(self, offset: float = 0.0, linear: bool = False) -> None: r"""Create a quadratic program from a qubit operator and a shift value. + Variables are mapped to qubits in the same order, i.e., + i-th variable is mapped to i-th qubit. + See https://github.com/Qiskit/qiskit-terra/issues/1148 for details. + Args: qubit_op: The qubit operator of the problem. offset: The constant value in the Ising Hamiltonian. @@ -1464,7 +1472,7 @@ def substitute_variables( Returns: An optimization problem by substituting variables with constants or other variables. If the substitution is valid, `QuadraticProgram.status` is still - `QuadraticProgram.Status.VALIAD`. + `QuadraticProgram.Status.VALID`. Otherwise, it gets `QuadraticProgram.Status.INFEASIBLE`. Raises: diff --git a/releasenotes/notes/fix-bit-ordering-e807ec9f4b206ec3.yaml b/releasenotes/notes/fix-bit-ordering-e807ec9f4b206ec3.yaml new file mode 100644 index 000000000..f86f04f90 --- /dev/null +++ b/releasenotes/notes/fix-bit-ordering-e807ec9f4b206ec3.yaml @@ -0,0 +1,8 @@ +--- +fixes: + - | + Fix bit ordering in :class`qiskit_optimization.algorithms.minimum_eigen_optimizer.MinimumEigenOptimizer` + with qasm_simulator. + - | + Fix probabilities of solution samples with qasm_simulator. + See https://github.com/Qiskit/qiskit-optimization/pull/97 for details. diff --git a/releasenotes/notes/fix-grover-rotation-count-853baefc6b7e8476.yaml b/releasenotes/notes/fix-grover-rotation-count-853baefc6b7e8476.yaml new file mode 100644 index 000000000..4ac4210b7 --- /dev/null +++ b/releasenotes/notes/fix-grover-rotation-count-853baefc6b7e8476.yaml @@ -0,0 +1,12 @@ +--- +fixes: + - | + Fixes ``rotation_count`` in :class`qiskit_optimization.algorithms.GroverOptimizer`. + This fix uses ``algorithm_globals.random.integers(0, m)`` to generate a random integer + in a range 0..``m``-1. + - | + Sorts the order of ``result.get_counts(qc)`` by bitstring + in :class`qiskit_optimization.algorithms.GroverOptimizer` when ``qasm_simulator`` is used + so that the algorithm behaves deterministically. + The previous version sorts the counts by probabilities, but some bitstrings may have + the same probability and the algorithm could behave probabilistically. diff --git a/test/algorithms/test_grover_optimizer.py b/test/algorithms/test_grover_optimizer.py index 0047a9628..250bd7765 100644 --- a/test/algorithms/test_grover_optimizer.py +++ b/test/algorithms/test_grover_optimizer.py @@ -38,17 +38,21 @@ class TestGroverOptimizer(QiskitOptimizationTestCase): def setUp(self): super().setUp() algorithm_globals.random_seed = 1 - self.sv_simulator = QuantumInstance(Aer.get_backend('statevector_simulator'), - seed_simulator=921, seed_transpiler=200) - self.qasm_simulator = QuantumInstance(Aer.get_backend('qasm_simulator'), - seed_simulator=123, seed_transpiler=123) + self.sv_simulator = QuantumInstance( + Aer.get_backend("statevector_simulator"), + seed_simulator=921, + seed_transpiler=200, + ) + self.qasm_simulator = QuantumInstance( + Aer.get_backend("qasm_simulator"), seed_simulator=123, seed_transpiler=123 + ) + self.n_iter = 8 def validate_results(self, problem, results): """Validate the results object returned by GroverOptimizer.""" # Get expected value. solver = MinimumEigenOptimizer(NumPyMinimumEigensolver()) comp_result = solver.solve(problem) - # Validate results. np.testing.assert_array_almost_equal(comp_result.x, results.x) self.assertEqual(comp_result.fval, results.fval) @@ -84,8 +88,7 @@ def test_qubo_gas_int_simple(self): op.from_docplex(model) # Get the optimum key and value. - n_iter = 8 - gmf = GroverOptimizer(4, num_iterations=n_iter, quantum_instance=self.sv_simulator) + gmf = GroverOptimizer(4, num_iterations=self.n_iter, quantum_instance=self.sv_simulator) results = gmf.solve(op) self.validate_results(op, results) @@ -105,8 +108,7 @@ def test_qubo_gas_int_simple_maximize(self): op.from_docplex(model) # Get the optimum key and value. - n_iter = 8 - gmf = GroverOptimizer(4, num_iterations=n_iter, quantum_instance=self.sv_simulator) + gmf = GroverOptimizer(4, num_iterations=self.n_iter, quantum_instance=self.sv_simulator) results = gmf.solve(op) self.validate_results(op, results) @@ -127,16 +129,15 @@ def test_qubo_gas_int_paper_example(self, simulator): op.from_docplex(model) # Get the optimum key and value. - n_iter = 10 - - q_instance = self.sv_simulator if simulator == 'sv' else self.qasm_simulator - gmf = GroverOptimizer(6, num_iterations=n_iter, quantum_instance=q_instance) + 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) self.validate_results(op, results) def test_converter_list(self): """Test converters list""" # Input. + model = Model() x_0 = model.binary_var(name='x0') x_1 = model.binary_var(name='x1') @@ -145,54 +146,101 @@ def test_converter_list(self): op.from_docplex(model) # Get the optimum key and value. - n_iter = 8 # a single converter. qp2qubo = QuadraticProgramToQubo() - gmf = GroverOptimizer(4, num_iterations=n_iter, quantum_instance=self.sv_simulator, - converters=qp2qubo) + gmf = GroverOptimizer( + 4, + num_iterations=self.n_iter, + quantum_instance=self.sv_simulator, + converters=qp2qubo, + ) results = gmf.solve(op) self.validate_results(op, results) + # a list of converters ineq2eq = InequalityToEquality() int2bin = IntegerToBinary() penalize = LinearEqualityToPenalty() converters = [ineq2eq, int2bin, penalize] - gmf = GroverOptimizer(4, num_iterations=n_iter, quantum_instance=self.sv_simulator, - converters=converters) + gmf = GroverOptimizer( + 4, + num_iterations=self.n_iter, + quantum_instance=self.sv_simulator, + converters=converters, + ) results = gmf.solve(op) self.validate_results(op, results) # invalid converters with self.assertRaises(TypeError): invalid = [qp2qubo, "invalid converter"] - GroverOptimizer(4, num_iterations=n_iter, - quantum_instance=self.sv_simulator, - converters=invalid) - - def test_samples_and_raw_samples(self): + GroverOptimizer( + 4, + num_iterations=self.n_iter, + quantum_instance=self.sv_simulator, + converters=invalid, + ) + + @data("sv", "qasm") + def test_samples_and_raw_samples(self, simulator): """Test samples and raw_samples""" + algorithm_globals.random_seed = 2 op = QuadraticProgram() - op.integer_var(0, 3, 'x') - op.binary_var('y') - op.minimize(linear={'x': 1, 'y': 2}) - op.linear_constraint(linear={'x': 1, 'y': 1}, sense='>=', rhs=1, name='xy') + op.integer_var(0, 3, "x") + 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 + ) opt_sol = 1 success = OptimizationResultStatus.SUCCESS - algorithm_globals.random_seed = 1 + results = grover_optimizer.solve(op) + self.assertEqual(len(results.samples), 8) + self.assertEqual(len(results.raw_samples), 32) + self.assertAlmostEqual(sum(s.probability for s in results.samples), 1) + self.assertAlmostEqual(sum(s.probability for s in results.raw_samples), 1) + self.assertAlmostEqual(min(s.fval for s in results.samples), 0) + 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.x, results.samples[0].x) + self.assertAlmostEqual(results.fval, results.samples[0].fval) + self.assertEqual(results.status, results.samples[0].status) + self.assertAlmostEqual(results.fval, results.raw_samples[0].fval) + 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") + 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") + mdl.minimize(x - 2 * y) + op = QuadraticProgram() + op.from_docplex(mdl) + opt_sol = -2 + success = OptimizationResultStatus.SUCCESS grover_optimizer = GroverOptimizer( - 8, num_iterations=5, quantum_instance=self.qasm_simulator) - result = grover_optimizer.solve(op) - self.assertEqual(len(result.samples), 8) - self.assertEqual(len(result.raw_samples), 32) - 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: + 3, num_iterations=self.n_iter, quantum_instance=q_instance + ) + results = grover_optimizer.solve(op) + 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) + 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(result.x, result.raw_samples[0].x[0:2]) - self.assertAlmostEqual(result.fval, result.raw_samples[0].fval) - self.assertEqual(result.status, result.raw_samples[0].status) if __name__ == '__main__': diff --git a/test/algorithms/test_min_eigen_optimizer.py b/test/algorithms/test_min_eigen_optimizer.py index 2a65cfaa9..2383117a4 100644 --- a/test/algorithms/test_min_eigen_optimizer.py +++ b/test/algorithms/test_min_eigen_optimizer.py @@ -17,15 +17,23 @@ import numpy as np from ddt import data, ddt +from docplex.mp.model import Model 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.utils import QuantumInstance, algorithm_globals -from qiskit.algorithms import QAOA, NumPyMinimumEigensolver -from qiskit.algorithms.optimizers import COBYLA -from qiskit_optimization.algorithms import (CplexOptimizer, MinimumEigenOptimizer) -from qiskit_optimization.algorithms.optimization_algorithm import OptimizationResultStatus -from qiskit_optimization.converters import (InequalityToEquality, IntegerToBinary, - LinearEqualityToPenalty, QuadraticProgramToQubo) +from qiskit_optimization.algorithms import CplexOptimizer, MinimumEigenOptimizer +from qiskit_optimization.algorithms.optimization_algorithm import ( + OptimizationResultStatus, +) +from qiskit_optimization.converters import ( + InequalityToEquality, + IntegerToBinary, + LinearEqualityToPenalty, + QuadraticProgramToQubo, +) from qiskit_optimization.problems import QuadraticProgram @@ -44,7 +52,39 @@ def setUp(self): # QAOA optimizer = COBYLA() - self.min_eigen_solvers['qaoa'] = QAOA(optimizer=optimizer) + 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 + mdl = Model("docplex model") + x = mdl.binary_var("x") + y = mdl.binary_var("y") + mdl.minimize(x - 2 * y) + self.op_ordering = QuadraticProgram() + self.op_ordering.from_docplex(mdl) @data( ('exact', None, 'op_ip1.lp'), @@ -156,91 +196,148 @@ def test_converter_list(self): MinimumEigenOptimizer(min_eigen_solver, converters=invalid) - def test_samples(self): - """Test samples""" - SUCCESS = OptimizationResultStatus.SUCCESS # pylint: disable=invalid-name - algorithm_globals.random_seed = 123 - quantum_instance = QuantumInstance(backend=BasicAer.get_backend('qasm_simulator'), - seed_simulator=123, seed_transpiler=123, - shots=1000) - + def test_samples_numpy_eigen_solver(self): + """Test samples for NumPyMinimumEigensolver""" # test minimize - op = QuadraticProgram() - op.integer_var(0, 3, 'x') - op.binary_var('y') - op.minimize(linear={'x': 1, 'y': 2}) - op.linear_constraint(linear={'x': 1, 'y': 1}, sense='>=', rhs=1, name='xy') - min_eigen_solver = NumPyMinimumEigensolver() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) - result = min_eigen_optimizer.solve(op) + 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(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) - - qaoa = QAOA(quantum_instance=quantum_instance) - min_eigen_optimizer = MinimumEigenOptimizer(qaoa) - result = min_eigen_optimizer.solve(op) - self.assertEqual(len(result.samples), 8) - self.assertEqual(len(result.raw_samples), 32) - 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, result.samples[0].x) - self.assertAlmostEqual(result.fval, result.samples[0].fval) - self.assertEqual(result.status, result.samples[0].status) - + self.assertEqual(result.raw_samples[0].status, success) # test maximize - 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='<=', rhs=1, name='xy') - min_eigen_solver = NumPyMinimumEigensolver() min_eigen_optimizer = MinimumEigenOptimizer(min_eigen_solver) - result = min_eigen_optimizer.solve(op) + 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(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]) 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) + self.assertEqual(result.raw_samples[0].status, success) - qaoa = QAOA(quantum_instance=quantum_instance) + @data("sv", "qasm") + def test_samples_qaoa(self, simulator): + """Test samples for QAOA""" + # test minimize + algorithm_globals.random_seed = 1 + quantum_instance = self.sv_simulator if simulator == "sv" else self.qasm_simulator + qaoa = QAOA(quantum_instance=quantum_instance, reps=2) min_eigen_optimizer = MinimumEigenOptimizer(qaoa) - result = min_eigen_optimizer.solve(op) + result = min_eigen_optimizer.solve(self.op_minimize) + success = OptimizationResultStatus.SUCCESS + opt_sol = 1 + self.assertEqual(len(result.samples), 8) + self.assertEqual(len(result.raw_samples), 32) + 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(quantum_instance=quantum_instance, reps=2) + min_eigen_optimizer = MinimumEigenOptimizer(qaoa) + result = min_eigen_optimizer.solve(self.op_maximize) self.assertEqual(len(result.samples), 8) self.assertEqual(len(result.raw_samples), 16) 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) + self.assertAlmostEqual(max(s.fval for s in result.samples if s.status == success), opt_sol) self.assertAlmostEqual(max(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, result.samples[0].x) - self.assertAlmostEqual(result.fval, result.samples[0].fval) - self.assertEqual(result.status, result.samples[0].status) + 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]) + self.assertAlmostEqual(result.raw_samples[0].fval, opt_sol) + self.assertEqual(result.raw_samples[0].status, success) + # test bit ordering + opt_sol = -2 + qaoa = QAOA(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) if __name__ == '__main__':