예제 #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._qubo_converter.convert(problem)
        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())

        # 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(aqua_globals.random.uniform(0, m - 1)))
                rotations += rotation_count

                # Apply Grover's Algorithm to find values below the threshold.
                if rotation_count > 0:
                    # TODO: Utilize Grover's incremental feature - requires changes to Grover.
                    grover = Grover(oracle,
                                    state_preparation=a_operator,
                                    good_state=is_good_state)
                    circuit = grover.construct_circuit(rotation_count,
                                                       measurement=measurement)
                else:
                    circuit = a_operator

                # 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
                v = self._twos_complement(int_v, n_value)
                logger.info('Outcome: %s', outcome)
                logger.info('Value: %s = %s', v, 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)
                    if v.startswith('1'):
                        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)
        result = OptimizationResult(x=opt_x,
                                    fval=fval,
                                    variables=problem_.variables,
                                    status=OptimizationResultStatus.SUCCESS)

        # cast binaries back to integers
        result = self._qubo_converter.interpret(result)

        return GroverOptimizationResult(x=result.x,
                                        fval=result.fval,
                                        variables=result.variables,
                                        operation_counts=operation_count,
                                        n_input_qubits=n_key,
                                        n_output_qubits=n_value,
                                        intermediate_fval=fval,
                                        threshold=threshold,
                                        status=self._get_feasibility_status(
                                            problem, result.x))
예제 #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.')

        # check compatibility and raise exception if incompatible
        msg = self.get_compatibility_msg(problem)
        if len(msg) > 0:
            raise QiskitOptimizationError(
                'Incompatible problem: {}'.format(msg))

        # convert problem to QUBO
        qubo_converter = QuadraticProgramToQubo()
        problem_ = qubo_converter.encode(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

        # 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.
        func_dict = {}  # type: Dict[Union[int, Tuple[int, int]], int]
        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.
        orig_constant = problem_.objective.constant
        measurement = not self.quantum_instance.is_statevector
        opt_prob_converter = QuadraticProgramToNegativeValueOracle(
            n_value, measurement)

        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, oracle, func_dict = opt_prob_converter.encode(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(aqua_globals.random.uniform(0, m - 1)))
                rotations += rotation_count

                # Apply Grover's Algorithm to find values below the threshold.
                if rotation_count > 0:
                    # TODO: Utilize Grover's incremental feature - requires changes to Grover.
                    grover = Grover(oracle,
                                    init_state=a_operator,
                                    num_iterations=rotation_count)
                    circuit = grover.construct_circuit(
                        measurement=self.quantum_instance.is_statevector)
                else:
                    circuit = a_operator._circuit

                # 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
                v = self._twos_complement(int_v, n_value)
                logger.info('Outcome: %s', outcome)
                logger.info('Value: %s = %s', v, 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)
                    if v.startswith('1'):
                        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)

        # Get original key and value pairs.
        func_dict[-1] = int(orig_constant)
        solutions = self._get_qubo_solutions(func_dict, n_key)

        # 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 = [
            1 if s == '1' else 0
            for s in ('{0:%sb}' % n_key).format(optimum_key)
        ]

        # Build the results object.
        grover_results = GroverOptimizationResults(operation_count, n_key,
                                                   n_value, func_dict)
        fval = solutions[optimum_key]
        if sense == problem_.objective.Sense.MAXIMIZE:
            fval = -fval
        result = OptimizationResult(x=opt_x,
                                    fval=fval,
                                    results={
                                        "grover_results": grover_results,
                                        "qubo_converter": qubo_converter
                                    })

        # cast binaries back to integers
        result = qubo_converter.decode(result)

        return result
예제 #3
0
    def solve(self, problem: OptimizationProblem) -> OptimizationResult:
        """Tries to solves the given problem using the optimizer.

        Runs the optimizer to try to solve the optimization problem. If problem is not convex,
        this optimizer may raise an exception due to incompatibility, depending on the settings.

        Args:
            problem: The problem to be solved.

        Returns:
            The result of the optimizer applied to the problem.

        Raises:
            QiskitOptimizationError: If the problem is incompatible with the optimizer.
        """

        # convert problem to QUBO
        qubo_converter = OptimizationProblemToQubo()
        problem_ = qubo_converter.encode(problem)

        # TODO: How to get from Optimization Problem?
        num_output_qubits = 6

        # Variables for tracking the optimum.
        optimum_found = False
        optimum_key = math.inf
        optimum_value = math.inf
        threshold = 0
        n_key = problem_.variables.get_num()
        n_value = num_output_qubits

        # Variables for tracking the solutions encountered.
        num_solutions = 2**n_key
        keys_measured = []

        # Variables for result object.
        func_dict = {}
        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.
        orig_constant = problem_.objective.get_offset()
        measurement = not self._quantum_instance.is_statevector
        opt_prob_converter = OptimizationProblemToNegativeValueOracle(
            n_value, measurement)

        while not optimum_found:
            m = 1
            improvement_found = False

            # Get oracle O and the state preparation operator A for the current threshold.
            problem_.objective.set_offset(orig_constant - threshold)
            a_operator, oracle, func_dict = opt_prob_converter.encode(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(random.uniform(0, m - 1)))
                rotations += rotation_count

                # Apply Grover's Algorithm to find values below the threshold.
                if rotation_count > 0:
                    grover = Grover(oracle,
                                    init_state=a_operator,
                                    num_iterations=rotation_count)
                    circuit = grover.construct_circuit(
                        measurement=self._quantum_instance.is_statevector)

                else:
                    circuit = a_operator._circuit

                # Get the next outcome.
                outcome = self._measure(circuit, n_key, n_value)
                k = int(outcome[0:n_key], 2)
                v = outcome[n_key:n_key + n_value]

                # Convert the binary string to integer.
                int_v = self._bin_to_int(v, n_value) + threshold
                v = self._twos_complement(int_v, n_value)

                self._logger.info('Iterations: %s', rotation_count)
                self._logger.info('Outcome: %s', outcome)
                self._logger.info('Value: %s = %s', v, 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
                    self._logger.info('Current Optimum Key: %s', optimum_key)
                    self._logger.info('Current Optimum Value: %s',
                                      optimum_value)
                    if v.startswith('1'):
                        improvement_found = True
                        threshold = optimum_value
                else:
                    # No better number after the max number of iterations, so we assume the optimal.
                    if loops_with_no_improvement >= self._n_iterations:
                        improvement_found = True
                        optimum_found = True

                    # Using Durr and Hoyer method, increase m.
                    # TODO: Give option for a rotation schedule, or for different lambda's.
                    m = int(np.ceil(min(m * 8 / 7, 2**(n_key / 2))))
                    self._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)

                # Stop if we've seen all the keys or hit the rotation max.
                if 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
                self._logger.info('Operation Count: %s\n', operations)

        # Get original key and value pairs.
        func_dict[-1] = orig_constant
        solutions = get_qubo_solutions(func_dict, n_key)

        # 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 = [
            1 if s == '1' else 0
            for s in ('{0:%sb}' % n_key).format(optimum_key)
        ]

        # Build the results object.
        grover_results = GroverOptimizationResults(operation_count, rotations,
                                                   n_key, n_value, func_dict)
        result = OptimizationResult(x=opt_x,
                                    fval=solutions[optimum_key],
                                    results={
                                        "grover_results": grover_results,
                                        "qubo_converter": qubo_converter
                                    })

        # cast binaries back to integers
        result = qubo_converter.decode(result)

        return result