def test_circuit_result(self): """Test circuit_result""" oracle = QuantumCircuit(2) oracle.cz(0, 1) # is_good_state=['00'] is intentionally selected to obtain a list of results problem = AmplificationProblem(oracle, is_good_state=["00"]) grover = Grover(iterations=[1, 2, 3, 4], quantum_instance=self.qasm) result = grover.amplify(problem) expected_results = [ { "11": 1024 }, { "00": 238, "01": 253, "10": 263, "11": 270 }, { "00": 238, "01": 253, "10": 263, "11": 270 }, { "11": 1024 }, ] self.assertEqual(result.circuit_results, expected_results)
def test_fixed_iterations(self): """Test the iterations argument""" grover = Grover(iterations=2, quantum_instance=self.statevector) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111")
def test_iterations_with_good_state(self, iterations): """Test the algorithm with different iteration types and with good state""" grover = Grover(iterations, quantum_instance=self.statevector) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111")
def test_max_power(self): """Test the iteration stops when the maximum power is reached.""" lam = 10.0 grover = Grover(growth_rate=lam, quantum_instance=self.statevector) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(len(result.iterations), 0)
def test_growth_rate(self): """Test running the algorithm on a growth rate""" grover = Grover(growth_rate=8 / 7, quantum_instance=self.statevector) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111")
def test_implicit_phase_oracle_is_good_state(self): """Test implicit default for is_good_state with PhaseOracle.""" grover = Grover(iterations=2, quantum_instance=self.statevector) oracle = PhaseOracle("x | x") problem = AmplificationProblem(oracle) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "0")
def test_oracle_evaluation(self): """Test oracle_evaluation for PhaseOracle""" oracle = PhaseOracle("x1 & x2 & (not x3)") problem = AmplificationProblem(oracle, is_good_state=oracle.evaluate_bitstring) grover = Grover(quantum_instance=self.qasm) result = grover.amplify(problem) self.assertTrue(result.oracle_evaluation) self.assertEqual("011", result.top_measurement)
def test_iterations_without_good_state(self, iterations): """Test the correct error is thrown for none/list of iterations and without good state""" grover = Grover(iterations, quantum_instance=self.statevector) problem = AmplificationProblem(Statevector.from_label("111")) with self.assertRaisesRegex( TypeError, "An is_good_state function is required with the provided oracle" ): grover.amplify(problem)
def test_multiple_iterations(self): """Test the algorithm for a list of iterations.""" grover = Grover(iterations=[1, 2, 3], quantum_instance=self.statevector) problem = AmplificationProblem(Statevector.from_label('111'), is_good_state=['111']) result = grover.amplify(problem) self.assertEqual(result.top_measurement, '111')
def test_run_state_vector_oracle(self): """Test execution with a state vector oracle""" mark_state = Statevector.from_label('11') problem = AmplificationProblem(mark_state, is_good_state=['11']) grover = Grover(quantum_instance=self.qasm) result = grover.amplify(problem) self.assertIn(result.top_measurement, ['11'])
def test_groverop_getter(self, kind): """Test the default construction of the Grover operator.""" oracle = QuantumCircuit(2) oracle.cz(0, 1) if kind == "oracle_only": problem = AmplificationProblem(oracle, is_good_state=["11"]) expected = GroverOperator(oracle) else: stateprep = QuantumCircuit(2) stateprep.ry(0.2, [0, 1]) problem = AmplificationProblem(oracle, state_preparation=stateprep, is_good_state=["11"]) expected = GroverOperator(oracle, stateprep) self.assertEqual(Operator(expected), Operator(problem.grover_operator))
def test_max_probability(self): """Test max_probability""" oracle = QuantumCircuit(2) oracle.cz(0, 1) problem = AmplificationProblem(oracle, is_good_state=["11"]) grover = Grover(quantum_instance=self.qasm) result = grover.amplify(problem) self.assertEqual(result.max_probability, 1.0)
def test_run_circuit_oracle(self): """Test execution with a quantum circuit oracle""" oracle = QuantumCircuit(2) oracle.cz(0, 1) problem = AmplificationProblem(oracle, is_good_state=['11']) grover = Grover(quantum_instance=self.qasm) result = grover.amplify(problem) self.assertIn(result.top_measurement, ['11'])
def test_run_circuit_oracle(self): """Test execution with a quantum circuit oracle""" oracle = QuantumCircuit(2) oracle.cz(0, 1) problem = AmplificationProblem(oracle, is_good_state=["11"]) qi = QuantumInstance(self._provider.get_backend("fake_yorktown"), seed_simulator=12, seed_transpiler=32) grover = Grover(quantum_instance=qi) result = grover.amplify(problem) self.assertIn(result.top_measurement, ["11"])
def test_run_circuit_oracle_single_experiment_backend(self): """Test execution with a quantum circuit oracle""" oracle = QuantumCircuit(2) oracle.cz(0, 1) problem = AmplificationProblem(oracle, is_good_state=["11"]) backend = self._provider.get_backend("fake_vigo") backend._configuration.max_experiments = 1 qi = QuantumInstance(backend, seed_simulator=12, seed_transpiler=32) grover = Grover(quantum_instance=qi) result = grover.amplify(problem) self.assertIn(result.top_measurement, ["11"])
def test_run_custom_grover_operator(self): """Test execution with a grover operator oracle""" oracle = QuantumCircuit(2) oracle.cz(0, 1) grover_op = GroverOperator(oracle) problem = AmplificationProblem(oracle=oracle, grover_operator=grover_op, is_good_state=['11']) grover = Grover(quantum_instance=self.qasm) ret = grover.amplify(problem) self.assertIn(ret.top_measurement, ['11'])
def test_max_num_iterations(self): """Test the iteration stops when the maximum number of iterations is reached.""" def zero(): while True: yield 0 grover = Grover(iterations=zero(), quantum_instance=self.statevector) n = 5 problem = AmplificationProblem(Statevector.from_label('1' * n), is_good_state=['1' * n]) result = grover.amplify(problem) self.assertEqual(len(result.iterations), 2**n)
def test_construct_circuit(self): """Test construct_circuit""" oracle = QuantumCircuit(2) oracle.cz(0, 1) problem = AmplificationProblem(oracle) grover = Grover() constructed = grover.construct_circuit(problem, 2, measurement=False) grover_op = GroverOperator(oracle) expected = QuantumCircuit(2) expected.h([0, 1]) expected.compose(grover_op.power(2), inplace=True) self.assertTrue(Operator(constructed).equiv(Operator(expected)))
def test_iterator(self): """Test running the algorithm on an iterator.""" # step-function iterator def iterator(): wait, value, count = 3, 1, 0 while True: yield value count += 1 if count % wait == 0: value += 1 grover = Grover(iterations=iterator(), quantum_instance=self.statevector) problem = AmplificationProblem(Statevector.from_label("111"), is_good_state=["111"]) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111")
def solve(self, problem: QuadraticProgram) -> OptimizationResult: """Tries to solves the given problem using the grover optimizer. Runs the optimizer to try to solve the optimization problem. If the problem cannot be, converted to a QUBO, this optimizer raises an exception due to incompatibility. Args: problem: The problem to be solved. Returns: The result of the optimizer applied to the problem. Raises: 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.') self._verify_compatibility(problem) # convert problem to QUBO problem_ = self._convert(problem, self._converters) problem_init = deepcopy(problem_) # convert to minimization problem sense = problem_.objective.sense if sense == problem_.objective.Sense.MAXIMIZE: problem_.objective.sense = problem_.objective.Sense.MINIMIZE problem_.objective.constant = -problem_.objective.constant for i, val in problem_.objective.linear.to_dict().items(): 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 # Variables for tracking the optimum. optimum_found = False optimum_key = math.inf optimum_value = math.inf threshold = 0 n_key = len(problem_.variables) n_value = self._num_value_qubits # Variables for tracking the solutions encountered. num_solutions = 2**n_key keys_measured = [] # Variables for result object. operation_count = {} iteration = 0 # Variables for stopping if we've hit the rotation max. rotations = 0 max_rotations = int(np.ceil(100 * np.pi / 4)) # 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 oracle, is_good_state = self._get_oracle(qr_key_value) while not optimum_found: m = 1 improvement_found = False # Get oracle O and the state preparation operator A for the current threshold. problem_.objective.constant = orig_constant - threshold a_operator = self._get_a_operator(qr_key_value, problem_) # Iterate until we measure a negative. loops_with_no_improvement = 0 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))) rotations += rotation_count # Apply Grover's Algorithm to find values below the threshold. # TODO: Utilize Grover's incremental feature - requires changes to Grover. amp_problem = AmplificationProblem( oracle=oracle, state_preparation=a_operator, is_good_state=is_good_state) grover = Grover() circuit = grover.construct_circuit(problem=amp_problem, power=rotation_count, measurement=measurement) # Get the next outcome. outcome = self._measure(circuit) 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 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). if int_v < optimum_value: optimum_key = k optimum_value = int_v logger.info('Current Optimum Key: %s', optimum_key) logger.info('Current Optimum Value: %s', optimum_value) improvement_found = True threshold = optimum_value else: # Using Durr and Hoyer method, increase m. m = int(np.ceil(min(m * 8 / 7, 2**(n_key / 2)))) logger.info('No Improvement. M: %s', m) # Check if we've already seen this value. if k not in keys_measured: keys_measured.append(k) # Assume the optimal if any of the stop parameters are true. if loops_with_no_improvement >= self._n_iterations or \ len(keys_measured) == num_solutions or rotations >= max_rotations: improvement_found = True optimum_found = True # Track the operation count. operations = circuit.count_ops() operation_count[iteration] = operations iteration += 1 logger.info('Operation Count: %s\n', operations) # If the constant is 0 and we didn't find a negative, the answer is likely 0. if optimum_value >= 0 and orig_constant == 0: optimum_key = 0 opt_x = np.array([ 1 if s == '1' else 0 for s in ('{0:%sb}' % n_key).format(optimum_key) ]) # Compute function value fval = problem_init.objective.evaluate(opt_x) # cast binaries back to integers return cast( GroverOptimizationResult, self._interpret(x=opt_x, converters=self._converters, problem=problem, result_class=GroverOptimizationResult, operation_counts=operation_count, n_input_qubits=n_key, n_output_qubits=n_value, intermediate_fval=fval, threshold=threshold))
def solve(self, problem: QuadraticProgram) -> OptimizationResult: """Tries to solves the given problem using the grover optimizer. Runs the optimizer to try to solve the optimization problem. If the problem cannot be, converted to a QUBO, this optimizer raises an exception due to incompatibility. Args: problem: The problem to be solved. Returns: The result of the optimizer applied to the problem. Raises: 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.") self._verify_compatibility(problem) # convert problem to minimization QUBO problem problem_ = self._convert(problem, self._converters) problem_init = deepcopy(problem_) self._num_key_qubits = len(problem_.objective.linear.to_array()) # Variables for tracking the optimum. optimum_found = False optimum_key = math.inf optimum_value = math.inf threshold = 0 n_key = self._num_key_qubits n_value = self._num_value_qubits # Variables for tracking the solutions encountered. num_solutions = 2**n_key keys_measured = [] # Variables for result object. operation_count = {} iteration = 0 # Variables for stopping if we've hit the rotation max. rotations = 0 max_rotations = int(np.ceil(100 * np.pi / 4)) # 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 oracle, is_good_state = self._get_oracle(qr_key_value) while not optimum_found: m = 1 improvement_found = False # Get oracle O and the state preparation operator A for the current threshold. problem_.objective.constant = orig_constant - threshold a_operator = self._get_a_operator(qr_key_value, problem_) # Iterate until we measure a negative. loops_with_no_improvement = 0 while not improvement_found: # Determine the number of rotations. loops_with_no_improvement += 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. amp_problem = AmplificationProblem( oracle=oracle, state_preparation=a_operator, is_good_state=is_good_state, ) grover = Grover() circuit = grover.construct_circuit( problem=amp_problem, power=rotation_count, measurement=measurement ) # Get the next outcome. outcome = self._measure(circuit) 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 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). if int_v < optimum_value: optimum_key = k optimum_value = int_v logger.info("Current Optimum Key: %s", optimum_key) logger.info("Current Optimum Value: %s", optimum_value) improvement_found = True 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: 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 ) raw_samples.sort(key=lambda x: x.fval) samples, _ = self._interpret_samples(problem, raw_samples, self._converters) else: # Using Durr and Hoyer method, increase m. m = int(np.ceil(min(m * 8 / 7, 2 ** (n_key / 2)))) logger.info("No Improvement. M: %s", m) # Check if we've already seen this value. if k not in keys_measured: keys_measured.append(k) # Assume the optimal if any of the stop parameters are true. if ( loops_with_no_improvement >= self._n_iterations or len(keys_measured) == num_solutions or rotations >= max_rotations ): improvement_found = True optimum_found = True # Track the operation count. operations = circuit.count_ops() operation_count[iteration] = operations iteration += 1 logger.info("Operation Count: %s\n", operations) # If the constant is 0 and we didn't find a negative, the answer is likely 0. if optimum_value >= 0 and orig_constant == 0: optimum_key = 0 opt_x = np.array([1 if s == "1" else 0 for s in f"{optimum_key:{n_key}b}"]) # Compute function value of minimization QUBO fval = problem_init.objective.evaluate(opt_x) # cast binaries back to integers and eventually minimization to maximization return cast( GroverOptimizationResult, self._interpret( x=opt_x, converters=self._converters, problem=problem, result_class=GroverOptimizationResult, samples=samples, raw_samples=raw_samples, operation_counts=operation_count, n_input_qubits=n_key, n_output_qubits=n_value, intermediate_fval=fval, threshold=threshold, ), )
def test_fixed_iterations_without_good_state(self): """Test the algorithm with iterations as an int and without good state""" grover = Grover(iterations=2, quantum_instance=self.statevector) problem = AmplificationProblem(Statevector.from_label("111")) result = grover.amplify(problem) self.assertEqual(result.top_measurement, "111")