コード例 #1
0
    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))
コード例 #2
0
    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,
            ),
        )