Пример #1
0
    def __init__(
        self,
        operator: Optional[Union[OperatorBase, LegacyBaseOperator]] = None,
        var_form: Optional[Union[QuantumCircuit, VariationalForm]] = None,
        optimizer: Optional[Optimizer] = None,
        initial_point: Optional[np.ndarray] = None,
        expectation: Optional[ExpectationBase] = None,
        include_custom: bool = False,
        max_evals_grouped: int = 1,
        aux_operators: Optional[List[Optional[Union[
            OperatorBase, LegacyBaseOperator]]]] = None,
        callback: Optional[Callable[[int, np.ndarray, float, float],
                                    None]] = None,
        quantum_instance: Optional[Union[QuantumInstance, BaseBackend]] = None,
        orbital_rotation: Optional['OrbitalRotation'] = None,
        core: Optional[LegacyBaseOperator] = None,
        qmolecule: Optional[QMolecule] = None,
        bounds: Optional[np.ndarray] = None,
        iterative_oo: bool = False,
        iterative_oo_iterations: int = 3,
    ) -> None:
        """

        Args:
            operator: Qubit operator of the Hamiltonian.
            var_form: A parameterized variational form (ansatz).
            optimizer: A classical optimizer.
            initial_point: An optional initial point (i.e. initial parameter values)
                for the optimizer. If ``None`` then VQE will look to the variational form for a
                preferred point and if not will simply compute a random one.
            expectation: The Expectation converter for taking the average value of the
                Observable over the var_form state function. When ``None`` (the default) an
                :class:`~qiskit.aqua.operators.expectations.ExpectationFactory` is used to select
                an appropriate expectation based on the operator and backend. When using Aer
                qasm_simulator backend, with paulis, it is however much faster to leverage custom
                Aer function for the computation but, although VQE performs much faster
                with it, the outcome is ideal, with no shot noise, like using a state vector
                simulator. If you are just looking for the quickest performance when choosing Aer
                qasm_simulator and the lack of shot noise is not an issue then set `include_custom`
                parameter here to ``True`` (defaults to ``False``).
            include_custom: When `expectation` parameter here is None setting this to ``True`` will
                allow the factory to include the custom Aer pauli expectation.
            max_evals_grouped: Max number of evaluations performed simultaneously. Signals the
                given optimizer that more than one set of parameters can be supplied so that
                potentially the expectation values can be computed in parallel. Typically this is
                possible when a finite difference gradient is used by the optimizer such that
                multiple points to compute the gradient can be passed and if computed in parallel
                improve overall execution time.
            aux_operators: Optional list of auxiliary operators to be evaluated with the eigenstate
                of the minimum eigenvalue main result and their expectation values returned.
                For instance in chemistry these can be dipole operators, total particle count
                operators so we can get values for these at the ground state.
            callback: a callback that can access the intermediate data during the optimization.
                Four parameter values are passed to the callback as follows during each evaluation
                by the optimizer for its current set of parameters as it works towards the minimum.
                These are: the evaluation count, the optimizer parameters for the
                variational form, the evaluated mean and the evaluated standard deviation.
            quantum_instance: :class:`~qiskit.aqua.QuantumInstance` that the algorithm is
                 executed on.
            orbital_rotation: instance of
                :class:`~qiskit.chemistry.algorithms.minimum_eigen_solvers.OrbitalRotation` class
                that creates the matrices that rotate the orbitals needed to produce the rotated
                MO coefficients C as C = C0 * exp(-kappa).
            core: instance of the :class:`~qiskit.chemistry.core.Hamiltonian` class to
                 make new qubit operator after the orbital rotation using the data stored in
                 qmolecule.
            qmolecule: instance of the :class:`~qiskit.chemistry.QMolecule` class which has methods
                needed to recompute one-/two-electron/dipole integrals after orbital rotation
                (C = C0 * exp(-kappa)).
            bounds: bounds for variational form and orbital rotation
                 parameters given to a classical optimizer.
            iterative_oo: when ``True`` optimize first the variational form and then the
                orbitals, iteratively. Otherwise, the wavefunction ansatz and orbitals are
                optimized simultaneously.
            iterative_oo_iterations: number of iterations in the iterative procedure,
                set larger to be sure to converge to the global minimum.

        Raises:
            AquaError: if the number of orbital optimization iterations is less or equal to zero.
        """

        super().__init__(
            operator=operator,
            var_form=var_form,
            optimizer=optimizer,
            initial_point=initial_point,
            expectation=expectation,
            include_custom=include_custom,
            max_evals_grouped=max_evals_grouped,
            aux_operators=aux_operators,
            callback=callback,
            quantum_instance=quantum_instance,
        )
        self._qmolecule_rotated = None
        self._fixed_wavefunction_params = None
        if aux_operators is None:
            self._aux_operators = []
        else:
            self._aux_operators = [aux_operators] if not isinstance(aux_operators, list) else \
                aux_operators
        logger.info(self.print_settings())
        self._core = core
        self._qmolecule = qmolecule
        if orbital_rotation is None:
            self._orbital_rotation = OrbitalRotation(
                num_qubits=self.var_form.num_qubits,
                core=self._core,
                qmolecule=self._qmolecule)
        self._num_parameters_oovqe = None
        if initial_point is None:
            self._set_initial_point()
        self._bounds = bounds
        if self._bounds is None:
            self._set_bounds(self._orbital_rotation.parameter_bound_value)
        self._iterative_oo = iterative_oo
        self._iterative_oo_iterations = iterative_oo_iterations
        if self._iterative_oo_iterations < 1:
            raise AquaError(
                'Please set iterative_oo_iterations parameter to a positive number,'
                ' got {} instead'.format(self._iterative_oo_iterations))

        # copies to overcome incompatibilities with error checks in VQAlgorithm class
        self.var_form_num_parameters = self.var_form.num_parameters
        self.var_form_bounds = copy.copy(self.var_form._bounds)
    def construct_circuit(self,
                          circuit=None,
                          variable_register=None,
                          output_register=None,
                          output_idx=None,
                          ancillary_register=None,
                          mct_mode='basic'):  # pylint: disable=arguments-differ
        """
        Construct circuit.

        Args:
            circuit (QuantumCircuit): The optional circuit to extend from
            variable_register (QuantumRegister): The optional quantum
            register to use for problem variables
            output_register (QuantumRegister): The optional quantum
            register to use for holding the output
            output_idx (int): The index of the output register to write to
            ancillary_register (QuantumRegister): The optional quantum register to use as ancilla
            mct_mode (str): The mode to use for building Multiple-Control Toffoli

        Returns:
            QuantumCircuit: quantum circuit.
        Raises:
            AquaError: invalid input
        """

        circuit = self._set_up_circuit(circuit=circuit,
                                       variable_register=variable_register,
                                       clause_register='skip',
                                       output_register=output_register,
                                       output_idx=output_idx,
                                       ancillary_register=ancillary_register,
                                       mct_mode=mct_mode)

        def build_clause(clause_expr):
            if clause_expr[0] == 'and':
                lits = [l[1] for l in clause_expr[1:]]
            elif clause_expr[0] == 'lit':
                lits = [clause_expr[1]]
            else:
                raise AquaError(
                    'Unexpected clause expression {}.'.format(clause_expr))
            flags = BooleanLogicNormalForm._lits_to_flags(lits)
            and_circuit = AND(num_variable_qubits=len(self._variable_register),
                              flags=flags,
                              mcx_mode=mct_mode)
            qubits = self._variable_register[:] + [
                self._output_register[self._output_idx]
            ]
            if self._ancillary_register:
                qubits += self._ancillary_register[:and_circuit.
                                                   num_ancilla_qubits]

            circuit.compose(and_circuit, qubits, inplace=True)

        # compute all clauses
        if self._depth == 0:
            self._construct_circuit_for_tiny_expr(circuit,
                                                  output_idx=output_idx)
        elif self._depth == 1:
            if self._ast[0] == 'xor':
                for cur_clause_expr in self._ast[1:]:
                    build_clause(cur_clause_expr)
            else:
                build_clause(self._ast)
        elif self._depth == 2:
            if not self._ast[0] == 'xor':
                raise AquaError('Unexpected root logical '
                                'operation {} for ESOP.'.format(self._ast[0]))
            for cur_clause_expr in self._ast[1:]:
                build_clause(cur_clause_expr)
        else:
            raise AquaError('Unexpected ESOP expression {}.'.format(self._ast))

        return circuit
Пример #3
0
 def get_optimal_cost(self):
     if 'opt_params' not in self._ret:
         raise AquaError("Cannot return optimal cost before running the "
                         "algorithm to find optimal params.")
     return self._ret['min_val']
Пример #4
0
    def __init__(
        self,
        data: np.ndarray,
        bounds: Optional[np.ndarray] = None,
        num_qubits: Optional[np.ndarray] = None,
        batch_size: int = 500,
        num_epochs: int = 3000,
        seed: int = 7,
        discriminator: Optional[DiscriminativeNetwork] = None,
        generator: Optional[GenerativeNetwork] = None,
        tol_rel_ent: Optional[float] = None,
        snapshot_dir: Optional[str] = None,
        quantum_instance: Optional[Union[QuantumInstance, BaseBackend]] = None
    ) -> None:
        """

        Args:
            data: Training data of dimension k
            bounds: k min/max data values [[min_0,max_0],...,[min_k-1,max_k-1]]
                if univariate data: [min_0,max_0]
            num_qubits: k numbers of qubits to determine representation resolution,
                i.e. n qubits enable the representation of 2**n values
                [num_qubits_0,..., num_qubits_k-1]
            batch_size: Batch size, has a min. value of 1.
            num_epochs: Number of training epochs
            seed: Random number seed
            discriminator: Discriminates between real and fake data samples
            generator: Generates 'fake' data samples
            tol_rel_ent: Set tolerance level for relative entropy.
                If the training achieves relative entropy equal or lower than tolerance it finishes.
            snapshot_dir: Directory in to which to store cvs file with parameters,
                if None (default) then no cvs file is created.
            quantum_instance: Quantum Instance or Backend
        Raises:
            AquaError: invalid input
        """
        validate_min('batch_size', batch_size, 1)
        super().__init__(quantum_instance)
        if data is None:
            raise AquaError('Training data not given.')
        self._data = np.array(data)
        if bounds is None:
            bounds_min = np.percentile(self._data, 5, axis=0)
            bounds_max = np.percentile(self._data, 95, axis=0)
            bounds = []
            for i, _ in enumerate(bounds_min):
                bounds.append([bounds_min[i], bounds_max[i]])
        if np.ndim(data) > 1:
            if len(bounds) != (len(num_qubits) or len(data[0])):
                raise AquaError(
                    'Dimensions of the data, the length of the data bounds '
                    'and the numbers of qubits per '
                    'dimension are incompatible.')
        else:
            if (np.ndim(bounds) or len(num_qubits)) != 1:
                raise AquaError(
                    'Dimensions of the data, the length of the data bounds '
                    'and the numbers of qubits per '
                    'dimension are incompatible.')
        self._bounds = np.array(bounds)
        self._num_qubits = num_qubits
        # pylint: disable=unsubscriptable-object
        if np.ndim(data) > 1:
            if self._num_qubits is None:
                self._num_qubits = np.ones[len(data[0])] * 3
        else:
            if self._num_qubits is None:
                self._num_qubits = np.array([3])
        self._data, self._data_grid, self._grid_elements, self._prob_data = \
            discretize_and_truncate(self._data, self._bounds, self._num_qubits,
                                    return_data_grid_elements=True,
                                    return_prob=True, prob_non_zero=True)
        self._batch_size = batch_size
        self._num_epochs = num_epochs
        self._snapshot_dir = snapshot_dir
        self._g_loss = []
        self._d_loss = []
        self._rel_entr = []
        self._tol_rel_ent = tol_rel_ent

        self._random_seed = seed

        if generator is None:
            self.set_generator()
        else:
            self._generator = generator
        if discriminator is None:
            self.set_discriminator()
        else:
            self._discriminator = discriminator

        self.seed = self._random_seed

        self._ret = {}
Пример #5
0
 def optimal_params(self):
     if 'opt_params' not in self._ret:
         raise AquaError(
             "Cannot find optimal params before running the algorithm.")
     return self._ret['opt_params']
Пример #6
0
    def __init__(self, circuit) -> None:

        if circuit is None:
            raise AquaError("No quantum circuit was passed.")
        else:
            self.circuit = circuit
Пример #7
0
    def __init__(
        self,
        optimizer: Optimizer,
        feature_map: Union[QuantumCircuit, FeatureMap],
        var_form: Union[QuantumCircuit, VariationalForm],
        training_dataset: Dict[str, np.ndarray],
        test_dataset: Optional[Dict[str, np.ndarray]] = None,
        datapoints: Optional[np.ndarray] = None,
        max_evals_grouped: int = 1,
        minibatch_size: int = -1,
        callback: Optional[Callable[[int, np.ndarray, float, int],
                                    None]] = None,
        use_sigmoid_cross_entropy: bool = False,
        quantum_instance: Optional[Union[QuantumInstance, BaseBackend,
                                         Backend]] = None
    ) -> None:
        """
        Args:
            optimizer: The classical optimizer to use.
            feature_map: The FeatureMap instance to use.
            var_form: The variational form instance.
            training_dataset: The training dataset, in the format
                {'A': np.ndarray, 'B': np.ndarray, ...}.
            test_dataset: The test dataset, in same format as `training_dataset`.
            datapoints: NxD array, N is the number of data and D is data dimension.
            max_evals_grouped: The maximum number of evaluations to perform simultaneously.
            minibatch_size: The size of a mini-batch.
            callback: a callback that can access the intermediate data during the optimization.
                Four parameter values are passed to the callback as follows during each evaluation.
                These are: the evaluation count, parameters of the variational form,
                the evaluated value, the index of data batch.
            use_sigmoid_cross_entropy: whether to use sigmoid cross entropy or not.
            quantum_instance: Quantum Instance or Backend

        Note:
            We use `label` to denote numeric results and `class` the class names (str).

        Raises:
            AquaError: Missing feature map or missing training dataset.
        """
        warn_package('aqua.algorithms.classifiers',
                     'qiskit_machine_learning.algorithms.classifiers',
                     'qiskit-machine-learning')
        # VariationalForm is not deprecated on level of the VQAlgorithm yet as UCCSD still
        # derives from there, therefore we're adding a warning here
        if isinstance(var_form, VariationalForm):
            warnings.warn("""
            The {} object as input for the VQC is deprecated as of 0.7.0 and will
            be removed no earlier than 3 months after the release.
            You should pass a QuantumCircuit object instead.
            See also qiskit.circuit.library.n_local for a collection
            of suitable circuits.""".format(type(feature_map)),
                          DeprecationWarning,
                          stacklevel=2)

        super().__init__(var_form=var_form,
                         optimizer=optimizer,
                         cost_fn=self._loss,
                         quantum_instance=quantum_instance)
        self._batches = None
        self._label_batches = None
        self._batch_index = None
        self._eval_time = None
        self.batch_num = None
        self._optimizer.set_max_evals_grouped(max_evals_grouped)

        self._callback = callback

        if use_sigmoid_cross_entropy:
            self.cost_function = cost_estimate_sigmoid
        else:
            self.cost_function = cost_estimate

        if feature_map is None:
            raise AquaError('Missing feature map.')
        if training_dataset is None:
            raise AquaError('Missing training dataset.')
        self._training_dataset, self._class_to_label = split_dataset_to_data_and_labels(
            training_dataset)
        self._label_to_class = {
            label: class_name
            for class_name, label in self._class_to_label.items()
        }
        self._num_classes = len(list(self._class_to_label.keys()))

        if test_dataset is not None:
            self._test_dataset = split_dataset_to_data_and_labels(
                test_dataset, self._class_to_label)
        else:
            self._test_dataset = test_dataset

        if datapoints is not None and not isinstance(datapoints, np.ndarray):
            datapoints = np.asarray(datapoints)
            if len(datapoints) == 0:  # pylint: disable=len-as-condition
                datapoints = None
        self._datapoints = datapoints
        self._minibatch_size = minibatch_size

        self._eval_count = 0
        self._ret = {}  # type: Dict[str, Any]
        self._parameterized_circuits = None

        self.feature_map = feature_map
Пример #8
0
 def get_optimal_cost(self) -> float:
     """Get the minimal cost or energy found by the VQE."""
     if 'opt_params' not in self._ret:
         raise AquaError("Cannot return optimal cost before running the "
                         "algorithm to find optimal params.")
     return self._ret['min_val']
Пример #9
0
 def optimal_params(self) -> List[float]:
     """The optimal parameters for the variational form."""
     if 'opt_params' not in self._ret:
         raise AquaError("Cannot find optimal params before running the algorithm.")
     return self._ret['opt_params']
Пример #10
0
    def _run(self):
        """
        Run the algorithm to compute up to the requested k number of eigenvalues.
        Returns:
            dict: Dictionary of results
        Raises:
            AquaError: if no operator has been provided
        """
        if self._operator is None:
            raise AquaError("Operator was never provided")

        k_orig = self._k
        if self._filter_criterion:
            # need to consider all elements if a filter is set
            self._k = 2**(self._operator.num_qubits)

        self._ret = {}
        self._solve()

        # compute energies before filtering, as this also evaluates the aux operators
        self._get_energies()

        # if a filter is set, loop over the given values and only keep
        if self._filter_criterion:

            eigvecs = []
            eigvals = []
            energies = []
            aux_ops = []
            cnt = 0
            for i in range(len(self._ret['eigvals'])):
                eigvec = self._ret['eigvecs'][i]
                eigval = self._ret['eigvals'][i]
                energy = self._ret['energies'][i]
                if 'aux_ops' in self._ret:
                    aux_op = self._ret['aux_ops'][i]
                else:
                    aux_op = None
                if self._filter_criterion(eigvec, eigval, aux_op):
                    cnt += 1
                    eigvecs += [eigvec]
                    eigvals += [eigval]
                    energies += [energy]
                    if 'aux_ops' in self._ret:
                        aux_ops += [aux_op]
                if cnt == k_orig:
                    break

            self._ret['eigvecs'] = np.array(eigvecs)
            self._ret['eigvals'] = np.array(eigvals)
            self._ret['energies'] = np.array(energies)

            self._k = k_orig

        # evaluate ground state after filtering (in case a filter is set)
        self._get_ground_state_energy()

        logger.debug('NumPyEigensolver _run result:\n%s',
                     pprint.pformat(self._ret, indent=4))
        result = EigensolverResult()
        if 'eigvals' in self._ret:
            result.eigenvalues = self._ret['eigvals']
        if 'eigvecs' in self._ret:
            result.eigenstates = ListOp([StateFn(vec) for vec in self._ret['eigvecs']])
        if 'aux_ops' in self._ret:
            result.aux_operator_eigenvalues = self._ret['aux_ops']

        logger.debug('EigensolverResult dict:\n%s',
                     pprint.pformat(result.data, indent=4))
        return result
Пример #11
0
    def _run(self) -> 'VQEResult':
        """Run the algorithm to compute the minimum eigenvalue.

        Returns:
            The result of the VQE algorithm as ``VQEResult``.

        Raises:
            AquaError: Wrong setting of operator and backend.
        """
        if self.operator is None:
            raise AquaError("The operator was never provided.")

        self._check_operator_varform()

        self._quantum_instance.circuit_summary = True

        self._eval_count = 0

        # Convert the gradient operator into a callable function that is compatible with the
        # optimization routine.
        if self._gradient:
            if isinstance(self._gradient, GradientBase):
                self._gradient = self._gradient.gradient_wrapper(
                    ~StateFn(self._operator) @ StateFn(self._var_form),
                    bind_params=self._var_form_params,
                    backend=self._quantum_instance)
        vqresult = self.find_minimum(initial_point=self.initial_point,
                                     var_form=self.var_form,
                                     cost_fn=self._energy_evaluation,
                                     gradient_fn=self._gradient,
                                     optimizer=self.optimizer)

        # TODO remove all former dictionary logic
        self._ret = {}
        self._ret['num_optimizer_evals'] = vqresult.optimizer_evals
        self._ret['min_val'] = vqresult.optimal_value
        self._ret['opt_params'] = vqresult.optimal_point
        self._ret['eval_time'] = vqresult.optimizer_time
        self._ret['opt_params_dict'] = vqresult.optimal_parameters

        if self._ret['num_optimizer_evals'] is not None and \
                self._eval_count >= self._ret['num_optimizer_evals']:
            self._eval_count = self._ret['num_optimizer_evals']
        self._eval_time = self._ret['eval_time']
        logger.info('Optimization complete in %s seconds.\nFound opt_params %s in %s evals',
                    self._eval_time, self._ret['opt_params'], self._eval_count)
        self._ret['eval_count'] = self._eval_count

        result = VQEResult()
        result.combine(vqresult)
        result.eigenvalue = vqresult.optimal_value + 0j
        result.eigenstate = self.get_optimal_vector()

        self._ret['energy'] = self.get_optimal_cost()
        self._ret['eigvals'] = np.asarray([self._ret['energy']])
        self._ret['eigvecs'] = np.asarray([result.eigenstate])

        if len(self.aux_operators) > 0:
            self._eval_aux_ops()
            # TODO remove when ._ret is deprecated
            result.aux_operator_eigenvalues = self._ret['aux_ops'][0]

        result.cost_function_evals = self._eval_count

        return result
Пример #12
0
def to_weighted_pauli_operator(operator):
    """
    Converting a given operator to `WeightedPauliOperator`

    Args:
        operator (WeightedPauliOperator | TPBGroupedWeightedPauliOperator | MatrixOperator):
            one of supported operator type
    Returns:
        WeightedPauliOperator: the converted weighted pauli operator
    Raises:
        AquaError: Unsupported type to convert

    Warnings:
        Converting time from a MatrixOperator to a Pauli-type Operator grows exponentially.
        If you are converting a system with large number of qubits, it will take time.
        You can turn on DEBUG logging to check the progress.
    """
    if operator.__class__ == WeightedPauliOperator:
        return operator
    elif operator.__class__ == TPBGroupedWeightedPauliOperator:
        # destroy the grouping but keep z2 symmetries info
        return WeightedPauliOperator(paulis=operator.paulis,
                                     z2_symmetries=operator.z2_symmetries,
                                     name=operator.name)
    elif operator.__class__ == MatrixOperator:
        if operator.is_empty():
            return WeightedPauliOperator(paulis=[])
        if operator.num_qubits > 10:
            logger.warning(
                "Converting time from a MatrixOperator to a Pauli-type Operator grows "
                "exponentially. If you are converting a system with large number of "
                "qubits, it will take time. And now you are converting a %s-qubit "
                "Hamiltonian. You can turn on DEBUG logging to check the progress."
                "", operator.num_qubits)
        num_qubits = operator.num_qubits
        coeff = 2**(-num_qubits)

        paulis = []
        possible_basis = 'IXYZ'
        if operator.dia_matrix is not None:
            possible_basis = 'IZ'

        if logger.isEnabledFor(logging.DEBUG):
            logger.debug(
                "Converting a MatrixOperator to a Pauli-type Operator:")
            TextProgressBar(sys.stderr)
        results = parallel_map(_conversion,
                               list(
                                   itertools.product(possible_basis,
                                                     repeat=num_qubits)),
                               task_kwargs={"matrix": operator._matrix},
                               num_processes=aqua_globals.num_processes)
        for trace_value, pauli in results:
            weight = trace_value * coeff
            if weight != 0.0 and np.abs(weight) > operator.atol:
                paulis.append([weight, pauli])

        return WeightedPauliOperator(paulis,
                                     z2_symmetries=operator.z2_symmetries,
                                     name=operator.name)
    else:
        raise AquaError(
            "Unsupported type to convert to WeightedPauliOperator: "
            "{}".format(operator.__class__))
def _mct_v_chain(qc,
                 control_qubits,
                 target_qubit,
                 ancillary_qubits,
                 dirty_ancilla=False):
    """
    Create new MCT circuit by chaining Toffoli gates into a V shape.

    The dirty_ancilla mode is from https://arxiv.org/abs/quant-ph/9503016 Lemma 7.2

    All intermediate Toffoli gates are implemented up to a relative phase,
    see https://arxiv.org/abs/1508.03273

    An additional saving of 4 CNOTs is achieved
    by using the Toffoli implementation from Section IV.B of https://arxiv.org/abs/1508.03273
    """

    if len(ancillary_qubits) < len(control_qubits) - 2:
        raise AquaError('Insufficient number of ancillary qubits.')

    if dirty_ancilla:
        anci_idx = len(control_qubits) - 3

        qc.u2(0, pi, target_qubit)
        qc.cx(target_qubit, ancillary_qubits[anci_idx])
        qc.u1(-pi / 4, ancillary_qubits[anci_idx])
        qc.cx(control_qubits[len(control_qubits) - 1],
              ancillary_qubits[anci_idx])
        qc.u1(pi / 4, ancillary_qubits[anci_idx])
        qc.cx(target_qubit, ancillary_qubits[anci_idx])
        qc.u1(-pi / 4, ancillary_qubits[anci_idx])
        qc.cx(control_qubits[len(control_qubits) - 1],
              ancillary_qubits[anci_idx])
        qc.u1(pi / 4, ancillary_qubits[anci_idx])

        for idx in reversed(range(2, len(control_qubits) - 1)):
            qc.rccx(control_qubits[idx], ancillary_qubits[anci_idx - 1],
                    ancillary_qubits[anci_idx])
            anci_idx -= 1

    anci_idx = 0
    qc.rccx(control_qubits[0], control_qubits[1], ancillary_qubits[anci_idx])
    for idx in range(2, len(control_qubits) - 1):
        qc.rccx(control_qubits[idx], ancillary_qubits[anci_idx],
                ancillary_qubits[anci_idx + 1])
        anci_idx += 1

    if dirty_ancilla:
        qc.u1(-pi / 4, ancillary_qubits[anci_idx])
        qc.cx(control_qubits[len(control_qubits) - 1],
              ancillary_qubits[anci_idx])
        qc.u1(pi / 4, ancillary_qubits[anci_idx])
        qc.cx(target_qubit, ancillary_qubits[anci_idx])
        qc.u1(-pi / 4, ancillary_qubits[anci_idx])
        qc.cx(control_qubits[len(control_qubits) - 1],
              ancillary_qubits[anci_idx])
        qc.u1(pi / 4, ancillary_qubits[anci_idx])
        qc.cx(target_qubit, ancillary_qubits[anci_idx])
        qc.u2(0, pi, target_qubit)
    else:
        qc.ccx(control_qubits[len(control_qubits) - 1],
               ancillary_qubits[anci_idx], target_qubit)

    for idx in reversed(range(2, len(control_qubits) - 1)):
        qc.rccx(control_qubits[idx], ancillary_qubits[anci_idx - 1],
                ancillary_qubits[anci_idx])
        anci_idx -= 1
    qc.rccx(control_qubits[0], control_qubits[1], ancillary_qubits[anci_idx])

    if dirty_ancilla:
        anci_idx = 0
        for idx in range(2, len(control_qubits) - 1):
            qc.rccx(control_qubits[idx], ancillary_qubits[anci_idx],
                    ancillary_qubits[anci_idx + 1])
            anci_idx += 1
def mct(self, q_controls, q_target, q_ancilla, mode='basic'):
    """
    Apply Multiple-Control Toffoli operation

    Args:
        self (QuantumCircuit): The QuantumCircuit object to apply the mct gate on.
        q_controls (Union(QuantumRegister, list[Qubit])): The list of control qubits
        q_target (Qubit): The target qubit
        q_ancilla (Union(QuantumRegister, list[Qubit])): The list of ancillary qubits
        mode (str): The implementation mode to use
    Raises:
        AquaError: invalid input
    """

    if len(q_controls) == 1:  # cx
        self.cx(q_controls[0], q_target)
    elif len(q_controls) == 2:  # ccx
        self.ccx(q_controls[0], q_controls[1], q_target)
    else:
        # check controls
        if isinstance(q_controls, QuantumRegister):
            control_qubits = [qb for qb in q_controls]
        elif isinstance(q_controls, list):
            control_qubits = q_controls
        else:
            raise AquaError(
                'MCT needs a list of qubits or a quantum register for controls.'
            )

        # check target
        if isinstance(q_target, Qubit):
            target_qubit = q_target
        else:
            raise AquaError('MCT needs a single qubit as target.')

        # check ancilla
        if q_ancilla is None:
            ancillary_qubits = []
        elif isinstance(q_ancilla, QuantumRegister):
            ancillary_qubits = [qb for qb in q_ancilla]
        elif isinstance(q_ancilla, list):
            ancillary_qubits = q_ancilla
        else:
            raise AquaError(
                'MCT needs None or a list of qubits or a quantum register for ancilla.'
            )

        all_qubits = control_qubits + [target_qubit] + ancillary_qubits

        self._check_qargs(all_qubits)
        self._check_dups(all_qubits)

        if mode == 'basic':
            _mct_v_chain(self,
                         control_qubits,
                         target_qubit,
                         ancillary_qubits,
                         dirty_ancilla=False)
        elif mode == 'basic-dirty-ancilla':
            _mct_v_chain(self,
                         control_qubits,
                         target_qubit,
                         ancillary_qubits,
                         dirty_ancilla=True)
        elif mode == 'advanced':
            _multicx(self, [*control_qubits, target_qubit],
                     ancillary_qubits[0] if ancillary_qubits else None)
        elif mode == 'noancilla':
            _multicx_noancilla(self, [*control_qubits, target_qubit])
        else:
            raise AquaError(
                'Unrecognized mode for building MCT circuit: {}.'.format(mode))
Пример #15
0
    def get_gradient(
        self, operator: OperatorBase, params: Union[ParameterExpression,
                                                    ParameterVector,
                                                    List[ParameterExpression]]
    ) -> OperatorBase:
        """Get the gradient for the given operator w.r.t. the given parameters

        Args:
            operator: Operator w.r.t. which we take the gradient.
            params: Parameters w.r.t. which we compute the gradient.

        Returns:
            Operator which represents the gradient w.r.t. the given params.

        Raises:
            ValueError: If ``params`` contains a parameter not present in ``operator``.
            AquaError: If the coefficient of the operator could not be reduced to 1.
            AquaError: If the differentiation of a combo_fn requires JAX but the package is not
                       installed.
            TypeError: If the operator does not include a StateFn given by a quantum circuit
            Exception: Unintended code is reached
        """
        def is_coeff_c(coeff, c):
            if isinstance(coeff, ParameterExpression):
                expr = coeff._symbol_expr
                return expr == c
            return coeff == c

        if isinstance(params, (ParameterVector, list)):
            param_grads = [
                self.get_gradient(operator, param) for param in params
            ]
            # If get_gradient returns None, then the corresponding parameter was probably not
            # present in the operator. This needs to be looked at more carefully as other things can
            # probably trigger a return of None.
            absent_params = [
                params[i] for i, grad_ops in enumerate(param_grads)
                if grad_ops is None
            ]
            if len(absent_params) > 0:
                raise ValueError(
                    'The following parameters do not appear in the provided operator: ',
                    absent_params)
            return ListOp(param_grads)

        # By now params is a single parameter
        param = params
        # Handle Product Rules
        if not is_coeff_c(operator._coeff, 1.0):
            # Separate the operator from the coefficient
            coeff = operator._coeff
            op = operator / coeff
            # Get derivative of the operator (recursively)
            d_op = self.get_gradient(op, param)
            # ..get derivative of the coeff
            d_coeff = self.parameter_expression_grad(coeff, param)

            grad_op = 0
            if d_op != ~Zero @ One and not is_coeff_c(coeff, 0.0):
                grad_op += coeff * d_op
            if op != ~Zero @ One and not is_coeff_c(d_coeff, 0.0):
                grad_op += d_coeff * op
            if grad_op == 0:
                grad_op = ~Zero @ One
            return grad_op

        # Base Case, you've hit a ComposedOp!
        # Prior to execution, the composite operator was standardized and coefficients were
        # collected. Any operator measurements were converted to Pauli-Z measurements and rotation
        # circuits were applied. Additionally, all coefficients within ComposedOps were collected
        # and moved out front.
        if isinstance(operator, ComposedOp):

            # Gradient of an expectation value
            if not is_coeff_c(operator._coeff, 1.0):
                raise AquaError(
                    'Operator pre-processing failed. Coefficients were not properly '
                    'collected inside the ComposedOp.')

            # Do some checks to make sure operator is sensible
            # TODO add compatibility with sum of circuit state fns
            if not isinstance(operator[-1], CircuitStateFn):
                raise TypeError(
                    'The gradient framework is compatible with states that are given as '
                    'CircuitStateFn')

            return self.grad_method.convert(operator, param)

        elif isinstance(operator, CircuitStateFn):
            # Gradient of an a state's sampling probabilities
            if not is_coeff_c(operator._coeff, 1.0):
                raise AquaError(
                    'Operator pre-processing failed. Coefficients were not properly '
                    'collected inside the ComposedOp.')
            return self.grad_method.convert(operator, param)

        # Handle the chain rule
        elif isinstance(operator, ListOp):
            grad_ops = [self.get_gradient(op, param) for op in operator.oplist]

            # Note: this check to see if the ListOp has a default combo_fn
            # will fail if the user manually specifies the default combo_fn.
            # I.e operator = ListOp([...], combo_fn=lambda x:x) will not pass this check and
            # later on jax will try to differentiate it and raise an error.
            # An alternative is to check the byte code of the operator's combo_fn against the
            # default one.
            if operator._combo_fn == ListOp([])._combo_fn:
                return ListOp(oplist=grad_ops)
            elif isinstance(operator, SummedOp):
                return SummedOp(
                    oplist=[grad for grad in grad_ops
                            if grad != ~Zero @ One]).reduce()
            elif isinstance(operator, TensoredOp):
                return TensoredOp(oplist=grad_ops)

            if operator.grad_combo_fn:
                grad_combo_fn = operator.grad_combo_fn
            else:
                if _HAS_JAX:
                    grad_combo_fn = jit(
                        grad(operator._combo_fn, holomorphic=True))
                else:
                    raise AquaError(
                        'This automatic differentiation function is based on JAX. Please install '
                        'jax and use `import jax.numpy as jnp` instead of `import numpy as np` when'
                        'defining a combo_fn.')

            def chain_rule_combo_fn(x):
                result = np.dot(x[1], x[0])
                if isinstance(result, np.ndarray):
                    result = list(result)
                return result

            return ListOp([
                ListOp(operator.oplist, combo_fn=grad_combo_fn),
                ListOp(grad_ops)
            ],
                          combo_fn=chain_rule_combo_fn)
Пример #16
0
    def train(self):
        """
        Train the qGAN

        Raises:
            AquaError: Batch size bigger than the number of items in the truncated data set
        """
        if self._snapshot_dir is not None:
            with open(os.path.join(self._snapshot_dir, 'output.csv'),
                      mode='w') as csv_file:
                fieldnames = [
                    'epoch', 'loss_discriminator', 'loss_generator',
                    'params_generator', 'rel_entropy'
                ]
                writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
                writer.writeheader()

        if len(self._data) < self._batch_size:
            raise AquaError('The batch size needs to be less than the '
                            'truncated data size of {}'.format(len(
                                self._data)))

        for e in range(self._num_epochs):
            aqua_globals.random.shuffle(self._data)
            index = 0
            while (index + self._batch_size) <= len(self._data):
                real_batch = self._data[index:index + self._batch_size]
                index += self._batch_size
                generated_batch, generated_prob = self._generator.get_output(
                    self._quantum_instance, shots=self._batch_size)

                # 1. Train Discriminator
                ret_d = self._discriminator.train(
                    [real_batch, generated_batch], [
                        np.ones(len(real_batch)) / len(real_batch),
                        generated_prob
                    ])
                d_loss_min = ret_d['loss']

                # 2. Train Generator
                self._generator.set_discriminator(self._discriminator)
                ret_g = self._generator.train(self._quantum_instance,
                                              shots=self._batch_size)
                g_loss_min = ret_g['loss']

            self._d_loss.append(np.around(float(d_loss_min), 4))
            self._g_loss.append(np.around(g_loss_min, 4))

            rel_entr = self.get_rel_entr()
            self._rel_entr.append(np.around(rel_entr, 4))
            self._ret['params_d'] = ret_d['params']
            self._ret['params_g'] = ret_g['params']
            self._ret['loss_d'] = np.around(float(d_loss_min), 4)
            self._ret['loss_g'] = np.around(g_loss_min, 4)
            self._ret['rel_entr'] = np.around(rel_entr, 4)

            if self._snapshot_dir is not None:
                self._store_params(e, np.around(d_loss_min, 4),
                                   np.around(g_loss_min, 4),
                                   np.around(rel_entr, 4))
            logger.debug('Epoch %s/%s...', e + 1, self._num_epochs)
            logger.debug('Loss Discriminator: %s',
                         np.around(float(d_loss_min), 4))
            logger.debug('Loss Generator: %s', np.around(g_loss_min, 4))
            logger.debug('Relative Entropy: %s', np.around(rel_entr, 4))

            if self._tol_rel_ent is not None:
                if rel_entr <= self._tol_rel_ent:
                    break
Пример #17
0
    def solve(self,
              driver: BaseDriver,
              aux_operators: Optional[Union[List[FermionicOperator],
                                            List[BosonicOperator]]] = None) \
            -> Union[ElectronicStructureResult, VibronicStructureResult]:
        """Computes the ground state.

        Args:
            driver: a chemistry driver.
            aux_operators: Additional auxiliary ``FermionicOperator`` instances to evaluate at the
                ground state.

        Raises:
            AquaError: if a solver other than VQE or a variational form other than UCCSD is
                provided or if the algorithm finishes due to an unforeseen reason.

        Returns:
            An AdaptVQEResult which is an ElectronicStructureResult but also includes runtime
            information about the AdaptVQE algorithm like the number of iterations, finishing
            criterion, and the final maximum gradient.
        """
        operator, aux_operators = self._transformation.transform(
            driver, aux_operators)

        vqe = self._solver.get_solver(self._transformation)
        vqe.operator = operator
        if not isinstance(vqe, VQE):
            raise AquaError(
                "The AdaptVQE algorithm requires the use of the VQE solver")
        if not isinstance(vqe.var_form, UCCSD):
            raise AquaError(
                "The AdaptVQE algorithm requires the use of the UCCSD variational form"
            )

        vqe.var_form.manage_hopping_operators()
        excitation_pool = vqe.var_form.excitation_pool

        threshold_satisfied = False
        alternating_sequence = False
        max_iterations_exceeded = False
        prev_op_indices: List[int] = []
        theta: List[float] = []
        max_grad: Tuple[float, Optional[WeightedPauliOperator]] = (0., None)
        iteration = 0
        while self._max_iterations is None or iteration < self._max_iterations:
            iteration += 1
            logger.info('--- Iteration #%s ---', str(iteration))
            # compute gradients

            cur_grads = self._compute_gradients(excitation_pool, theta, vqe)
            # pick maximum gradient
            max_grad_index, max_grad = max(enumerate(cur_grads),
                                           key=lambda item: np.abs(item[1][0]))
            # store maximum gradient's index for cycle detection
            prev_op_indices.append(max_grad_index)
            # log gradients
            if logger.isEnabledFor(logging.INFO):
                gradlog = "\nGradients in iteration #{}".format(str(iteration))
                gradlog += "\nID: Excitation Operator: Gradient  <(*) maximum>"
                for i, grad in enumerate(cur_grads):
                    gradlog += '\n{}: {}: {}'.format(str(i), str(grad[1]),
                                                     str(grad[0]))
                    if grad[1] == max_grad[1]:
                        gradlog += '\t(*)'
                logger.info(gradlog)
            if np.abs(max_grad[0]) < self._threshold:
                logger.info(
                    "Adaptive VQE terminated successfully "
                    "with a final maximum gradient: %s",
                    str(np.abs(max_grad[0])))
                threshold_satisfied = True
                break
            # check indices of picked gradients for cycles
            if self._check_cyclicity(prev_op_indices):
                logger.info("Alternating sequence found. Finishing.")
                logger.info("Final maximum gradient: %s",
                            str(np.abs(max_grad[0])))
                alternating_sequence = True
                break
            # add new excitation to self._var_form
            vqe.var_form.push_hopping_operator(max_grad[1])
            theta.append(0.0)
            # run VQE on current Ansatz
            vqe.initial_point = theta
            raw_vqe_result = vqe.compute_minimum_eigenvalue(operator)
            theta = raw_vqe_result.optimal_point.tolist()
        else:
            # reached maximum number of iterations
            max_iterations_exceeded = True
            logger.info("Maximum number of iterations reached. Finishing.")
            logger.info("Final maximum gradient: %s", str(np.abs(max_grad[0])))

        # once finished evaluate auxiliary operators if any
        if aux_operators is not None:
            aux_values = self.evaluate_operators(raw_vqe_result.eigenstate,
                                                 aux_operators)
        else:
            aux_values = None
        raw_vqe_result.aux_operator_eigenvalues = aux_values

        if threshold_satisfied:
            finishing_criterion = 'Threshold converged'
        elif alternating_sequence:
            finishing_criterion = 'Aborted due to cyclicity'
        elif max_iterations_exceeded:
            finishing_criterion = 'Maximum number of iterations reached'
        else:
            raise AquaError(
                'The algorithm finished due to an unforeseen reason!')

        electronic_result = self.transformation.interpret(raw_vqe_result)

        result = AdaptVQEResult(electronic_result.data)
        result.num_iterations = iteration
        result.final_max_gradient = max_grad[0]
        result.finishing_criterion = finishing_criterion

        logger.info('The final energy is: %s',
                    str(result.computed_energies[0]))
        return result
Пример #18
0
def mcry(self,
         theta,
         q_controls,
         q_target,
         q_ancillae,
         mode='basic',
         use_basis_gates=False):
    """
    Apply Multiple-Controlled Y rotation gate

    Args:
        self (QuantumCircuit): The QuantumCircuit object to apply the mcry gate on.
        theta (float): angle theta
        q_controls (list[Qubit]): The list of control qubits
        q_target (Qubit): The target qubit
        q_ancillae (Union(QuantumRegister,tuple(QuantumRegister, int))):
                The list of ancillary qubits.
        mode (str): The implementation mode to use
        use_basis_gates (bool): use basis gates
    Raises:
        AquaError: invalid input
    """

    # check controls
    if isinstance(q_controls, QuantumRegister):
        control_qubits = [qb for qb in q_controls]
    elif isinstance(q_controls, list):
        control_qubits = q_controls
    else:
        raise AquaError(
            'The mcry gate needs a list of qubits or a quantum register for controls.'
        )

    # check target
    if isinstance(q_target, Qubit):
        target_qubit = q_target
    else:
        raise AquaError('The mcry gate needs a single qubit as target.')

    # check ancilla
    if q_ancillae is None:
        ancillary_qubits = []
    elif isinstance(q_ancillae, QuantumRegister):
        ancillary_qubits = [qb for qb in q_ancillae]
    elif isinstance(q_ancillae, list):
        ancillary_qubits = q_ancillae
    else:
        raise AquaError('The mcry gate needs None or a list '
                        'of qubits or a quantum register for ancilla.')

    all_qubits = control_qubits + [target_qubit] + ancillary_qubits

    self._check_qargs(all_qubits)
    self._check_dups(all_qubits)

    if mode == 'basic':
        self.u3(theta / 2, 0, 0, q_target)
        self.mct(q_controls, q_target, q_ancillae)
        self.u3(-theta / 2, 0, 0, q_target)
        self.mct(q_controls, q_target, q_ancillae)
    elif mode == 'noancilla':
        n_c = len(control_qubits)
        if n_c == 1:  # cu3
            apply_cu3(self,
                      theta,
                      0,
                      0,
                      control_qubits[0],
                      target_qubit,
                      use_basis_gates=use_basis_gates)
        else:
            theta_step = theta * (1 / (2**(n_c - 1)))
            _apply_mcu3_graycode(self,
                                 theta_step,
                                 0,
                                 0,
                                 control_qubits,
                                 target_qubit,
                                 use_basis_gates=use_basis_gates)
    else:
        raise AquaError(
            'Unrecognized mode for building MCRY circuit: {}.'.format(mode))
Пример #19
0
    def __init__(self,
                 bounds: np.ndarray,
                 num_qubits: List[int],
                 generator_circuit: Optional[
                     Union[UnivariateVariationalDistribution,
                           MultivariateVariationalDistribution,
                           QuantumCircuit]] = None,
                 init_params: Optional[Union[List[float], np.ndarray]] = None,
                 snapshot_dir: Optional[str] = None) -> None:
        """
        Args:
            bounds: k min/max data values [[min_1,max_1],...,[min_k,max_k]],
                given input data dim k
            num_qubits: k numbers of qubits to determine representation resolution,
                i.e. n qubits enable the representation of 2**n values [n_1,..., n_k]
            generator_circuit: a UnivariateVariationalDistribution for univariate data,
                a MultivariateVariationalDistribution for multivariate data,
                or a QuantumCircuit implementing the generator.
            init_params: 1D numpy array or list, Initialization for
                the generator's parameters.
            snapshot_dir: str or None, if not None save the optimizer's parameter after every
                update step to the given directory

        Raises:
            AquaError: Set multivariate variational distribution to represent multivariate data
        """
        super().__init__()
        self._bounds = bounds
        self._num_qubits = num_qubits
        self.generator_circuit = generator_circuit
        if self.generator_circuit is None:
            entangler_map = []
            if np.sum(num_qubits) > 2:
                for i in range(int(np.sum(num_qubits))):
                    entangler_map.append(
                        [i, int(np.mod(i + 1, np.sum(num_qubits)))])
            else:
                if np.sum(num_qubits) > 1:
                    entangler_map.append([0, 1])

            if len(num_qubits) > 1:
                num_qubits = list(map(int, num_qubits))
                low = bounds[:, 0].tolist()
                high = bounds[:, 1].tolist()
                init_dist = MultivariateUniformDistribution(num_qubits,
                                                            low=low,
                                                            high=high)
                q = QuantumRegister(sum(num_qubits))
                qc = QuantumCircuit(q)
                init_dist.build(qc, q)
                init_distribution = Custom(num_qubits=sum(num_qubits),
                                           circuit=qc)
                # Set variational form
                var_form = TwoLocal(sum(num_qubits),
                                    'ry',
                                    'cz',
                                    reps=1,
                                    initial_state=init_distribution,
                                    entanglement=entangler_map)
                if init_params is None:
                    init_params = aqua_globals.random.random(
                        var_form.num_parameters) * 2 * 1e-2
                # Set generator circuit
                self.generator_circuit = MultivariateVariationalDistribution(
                    num_qubits, var_form, init_params, low=low, high=high)
            else:
                init_dist = UniformDistribution(sum(num_qubits),
                                                low=bounds[0],
                                                high=bounds[1])
                q = QuantumRegister(sum(num_qubits), name='q')
                qc = QuantumCircuit(q)
                init_dist.build(qc, q)
                init_distribution = Custom(num_qubits=sum(num_qubits),
                                           circuit=qc)
                var_form = TwoLocal(sum(num_qubits),
                                    'ry',
                                    'cz',
                                    reps=1,
                                    initial_state=init_distribution,
                                    entanglement=entangler_map)
                if init_params is None:
                    init_params = aqua_globals.random.random(
                        var_form.num_parameters) * 2 * 1e-2
                # Set generator circuit
                self.generator_circuit = UnivariateVariationalDistribution(
                    int(np.sum(num_qubits)),
                    var_form,
                    init_params,
                    low=bounds[0],
                    high=bounds[1])

        if len(num_qubits) > 1:
            if isinstance(self.generator_circuit,
                          MultivariateVariationalDistribution):
                pass
            else:
                raise AquaError('Set multivariate variational distribution '
                                'to represent multivariate data')
        else:
            if isinstance(self.generator_circuit,
                          UnivariateVariationalDistribution):
                pass
            else:
                raise AquaError('Set univariate variational distribution '
                                'to represent univariate data')
        # Set optimizer for updating the generator network
        self._optimizer = ADAM(maxiter=1,
                               tol=1e-6,
                               lr=1e-3,
                               beta_1=0.7,
                               beta_2=0.99,
                               noise_factor=1e-6,
                               eps=1e-6,
                               amsgrad=True,
                               snapshot_dir=snapshot_dir)

        if np.ndim(self._bounds) == 1:
            bounds = np.reshape(self._bounds, (1, len(self._bounds)))
        else:
            bounds = self._bounds
        for j, prec in enumerate(self._num_qubits):
            # prepare data grid for dim j
            grid = np.linspace(bounds[j, 0], bounds[j, 1], (2**prec))
            if j == 0:
                if len(self._num_qubits) > 1:
                    self._data_grid = [grid]
                else:
                    self._data_grid = grid
                self._grid_elements = grid
            elif j == 1:
                self._data_grid.append(grid)
                temp = []
                for g_e in self._grid_elements:
                    for g in grid:
                        temp0 = [g_e]
                        temp0.append(g)
                        temp.append(temp0)
                self._grid_elements = temp
            else:
                self._data_grid.append(grid)
                temp = []
                for g_e in self._grid_elements:
                    for g in grid:
                        temp0 = deepcopy(g_e)
                        temp0.append(g)
                        temp.append(temp0)
                self._grid_elements = deepcopy(temp)
        self._data_grid = np.array(self._data_grid)

        self._shots = None
        self._discriminator = None
        self._ret = {}
Пример #20
0
    def _run(self) -> 'AmplitudeEstimationResult':
        # check if A factory or state_preparation has been set
        if self.state_preparation is None:
            if self._a_factory is None:  # getter emits deprecation warnings, therefore nest
                raise AquaError(
                    'Either the state_preparation variable or the a_factory '
                    '(deprecated) must be set to run the algorithm.')

        if self._quantum_instance.is_statevector:
            self.construct_circuit(measurement=False)
            # run circuit on statevector simulator
            ret = self._quantum_instance.execute(self._circuit)
            state_vector = np.asarray([ret.get_statevector(self._circuit)])
            self._ret['statevector'] = state_vector

            # get state probabilities
            state_probabilities = np.real(state_vector.conj() *
                                          state_vector)[0]

            # evaluate results
            a_probabilities, y_probabilities = self._evaluate_statevector_results(
                state_probabilities)

            # store number of shots: convention is 1 shot for statevector,
            # needed so that MLE works!
            self._ret['shots'] = 1
        else:
            # run circuit on QASM simulator
            self.construct_circuit(measurement=True)
            ret = self._quantum_instance.execute(self._circuit)

            # get counts
            self._ret['counts'] = ret.get_counts()

            # construct probabilities
            y_probabilities = OrderedDict()
            a_probabilities = OrderedDict()
            shots = self._quantum_instance._run_config.shots

            for state, counts in ret.get_counts().items():
                y = int(state.replace(' ', '')[:self._m][::-1], 2)
                probability = counts / shots
                y_probabilities[y] = probability
                a = np.round(np.power(np.sin(y * np.pi / 2**self._m), 2),
                             decimals=7)
                a_probabilities[a] = a_probabilities.get(a, 0.0) + probability

            # store shots
            self._ret['shots'] = shots

        # construct a_items and y_items
        a_items = [(a, p) for (a, p) in a_probabilities.items() if p > 1e-6]
        y_items = [(y, p) for (y, p) in y_probabilities.items() if p > 1e-6]
        a_items = list(a_probabilities.items())
        y_items = list(y_probabilities.items())
        a_items = sorted(a_items)
        y_items = sorted(y_items)
        self._ret['a_items'] = a_items
        self._ret['y_items'] = y_items

        # map estimated values to original range and extract probabilities
        self._ret['mapped_values'] = [
            self.post_processing(a_item[0]) for a_item in self._ret['a_items']
        ]
        self._ret['values'] = [a_item[0] for a_item in self._ret['a_items']]
        self._ret['y_values'] = [y_item[0] for y_item in y_items]
        self._ret['probabilities'] = [
            a_item[1] for a_item in self._ret['a_items']
        ]
        self._ret['mapped_items'] = [
            (self._ret['mapped_values'][i], self._ret['probabilities'][i])
            for i in range(len(self._ret['mapped_values']))
        ]

        # determine most likely estimator
        self._ret['value'] = None  # estimate in [0,1]
        self._ret['estimation'] = None  # estimate mapped to right interval
        self._ret['max_probability'] = 0
        for val, (est, prob) in zip(self._ret['values'],
                                    self._ret['mapped_items']):
            if prob > self._ret['max_probability']:
                self._ret['max_probability'] = prob
                self._ret['estimation'] = est
                self._ret['value'] = val

        # count the number of Q-oracle calls
        self._ret['num_oracle_queries'] = self._ret['shots'] * (self._M - 1)

        # get MLE
        self._run_mle()

        # get 95% confidence interval
        alpha = 0.05
        kind = 'likelihood_ratio'  # empirically the most precise kind
        self._ret['95%_confidence_interval'] = self.confidence_interval(
            alpha, kind)

        ae_result = AmplitudeEstimationAlgorithmResult()
        ae_result.a_estimation = self._ret['value']
        ae_result.estimation = self._ret['estimation']
        ae_result.num_oracle_queries = self._ret['num_oracle_queries']
        ae_result.confidence_interval = self._ret['95%_confidence_interval']

        result = AmplitudeEstimationResult()
        result.combine(ae_result)
        result.ml_value = self._ret['ml_value']
        result.mapped_a_samples = self._ret['values']
        result.probabilities = self._ret['probabilities']
        result.shots = self._ret['shots']
        result.mle = self._ret['mle']
        if 'statevector' in self._ret:
            result.circuit_result = self._ret['statevector']
        elif 'counts' in self._ret:
            result.circuit_result = dict(self._ret['counts'])
        result.a_samples = self._ret['a_items']
        result.y_measurements = self._ret['y_items']
        result.mapped_values = self._ret['mapped_values']
        result.max_probability = self._ret['max_probability']
        return result
Пример #21
0
    def _run(self) -> 'VQEAdaptResult':
        """
        Run the algorithm to compute the minimum eigenvalue.

        Returns:
            dict: Dictionary of results

        Raises:
            AquaError: wrong setting of operator and backend.
        """
        self._ret = {}  # TODO should be eliminated
        self._operator = VQE._config_the_best_mode(
            self, self._operator, self._quantum_instance.backend)
        self._use_simulator_snapshot_mode = \
            is_aer_statevector_backend(self._quantum_instance.backend) \
            and isinstance(self._operator, (WeightedPauliOperator, TPBGroupedWeightedPauliOperator))
        self._quantum_instance.circuit_summary = True

        cycle_regex = re.compile(r'(.+)( \1)+')
        # reg-ex explanation:
        # 1. (.+) will match at least one number and try to match as many as possible
        # 2. the match of this part is placed into capture group 1
        # 3. ( \1)+ will match a space followed by the contents of capture group 1
        # -> this results in any number of repeating numbers being detected

        threshold_satisfied = False
        alternating_sequence = False
        prev_op_indices = []
        theta = []
        max_grad = ()
        iteration = 0
        while not threshold_satisfied and not alternating_sequence:
            iteration += 1
            logger.info('--- Iteration #%s ---', str(iteration))
            # compute gradients
            cur_grads = self._compute_gradients(self._excitation_pool, theta,
                                                self._delta,
                                                self._var_form_base,
                                                self._operator,
                                                self._optimizer)
            # pick maximum gradient
            max_grad_index, max_grad = max(enumerate(cur_grads),
                                           key=lambda item: np.abs(item[1][0]))
            # store maximum gradient's index for cycle detection
            prev_op_indices.append(max_grad_index)
            # log gradients
            gradlog = "\nGradients in iteration #{}".format(str(iteration))
            gradlog += "\nID: Excitation Operator: Gradient  <(*) maximum>"
            for i, grad in enumerate(cur_grads):
                gradlog += '\n{}: {}: {}'.format(str(i), str(grad[1]),
                                                 str(grad[0]))
                if grad[1] == max_grad[1]:
                    gradlog += '\t(*)'
            logger.info(gradlog)
            if np.abs(max_grad[0]) < self._threshold:
                logger.info(
                    "Adaptive VQE terminated succesfully with a final maximum gradient: %s",
                    str(np.abs(max_grad[0])))
                threshold_satisfied = True
                break
            # check indices of picked gradients for cycles
            if cycle_regex.search(' '.join(map(str,
                                               prev_op_indices))) is not None:
                logger.info("Alternating sequence found. Finishing.")
                logger.info("Final maximum gradient: %s",
                            str(np.abs(max_grad[0])))
                alternating_sequence = True
                break
            # add new excitation to self._var_form_base
            self._var_form_base.push_hopping_operator(max_grad[1])
            theta.append(0.0)
            # run VQE on current Ansatz
            algorithm = VQE(self._operator,
                            self._var_form_base,
                            self._optimizer,
                            initial_point=theta)
            vqe_result = algorithm.run(self._quantum_instance)
            self._ret['opt_params'] = vqe_result.optimal_point
            theta = vqe_result.optimal_point.tolist()
        # once finished evaluate auxiliary operators if any
        if self._aux_operators is not None and self._aux_operators:
            algorithm = VQE(self._operator,
                            self._var_form_base,
                            self._optimizer,
                            initial_point=theta,
                            aux_operators=self._aux_operators)
            vqe_result = algorithm.run(self._quantum_instance)
            self._ret['opt_params'] = vqe_result.optimal_point

        if threshold_satisfied:
            finishing_criterion = 'Threshold converged'
        elif alternating_sequence:
            finishing_criterion = 'Aborted due to cyclicity'
        else:
            raise AquaError(
                'The algorithm finished due to an unforeseen reason!')

        # extend VQE returned information with additional outputs
        result = VQEAdaptResult()
        result.combine(vqe_result)
        result.num_iterations = iteration
        result.final_max_gradient = max_grad[0]
        result.finishing_criterion = finishing_criterion

        logger.info('The final energy is: %s', str(result.optimal_value.real))
        return result
Пример #22
0
    def build_eom_matrices(self,
                           excitations_list,
                           q_commutators,
                           w_commutators,
                           m_commutators,
                           v_commutators,
                           available_entry,
                           wave_fn,
                           quantum_instance=None):
        """Compute M, V, Q and W matrices.

        Args:
            excitations_list (list): single excitations list + double excitation list
            q_commutators (dict): key: a string of matrix indices; value:
                                    the commutators for Q matrix
            w_commutators (dict): key: a string of matrix indices; value:
                                    the commutators for W matrix
            m_commutators (dict): key: a string of matrix indices; value:
                                    the commutators for M matrix
            v_commutators (dict): key: a string of matrix indices; value:
                                    the commutators for V matrix
            available_entry (int): number of entries in the matrix
            wave_fn (QuantumCircuit or numpy.ndarray): the circuit generated wave function
            for the ground state energy
            quantum_instance (QuantumInstance): a quantum instance with configured settings

        Returns:
            numpy.ndarray: M matrix
            numpy.ndarray: V matrix
            numpy.ndarray: Q matrix
            numpy.ndarray: W matrix

        Raises:
            AquaError: wrong setting for wave_fn and quantum_instance
        """
        if isinstance(wave_fn, QuantumCircuit) and quantum_instance is None:
            raise AquaError(
                "quantum_instance is required when wavn_fn is a QuantumCircuit."
            )

        size = len(excitations_list)
        logger.info('EoM matrix size is %sx%s.', size, size)

        # get all to-be-processed index
        if self._is_eom_matrix_symmetric:
            mus, nus = np.triu_indices(size)
        else:
            mus, nus = np.indices((size, size))
            mus = np.asarray(mus.flat)
            nus = np.asarray(nus.flat)

        m_mat = np.zeros((size, size), dtype=complex)
        v_mat = np.zeros((size, size), dtype=complex)
        q_mat = np.zeros((size, size), dtype=complex)
        w_mat = np.zeros((size, size), dtype=complex)
        m_mat_std, v_mat_std, q_mat_std, w_mat_std = 0, 0, 0, 0

        if quantum_instance is not None:

            circuit_names = []
            circuits = []
            for idx, _ in enumerate(mus):
                m_u = mus[idx]
                n_u = nus[idx]

                for op in [
                        q_commutators[m_u][n_u], w_commutators[m_u][n_u],
                        m_commutators[m_u][n_u], v_commutators[m_u][n_u]
                ]:
                    if op is not None and not op.is_empty():
                        curr_circuits = op.construct_evaluation_circuit(
                            wave_function=wave_fn,
                            statevector_mode=quantum_instance.is_statevector)
                        for c in curr_circuits:
                            if c.name not in circuit_names:
                                circuits.append(c)
                                circuit_names.append(c.name)

            result = quantum_instance.execute(circuits)

            # evaluate results
            for idx, _ in enumerate(mus):
                m_u = mus[idx]
                n_u = nus[idx]

                def _get_result(op):
                    mean, std = 0.0, 0.0
                    if op is not None and not op.is_empty():
                        mean, std = \
                            op.evaluate_with_result(
                                result=result,
                                statevector_mode=quantum_instance.is_statevector)
                    return mean, std

                q_mean, q_std = _get_result(q_commutators[m_u][n_u])
                w_mean, w_std = _get_result(w_commutators[m_u][n_u])
                m_mean, m_std = _get_result(m_commutators[m_u][n_u])
                v_mean, v_std = _get_result(v_commutators[m_u][n_u])

                q_mat[m_u][n_u] = q_mean if q_mean != 0.0 else q_mat[m_u][n_u]
                w_mat[m_u][n_u] = w_mean if w_mean != 0.0 else w_mat[m_u][n_u]
                m_mat[m_u][n_u] = m_mean if m_mean != 0.0 else m_mat[m_u][n_u]
                v_mat[m_u][n_u] = v_mean if v_mean != 0.0 else v_mat[m_u][n_u]
                q_mat_std += q_std
                w_mat_std += w_std
                m_mat_std += m_std
                v_mat_std += v_std
        else:
            for idx, _ in enumerate(mus):
                m_u = mus[idx]
                n_u = nus[idx]
                q_mean, q_std = q_commutators[m_u][n_u].evaluate_with_statevector(wave_fn) \
                    if q_commutators[m_u][n_u] is not None else (0.0, 0.0)
                w_mean, w_std = w_commutators[m_u][n_u].evaluate_with_statevector(wave_fn) \
                    if w_commutators[m_u][n_u] is not None else (0.0, 0.0)
                m_mean, m_std = m_commutators[m_u][n_u].evaluate_with_statevector(wave_fn) \
                    if m_commutators[m_u][n_u] is not None else (0.0, 0.0)
                v_mean, v_std = v_commutators[m_u][n_u].evaluate_with_statevector(wave_fn) \
                    if v_commutators[m_u][n_u] is not None else (0.0, 0.0)
                q_mat[m_u][n_u] = q_mean if q_mean != 0.0 else q_mat[m_u][n_u]
                w_mat[m_u][n_u] = w_mean if w_mean != 0.0 else w_mat[m_u][n_u]
                m_mat[m_u][n_u] = m_mean if m_mean != 0.0 else m_mat[m_u][n_u]
                v_mat[m_u][n_u] = v_mean if v_mean != 0.0 else v_mat[m_u][n_u]

        # pylint: disable=unsubscriptable-object
        if self._is_eom_matrix_symmetric:
            # pylint: disable=unsubscriptable-object
            q_mat = q_mat + q_mat.T - np.identity(q_mat.shape[0]) * q_mat
            w_mat = w_mat + w_mat.T - np.identity(w_mat.shape[0]) * w_mat
            m_mat = m_mat + m_mat.T - np.identity(m_mat.shape[0]) * m_mat
            v_mat = v_mat + v_mat.T - np.identity(v_mat.shape[0]) * v_mat

        q_mat = np.real(q_mat)
        w_mat = np.real(w_mat)
        m_mat = np.real(m_mat)
        v_mat = np.real(v_mat)

        q_mat_std = q_mat_std / float(available_entry)
        w_mat_std = w_mat_std / float(available_entry)
        m_mat_std = m_mat_std / float(available_entry)
        v_mat_std = v_mat_std / float(available_entry)

        logger.debug("\nQ:=========================\n%s", q_mat)
        logger.debug("\nW:=========================\n%s", w_mat)
        logger.debug("\nM:=========================\n%s", m_mat)
        logger.debug("\nV:=========================\n%s", v_mat)

        return m_mat, v_mat, q_mat, w_mat, m_mat_std, v_mat_std, q_mat_std, w_mat_std
Пример #23
0
    def __init__(self,
                 optimizer=None,
                 feature_map=None,
                 var_form=None,
                 training_dataset=None,
                 test_dataset=None,
                 datapoints=None,
                 max_evals_grouped=1,
                 minibatch_size=-1,
                 callback=None):
        """Initialize the object
        Args:
            optimizer (Optimizer): The classical optimizer to use.
            feature_map (FeatureMap): The FeatureMap instance to use.
            var_form (VariationalForm): The variational form instance.
            training_dataset (dict): The training dataset, in the format: {'A': np.ndarray, 'B': np.ndarray, ...}.
            test_dataset (dict): The test dataset, in same format as `training_dataset`.
            datapoints (np.ndarray): NxD array, N is the number of data and D is data dimension.
            max_evals_grouped (int): The maximum number of evaluations to perform simultaneously.
            minibatch_size (int): The size of a mini-batch.
            callback (Callable): a callback that can access the intermediate data during the optimization.
                Internally, four arguments are provided as follows the index of data batch, the index of evaluation,
                parameters of variational form, evaluated value.
        Notes:
            We use `label` to denotes numeric results and `class` the class names (str).
        """

        self.validate(locals())
        super().__init__(var_form=var_form,
                         optimizer=optimizer,
                         cost_fn=self._cost_function_wrapper)
        self._optimizer.set_max_evals_grouped(max_evals_grouped)

        self._callback = callback

        if feature_map is None:
            raise AquaError('Missing feature map.')
        if training_dataset is None:
            raise AquaError('Missing training dataset.')
        self._training_dataset, self._class_to_label = split_dataset_to_data_and_labels(
            training_dataset)
        self._label_to_class = {
            label: class_name
            for class_name, label in self._class_to_label.items()
        }
        self._num_classes = len(list(self._class_to_label.keys()))

        if test_dataset is not None:
            self._test_dataset = split_dataset_to_data_and_labels(
                test_dataset, self._class_to_label)
        else:
            self._test_dataset = test_dataset

        if datapoints is not None and not isinstance(datapoints, np.ndarray):
            datapoints = np.asarray(datapoints)
            if len(datapoints) == 0:
                datapoints = None
        self._datapoints = datapoints
        self._minibatch_size = minibatch_size

        self._eval_count = 0
        self._ret = {}
        self._feature_map = feature_map
        self._num_qubits = feature_map.num_qubits
Пример #24
0
def mcmt(self,
         q_controls,
         q_ancillae,
         single_control_gate_fun,
         q_targets,
         mode="basic"):
    """
    Apply a Multi-Control, Multi-Target using a generic gate.
    It can also be used to implement a generic Multi-Control gate, as the target could also be of length 1.

    Args:
        self (QuantumCircuit): The QuantumCircuit object to apply the mcmt gate on.
        q_controls (QuantumRegister | list of Qubit): The list of control qubits
        q_ancillae (QuantumRegister | list of Qubit): The list of ancillary qubits
        single_control_gate_fun (Gate): The single control gate function (e.g QuantumCircuit.cz or QuantumCircuit.ch)
        q_targets (QuantumRegister | list of Qubit): A list of qubits or a QuantumRegister
            to which the gate function should be applied.
        mode (str): The implementation mode to use (at the moment, only the basic mode is supported)

    """
    # check controls
    if isinstance(q_controls, QuantumRegister):
        control_qubits = [qb for qb in q_controls]
    elif isinstance(q_controls, list):
        control_qubits = q_controls
    else:
        raise AquaError(
            'MCT needs a list of qubits or a quantum register for controls.')

    # check target
    if isinstance(q_targets, QuantumRegister):
        target_qubits = [qb for qb in q_targets]
    elif isinstance(q_targets, list):
        target_qubits = q_targets
    else:
        raise AquaError(
            'MCT needs a list of qubits or a quantum register for targets.')

    # check ancilla
    if q_ancillae is None:
        ancillary_qubits = []
    elif isinstance(q_ancillae, QuantumRegister):
        ancillary_qubits = [qb for qb in q_ancillae]
    elif isinstance(q_ancillae, list):
        ancillary_qubits = q_ancillae
    else:
        raise AquaError(
            'MCT needs None or a list of qubits or a quantum register for ancilla.'
        )

    all_qubits = control_qubits + target_qubits + ancillary_qubits

    self._check_qargs(all_qubits)
    self._check_dups(all_qubits)

    if len(q_controls) == 1:
        for qubit in target_qubits:
            single_control_gate_fun(self, q_controls[0], qubit)
        return

    if mode == 'basic':
        # last ancillary qubit is the control of the gate
        ancn = len(ancillary_qubits)
        _ccx_v_chain_compute(self, control_qubits, ancillary_qubits)
        for qubit in target_qubits:
            single_control_gate_fun(self, ancillary_qubits[ancn - 1], qubit)
        _ccx_v_chain_uncompute(self, control_qubits, ancillary_qubits)
    else:
        raise AquaError(
            'Unrecognized mode "{0}" for building mcmt circuit, at the moment only "basic" mode is supported.'
            .format(mode))
    def construct_circuit(self,
                          circuit=None,
                          variable_register=None,
                          clause_register=None,
                          output_register=None,
                          ancillary_register=None,
                          mct_mode='basic'):  # pylint: disable=arguments-differ
        """
        Construct circuit.

        Args:
            circuit (QuantumCircuit): The optional circuit to extend from
            variable_register (QuantumRegister): The optional quantum register
                            to use for problem variables
            clause_register (QuantumRegister): The optional quantum register
                            to use for problem clauses
            output_register (QuantumRegister): The optional quantum register
                            to use for holding the output
            ancillary_register (QuantumRegister): The optional quantum register to use as ancilla
            mct_mode (str): The mode to use for building Multiple-Control Toffoli

        Returns:
            QuantumCircuit: quantum circuit.
        Raises:
            AquaError: invalid input
        """

        circuit = self._set_up_circuit(circuit=circuit,
                                       variable_register=variable_register,
                                       clause_register=clause_register,
                                       output_register=output_register,
                                       ancillary_register=ancillary_register,
                                       mct_mode=mct_mode)
        if self._depth == 0:
            self._construct_circuit_for_tiny_expr(circuit)
        elif self._depth == 1:
            lits = [l[1] for l in self._ast[1:]]
            flags = BooleanLogicNormalForm._lits_to_flags(lits)
            if flags is not None:
                or_circuit = OR(num_variable_qubits=len(
                    self._variable_register),
                                flags=flags,
                                mcx_mode=mct_mode)
                qubits = self._variable_register[:] + [
                    self._output_register[0]
                ]
                if self._ancillary_register:
                    qubits += self._ancillary_register[:or_circuit.
                                                       num_ancilla_qubits]

                circuit.compose(or_circuit, qubits, inplace=True)
            else:
                circuit.u(pi, 0, pi, self._output_register[0])
        else:  # self._depth == 2
            # compute all clauses
            for clause_index, clause_expr in enumerate(self._ast[1:]):
                if clause_expr[0] == 'and':
                    lits = [l[1] for l in clause_expr[1:]]
                elif clause_expr[0] == 'lit':
                    lits = [clause_expr[1]]
                else:
                    raise AquaError(
                        'Operator "{}" of clause {} in logic expression {} is unexpected.'
                        .format(clause_expr[0], clause_index, self._ast))
                flags = BooleanLogicNormalForm._lits_to_flags(lits)
                if flags is not None:
                    and_circuit = AND(num_variable_qubits=len(
                        self._variable_register),
                                      flags=flags,
                                      mcx_mode=mct_mode)
                    qubits = self._variable_register[:] + [
                        self._clause_register[clause_index]
                    ]
                    if self._ancillary_register:
                        qubits += self._ancillary_register[:and_circuit.
                                                           num_ancilla_qubits]

                    circuit.compose(and_circuit, qubits, inplace=True)
                else:
                    circuit.u(pi, 0, pi, self._clause_register[clause_index])

            # init the output qubit to 1
            circuit.u(pi, 0, pi, self._output_register[self._output_idx])

            # collect results from all clauses
            circuit.u(pi, 0, pi, self._clause_register)
            circuit.mct(self._clause_register,
                        self._output_register[self._output_idx],
                        self._ancillary_register,
                        mode=mct_mode)
            circuit.u(pi, 0, pi, self._clause_register)

            # uncompute all clauses
            for clause_index, clause_expr in enumerate(self._ast[1:]):
                if clause_expr[0] == 'and':
                    lits = [l[1] for l in clause_expr[1:]]
                elif clause_expr[0] == 'lit':
                    lits = [clause_expr[1]]
                flags = BooleanLogicNormalForm._lits_to_flags(lits)
                if flags is not None:
                    and_circuit = AND(num_variable_qubits=len(
                        self._variable_register),
                                      flags=flags,
                                      mcx_mode=mct_mode)
                    qubits = self._variable_register[:] + [
                        self._clause_register[clause_index]
                    ]
                    if self._ancillary_register:
                        qubits += self._ancillary_register[:and_circuit.
                                                           num_ancilla_qubits]

                    circuit.compose(and_circuit, qubits, inplace=True)
                else:
                    circuit.u(pi, 0, pi, self._clause_register[clause_index])
        return circuit
Пример #26
0
    def __init__(self,
                 operator=None,
                 state_in=None,
                 iqft=None,
                 num_time_slices=1,
                 num_ancillae=1,
                 expansion_mode='trotter',
                 expansion_order=1,
                 evo_time=2 * np.pi,
                 state_in_circuit_factory=None,
                 unitary_circuit_factory=None,
                 shallow_circuit_concat=False,
                 pauli_list=None):
        """
        Constructor.

        Args:
            operator (WeightedPauliOperator): the hamiltonian Operator object
            state_in (InitialState): the InitialState pluggable component
            representing the initial quantum state
            iqft (IQFT): the Inverse Quantum Fourier Transform pluggable component
            num_time_slices (int): the number of time slices
            num_ancillae (int): the number of ancillary qubits to use for the measurement
            expansion_mode (str): the expansion mode (trotter|suzuki)
            expansion_order (int): the suzuki expansion order
            evo_time (float): the evolution time
            state_in_circuit_factory (CircuitFactory): the initial state represented by
            a CircuitFactory object
            unitary_circuit_factory (CircuitFactory): the problem unitary represented
            by a CircuitFactory object
            shallow_circuit_concat (bool): indicate whether to use shallow (cheap) mode
            for circuit concatenation
            pauli_list (list[Pauli]): the flat list of paulis for the operator
        Raises:
            AquaError: Missing input
        """

        if (operator is not None and unitary_circuit_factory is not None) or \
                (operator is None and unitary_circuit_factory is None):
            raise AquaError(
                'Please supply either an operator or a unitary circuit '
                'factory but not both.')

        self._operator = operator
        if operator is not None:
            self._pauli_list = operator.reorder_paulis(
            ) if pauli_list is None else pauli_list
        self._unitary_circuit_factory = unitary_circuit_factory
        self._state_in = state_in
        self._state_in_circuit_factory = state_in_circuit_factory
        self._iqft = iqft
        self._num_time_slices = num_time_slices
        self._num_ancillae = num_ancillae
        self._expansion_mode = expansion_mode
        self._expansion_order = expansion_order
        self._evo_time = evo_time
        self._shallow_circuit_concat = shallow_circuit_concat
        self._ancilla_phase_coef = 1
        self._state_register = None
        self._ancillary_register = None
        self._auxiliary_register = None
        self._circuit = None
Пример #27
0
    def _run(self) -> 'VQEAdaptResult':
        """
        Run the algorithm to compute the minimum eigenvalue.

        Returns:
            dict: Dictionary of results

        Raises:
            AquaError: wrong setting of operator and backend.
        """
        self._ret = {}  # TODO should be eliminated
        # self._operator = VQE._config_the_best_mode(self, self._operator,
        #                                            self._quantum_instance.backend)
        self._quantum_instance.circuit_summary = True

        threshold_satisfied = False
        alternating_sequence = False
        max_iterations_exceeded = False
        prev_op_indices = []
        theta = []  # type: List
        max_grad = (0, 0)
        iteration = 0
        while self._max_iterations is None or iteration < self._max_iterations:
            iteration += 1
            logger.info('--- Iteration #%s ---', str(iteration))
            # compute gradients
            cur_grads = self._compute_gradients(self._excitation_pool, theta,
                                                self._delta,
                                                self._var_form_base,
                                                self._operator,
                                                self._optimizer)
            # pick maximum gradient
            max_grad_index, max_grad = max(enumerate(cur_grads),
                                           key=lambda item: np.abs(item[1][0]))
            # store maximum gradient's index for cycle detection
            prev_op_indices.append(max_grad_index)
            # log gradients
            gradlog = "\nGradients in iteration #{}".format(str(iteration))
            gradlog += "\nID: Excitation Operator: Gradient  <(*) maximum>"
            for i, grad in enumerate(cur_grads):
                gradlog += '\n{}: {}: {}'.format(str(i), str(grad[1]),
                                                 str(grad[0]))
                if grad[1] == max_grad[1]:
                    gradlog += '\t(*)'
            logger.info(gradlog)
            if np.abs(max_grad[0]) < self._threshold:
                logger.info(
                    "Adaptive VQE terminated succesfully with a final maximum gradient: %s",
                    str(np.abs(max_grad[0])))
                threshold_satisfied = True
                break
            # check indices of picked gradients for cycles
            if VQEAdapt._check_cyclicity(prev_op_indices):
                logger.info("Alternating sequence found. Finishing.")
                logger.info("Final maximum gradient: %s",
                            str(np.abs(max_grad[0])))
                alternating_sequence = True
                break
            # add new excitation to self._var_form_base
            self._var_form_base.push_hopping_operator(max_grad[1])
            theta.append(0.0)
            # run VQE on current Ansatz
            algorithm = VQE(self._operator,
                            self._var_form_base,
                            self._optimizer,
                            initial_point=theta)
            vqe_result = algorithm.run(self._quantum_instance)
            self._ret['opt_params'] = vqe_result.optimal_point
            theta = vqe_result.optimal_point.tolist()
        else:
            # reached maximum number of iterations
            max_iterations_exceeded = True
            logger.info("Maximum number of iterations reached. Finishing.")
            logger.info("Final maximum gradient: %s", str(np.abs(max_grad[0])))

        # once finished evaluate auxiliary operators if any
        if self._aux_operators is not None and self._aux_operators:
            algorithm = VQE(self._operator,
                            self._var_form_base,
                            self._optimizer,
                            initial_point=theta,
                            aux_operators=self._aux_operators)
            vqe_result = algorithm.run(self._quantum_instance)
            self._ret['opt_params'] = vqe_result.optimal_point

        if threshold_satisfied:
            finishing_criterion = 'Threshold converged'
        elif alternating_sequence:
            finishing_criterion = 'Aborted due to cyclicity'
        elif max_iterations_exceeded:
            finishing_criterion = 'Maximum number of iterations reached'
        else:
            raise AquaError(
                'The algorithm finished due to an unforeseen reason!')

        # extend VQE returned information with additional outputs
        result = VQEAdaptResult()
        result.combine(vqe_result)
        result.num_iterations = iteration
        result.final_max_gradient = max_grad[0]
        result.finishing_criterion = finishing_criterion

        logger.info('The final energy is: %s', str(result.optimal_value.real))
        return result
Пример #28
0
    def construct_circuit(
            self,
            circuit=None,
            variable_register=None,
            clause_register=None,
            output_register=None,
            ancillary_register=None,
            mct_mode='basic'
    ):
        """
        Construct circuit.

        Args:
            circuit (QuantumCircuit): The optional circuit to extend from
            variable_register (QuantumRegister): The optional quantum register to use for problem variables
            clause_register (QuantumRegister): The optional quantum register to use for problem clauses
            output_register (QuantumRegister): The optional quantum register to use for holding the output
            ancillary_register (QuantumRegister): The optional quantum register to use as ancilla
            mct_mode (str): The mode to use for building Multiple-Control Toffoli

        Returns:
            QuantumCircuit: quantum circuit.
        """

        circuit = self._set_up_circuit(
            circuit=circuit,
            variable_register=variable_register,
            clause_register=clause_register,
            output_register=output_register,
            ancillary_register=ancillary_register,
            mct_mode=mct_mode
        )
        if self._depth == 0:
            self._construct_circuit_for_tiny_expr(circuit)
        elif self._depth == 1:
            circuit.u3(pi, 0, pi, self._variable_register)
            circuit.u3(pi, 0, pi, self._output_register)
            lits = [l[1] for l in self._ast[1:]]
            logic_or(
                lits,
                circuit,
                self._variable_register,
                self._output_register[0],
                self._ancillary_register,
                mct_mode
            )
        else:  # self._depth == 2
            # compute all clauses
            for clause_index, clause_expr in enumerate(self._ast[1:]):
                if clause_expr[0] == 'and':
                    lits = [l[1] for l in clause_expr[1:]]
                elif clause_expr[0] == 'lit':
                    lits = [clause_expr[1]]
                else:
                    raise AquaError(
                        'Operator "{}" of clause {} in logic expression {} is unexpected.'.format(
                            clause_expr[0], clause_index, self._ast)
                    )
                logic_and(
                    lits,
                    circuit,
                    self._variable_register,
                    self._clause_register[clause_index],
                    self._ancillary_register,
                    mct_mode
                )

            # init the output qubit to 1
            circuit.u3(pi, 0, pi, self._output_register[self._output_idx])

            # collect results from all clauses
            circuit.u3(pi, 0, pi, self._clause_register)
            circuit.mct(
                self._clause_register,
                self._output_register[self._output_idx],
                self._ancillary_register,
                mode=mct_mode
            )
            circuit.u3(pi, 0, pi, self._clause_register)

            # uncompute all clauses
            for clause_index, clause_expr in enumerate(self._ast[1:]):
                if clause_expr[0] == 'and':
                    lits = [l[1] for l in clause_expr[1:]]
                else:  # clause_expr[0] == 'lit':
                    lits = [clause_expr[1]]
                logic_and(
                    lits,
                    circuit,
                    self._variable_register,
                    self._clause_register[clause_index],
                    self._ancillary_register,
                    mct_mode
                )
        return circuit
Пример #29
0
 def get_optimal_circuit(self):
     if 'opt_params' not in self._ret:
         raise AquaError("Cannot find optimal circuit before running the "
                         "algorithm to find optimal params.")
     return self._var_form_base.construct_circuit(self._ret['opt_params'])
Пример #30
0
    def _run(self) -> VQEResult:
        """Run the algorithm to compute the minimum eigenvalue.

        Returns:
            The result of the VQE algorithm as ``VQEResult``.

        Raises:
            AquaError: Wrong setting of operator and backend.
        """

        if self.operator is None:  # type: ignore
            raise AquaError("The operator was never provided.")
        self._check_operator_varform()
        self._quantum_instance.circuit_summary = True
        self._eval_count = 0

        # initial orbital rotation starting point is provided
        if self._orbital_rotation.matrix_a is not None and self._orbital_rotation.matrix_b is not \
                None:
            self._qmolecule_rotated = copy.copy(self._qmolecule)
            OOVQE._rotate_orbitals_in_qmolecule(self._qmolecule_rotated,
                                                self._orbital_rotation)
            self._operator, _ = self._core.run(self._qmolecule_rotated)
            logger.info(
                '\n\nSetting the initial value for OO matrices and rotating Hamiltonian \n'
            )
            logger.info('Optimising  Orbital Coefficient Rotation Alpha: \n%s',
                        repr(self._orbital_rotation.matrix_a))
            logger.info('Optimising  Orbital Coefficient Rotation Beta: \n%s',
                        repr(self._orbital_rotation.matrix_b))

        # save the original number of parameters as we modify their number to bypass the
        # error checks that are not tailored to OOVQE

        total_time = 0
        # iterative method
        if self._iterative_oo:
            for _ in range(self._iterative_oo_iterations):

                # optimize wavefunction ansatz
                self.var_form._num_parameters = self.var_form_num_parameters
                if isinstance(self.operator,
                              LegacyBaseOperator):  # type: ignore
                    self.operator = self.operator.to_opflow()  # type: ignore
                self.var_form._bounds = self.var_form_bounds
                vqresult_wavefun = self.find_minimum(
                    initial_point=self.initial_point[:self.
                                                     var_form_num_parameters],
                    var_form=self.var_form,
                    cost_fn=self._energy_evaluation,
                    optimizer=self.optimizer)
                self.initial_point[:self.
                                   var_form_num_parameters] = vqresult_wavefun.optimal_point

                # optimize orbitals
                self.var_form._bounds = self._bound_oo
                self.var_form._num_parameters = self._orbital_rotation.num_parameters
                self._fixed_wavefunction_params = vqresult_wavefun.optimal_point
                vqresult = self.find_minimum(
                    initial_point=self.initial_point[self.
                                                     var_form_num_parameters:],
                    var_form=self.var_form,
                    cost_fn=self._energy_evaluation_oo,
                    optimizer=self.optimizer)
                self.initial_point[
                    self.var_form_num_parameters:] = vqresult.optimal_point
                total_time += vqresult.optimizer_time
        else:
            # simultaneous method (ansatz and orbitals are optimized at the same time)
            self.var_form._bounds = self._bounds
            self.var_form._num_parameters = len(self._bounds)

            vqresult = self.find_minimum(initial_point=self.initial_point,
                                         var_form=self.var_form,
                                         cost_fn=self._energy_evaluation_oo,
                                         optimizer=self.optimizer)
            total_time += vqresult.optimizer_time

        # write original number of parameters to avoid errors due to parameter number mismatch
        self.var_form._num_parameters = self.var_form_num_parameters

        # save the results
        self._ret = {}
        self._ret['num_optimizer_evals'] = vqresult.optimizer_evals
        self._ret['min_val'] = vqresult.optimal_value
        if self._iterative_oo:
            self._ret['opt_params'] = self.initial_point[
                self.var_form_num_parameters:]
            self._ret[
                'opt_params_orbitals'] = self.initial_point[:self.
                                                            var_form_num_parameters]
        else:
            self._ret[
                'opt_params'] = vqresult.optimal_point[:self.
                                                       var_form_num_parameters]
            self._ret['opt_params_orbitals'] = vqresult.optimal_point[
                self.var_form_num_parameters:]

        self._ret['eval_time'] = total_time
        self._ret['opt_params_dict'] = vqresult.optimal_parameters

        if self._ret['num_optimizer_evals'] is not None and \
                self._eval_count >= self._ret['num_optimizer_evals']:
            self._eval_count = self._ret['num_optimizer_evals']
        self._eval_time = self._ret['eval_time']
        logger.info(
            'Optimization complete in %s seconds.\nFound opt_params %s in %s evals',
            self._eval_time, self._ret['opt_params'], self._eval_count)
        self._ret['eval_count'] = self._eval_count

        result = OOVQEResult()
        result.combine(vqresult)
        result.optimal_point_orbitals = self._ret["opt_params_orbitals"]
        result.eigenvalue = vqresult.optimal_value + 0j
        # record all parameters (wavefunction and orbitals) to overcome error checks
        _ret_temp_params = copy.copy(self._ret['opt_params'])
        self._ret['opt_params'] = self._ret[
            'opt_params'][:self.var_form_num_parameters]
        result.eigenstate = self.get_optimal_vector()
        self._ret['opt_params'] = _ret_temp_params

        self._ret['energy'] = self.get_optimal_cost()
        self._ret['eigvals'] = np.asarray([self._ret['energy']])
        self._ret['eigvecs'] = np.asarray([result.eigenstate])

        if self.aux_operators:
            self._eval_aux_ops()
            # TODO remove when ._ret is deprecated
            result.aux_operator_eigenvalues = self._ret['aux_ops'][0]

        result.cost_function_evals = self._eval_count

        return result