diff --git a/.pylintdict b/.pylintdict index f3142299ee..15db10878e 100644 --- a/.pylintdict +++ b/.pylintdict @@ -54,6 +54,7 @@ bmod bobyqa bohr bool +bools boolean bosonic bpa @@ -243,6 +244,8 @@ gogolin goldfarb gridpoints grinko +groundenergy +groundstates grover gset gsls @@ -459,7 +462,9 @@ onee online onodera onwards +OO oom +oovqe operatorbase oplist oplus @@ -729,6 +734,7 @@ vecs Veis vertices vibrational +vibronic ville visualisation vqc diff --git a/qiskit/aqua/algorithms/eigen_solvers/eigen_solver.py b/qiskit/aqua/algorithms/eigen_solvers/eigen_solver.py index c12128c411..5354b723d3 100644 --- a/qiskit/aqua/algorithms/eigen_solvers/eigen_solver.py +++ b/qiskit/aqua/algorithms/eigen_solvers/eigen_solver.py @@ -55,7 +55,8 @@ def compute_eigenvalues( self.aux_operators = aux_operators if aux_operators else None # type: ignore return EigensolverResult() - def supports_aux_operators(self) -> bool: + @classmethod + def supports_aux_operators(cls) -> bool: """Whether computing the expectation value of auxiliary operators is supported. Returns: diff --git a/qiskit/aqua/algorithms/eigen_solvers/numpy_eigen_solver.py b/qiskit/aqua/algorithms/eigen_solvers/numpy_eigen_solver.py index 5d02ff2d45..71cbac3186 100755 --- a/qiskit/aqua/algorithms/eigen_solvers/numpy_eigen_solver.py +++ b/qiskit/aqua/algorithms/eigen_solvers/numpy_eigen_solver.py @@ -143,7 +143,8 @@ def filter_criterion(self, filter_criterion: Optional[ """ set the filter criterion """ self._filter_criterion = filter_criterion - def supports_aux_operators(self) -> bool: + @classmethod + def supports_aux_operators(cls) -> bool: return True def _check_set_k(self) -> None: diff --git a/qiskit/aqua/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py b/qiskit/aqua/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py index 7f4eebf2ff..2d4720dc55 100644 --- a/qiskit/aqua/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py +++ b/qiskit/aqua/algorithms/minimum_eigen_solvers/minimum_eigen_solver.py @@ -52,10 +52,11 @@ def compute_minimum_eigenvalue( if operator is not None: self.operator = operator # type: ignore if aux_operators is not None: - self.aux_operators = aux_operators if aux_operators else None # type: ignore + self.aux_operators = aux_operators # type: ignore return MinimumEigensolverResult() - def supports_aux_operators(self) -> bool: + @classmethod + def supports_aux_operators(cls) -> bool: """Whether computing the expectation value of auxiliary operators is supported. If the minimum eigensolver computes an eigenstate of the main operator then it diff --git a/qiskit/aqua/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py b/qiskit/aqua/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py index 81939bab20..fc80355318 100644 --- a/qiskit/aqua/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py +++ b/qiskit/aqua/algorithms/minimum_eigen_solvers/numpy_minimum_eigen_solver.py @@ -84,8 +84,9 @@ def filter_criterion(self, filter_criterion: Optional[ """ set the filter criterion """ self._ces.filter_criterion = filter_criterion - def supports_aux_operators(self) -> bool: - return self._ces.supports_aux_operators() + @classmethod + def supports_aux_operators(cls) -> bool: + return NumPyEigensolver.supports_aux_operators() def compute_minimum_eigenvalue( self, diff --git a/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py b/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py index 9f8c9c5fc4..5e5ad187e7 100755 --- a/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py +++ b/qiskit/aqua/algorithms/minimum_eigen_solvers/vqe.py @@ -391,7 +391,8 @@ def extract_circuits(op): return circuits - def supports_aux_operators(self) -> bool: + @classmethod + def supports_aux_operators(cls) -> bool: return True def _run(self) -> 'VQEResult': diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py b/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py new file mode 100644 index 0000000000..9217167746 --- /dev/null +++ b/qiskit/chemistry/algorithms/ground_state_solvers/__init__.py @@ -0,0 +1,25 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Ground state calculation algorithms.""" + +from .ground_state_solver import GroundStateSolver +from .adapt_vqe import AdaptVQE +from .ground_state_eigensolver import GroundStateEigensolver +from .minimum_eigensolver_factories import MinimumEigensolverFactory, VQEUCCSDFactory + +__all__ = ['GroundStateSolver', + 'AdaptVQE', + 'GroundStateEigensolver', + 'MinimumEigensolverFactory', + 'VQEUCCSDFactory' + ] diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/adapt_vqe.py b/qiskit/chemistry/algorithms/ground_state_solvers/adapt_vqe.py new file mode 100644 index 0000000000..41cf274627 --- /dev/null +++ b/qiskit/chemistry/algorithms/ground_state_solvers/adapt_vqe.py @@ -0,0 +1,277 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""A ground state calculation employing the AdaptVQE algorithm.""" + +import re +import logging +from typing import Optional, List, Tuple, Union +import numpy as np + +from qiskit.aqua.utils.validation import validate_min +from qiskit.aqua.operators import WeightedPauliOperator +from qiskit.aqua.algorithms import VQE +from qiskit.aqua import AquaError +from ...results.electronic_structure_result import ElectronicStructureResult +from ...results.vibronic_structure_result import VibronicStructureResult +from ...transformations.fermionic_transformation import FermionicTransformation +from ...drivers.base_driver import BaseDriver +from ...components.variational_forms import UCCSD +from ...fermionic_operator import FermionicOperator +from ...bosonic_operator import BosonicOperator + +from .minimum_eigensolver_factories import MinimumEigensolverFactory +from .ground_state_eigensolver import GroundStateEigensolver + +logger = logging.getLogger(__name__) + + +class AdaptVQE(GroundStateEigensolver): + """A ground state calculation employing the AdaptVQE algorithm.""" + + def __init__(self, + transformation: FermionicTransformation, + solver: MinimumEigensolverFactory, + threshold: float = 1e-5, + delta: float = 1, + max_iterations: Optional[int] = None, + ) -> None: + """ + Args: + transformation: a fermionic driver to operator transformation strategy. + solver: a factory for the VQE solver employing a UCCSD variational form. + threshold: the energy convergence threshold. It has a minimum value of 1e-15. + delta: the finite difference step size for the gradient computation. It has a minimum + value of 1e-5. + max_iterations: the maximum number of iterations of the AdaptVQE algorithm. + """ + validate_min('threshold', threshold, 1e-15) + validate_min('delta', delta, 1e-5) + + super().__init__(transformation, solver) + + self._threshold = threshold + self._delta = delta + self._max_iterations = max_iterations + + def returns_groundstate(self) -> bool: + return True + + def _compute_gradients(self, + excitation_pool: List[WeightedPauliOperator], + theta: List[float], + vqe: VQE, + ) -> List[Tuple[float, WeightedPauliOperator]]: + """ + Computes the gradients for all available excitation operators. + + Args: + excitation_pool: pool of excitation operators + theta: list of (up to now) optimal parameters + vqe: the variational quantum eigensolver instance used for solving + + Returns: + List of pairs consisting of gradient and excitation operator. + """ + res = [] + # compute gradients for all excitation in operator pool + for exc in excitation_pool: + # push next excitation to variational form + vqe.var_form.push_hopping_operator(exc) + # NOTE: because we overwrite the var_form inside of the VQE, we need to update the VQE's + # internal _var_form_params, too. We can do this by triggering the var_form setter. Once + # the VQE does not store this pure var_form property any longer this can be removed. + vqe.var_form = vqe.var_form + # We also need to invalidate the internally stored expectation operator because it needs + # to be updated for the new var_form. + vqe._expect_op = None + # evaluate energies + parameter_sets = theta + [-self._delta] + theta + [self._delta] + energy_results = vqe._energy_evaluation(np.asarray(parameter_sets)) + # compute gradient + gradient = (energy_results[0] - energy_results[1]) / (2 * self._delta) + res.append((np.abs(gradient), exc)) + # pop excitation from variational form + vqe.var_form.pop_hopping_operator() + + return res + + @staticmethod + def _check_cyclicity(indices: List[int]) -> bool: + """ + Auxiliary function to check for cycles in the indices of the selected excitations. + + Args: + indices: the list of chosen gradient indices. + Returns: + Whether repeating sequences of indices have been detected. + """ + cycle_regex = re.compile(r"(\b.+ .+\b)( \b\1\b)+") + # reg-ex explanation: + # 1. (\b.+ .+\b) will match at least two numbers and try to match as many as possible. The + # word boundaries in the beginning and end ensure that now numbers are split into digits. + # 2. the match of this part is placed into capture group 1 + # 3. ( \b\1\b)+ will match a space followed by the contents of capture group 1 (again + # delimited by word boundaries to avoid separation into digits). + # -> this results in any sequence of at least two numbers being detected + match = cycle_regex.search(' '.join(map(str, indices))) + logger.debug('Cycle detected: %s', match) + # Additionally we also need to check whether the last two numbers are identical, because the + # reg-ex above will only find cycles of at least two consecutive numbers. + # It is sufficient to assert that the last two numbers are different due to the iterative + # nature of the algorithm. + return match is not None or (len(indices) > 1 and indices[-2] == indices[-1]) + + 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``s 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_electronic_energy)) + return result + + +class AdaptVQEResult(ElectronicStructureResult): + """ AdaptVQE Result.""" + + @property + def num_iterations(self) -> int: + """ Returns number of iterations """ + return self.get('num_iterations') + + @num_iterations.setter + def num_iterations(self, value: int) -> None: + """ Sets number of iterations """ + self.data['num_iterations'] = value + + @property + def final_max_gradient(self) -> float: + """ Returns final maximum gradient """ + return self.get('final_max_gradient') + + @final_max_gradient.setter + def final_max_gradient(self, value: float) -> None: + """ Sets final maximum gradient """ + self.data['final_max_gradient'] = value + + @property + def finishing_criterion(self) -> str: + """ Returns finishing criterion """ + return self.get('finishing_criterion') + + @finishing_criterion.setter + def finishing_criterion(self, value: str) -> None: + """ Sets finishing criterion """ + self.data['finishing_criterion'] = value diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py new file mode 100644 index 0000000000..3b7d5dfcfa --- /dev/null +++ b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_eigensolver.py @@ -0,0 +1,177 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Ground state computation using a minimum eigensolver.""" + +from typing import Union, List, Optional, Dict + +import numpy as np + +from qiskit import QuantumCircuit +from qiskit.circuit import Instruction +from qiskit.quantum_info import Statevector +from qiskit.result import Result +from qiskit.aqua.algorithms import MinimumEigensolver +from qiskit.aqua.operators import OperatorBase, WeightedPauliOperator, StateFn, CircuitSampler +from ...fermionic_operator import FermionicOperator +from ...bosonic_operator import BosonicOperator +from ...drivers.base_driver import BaseDriver +from ...transformations.transformation import Transformation +from ...results.electronic_structure_result import ElectronicStructureResult +from ...results.vibronic_structure_result import VibronicStructureResult +from .ground_state_solver import GroundStateSolver + +from .minimum_eigensolver_factories import MinimumEigensolverFactory + + +class GroundStateEigensolver(GroundStateSolver): + """Ground state computation using a minimum eigensolver.""" + + def __init__(self, transformation: Transformation, + solver: Union[MinimumEigensolver, MinimumEigensolverFactory]) -> None: + """ + + Args: + transformation: Qubit Operator Transformation + solver: Minimum Eigensolver or MESFactory object, e.g. the VQEUCCSDFactory. + """ + super().__init__(transformation) + self._solver = solver + + @property + def solver(self) -> Union[MinimumEigensolver, MinimumEigensolverFactory]: + """Returns the minimum eigensolver or factory.""" + return self._solver + + @solver.setter + def solver(self, solver: Union[MinimumEigensolver, MinimumEigensolverFactory]) -> None: + """Sets the minimum eigensolver or factory.""" + self._solver = solver + + def returns_groundstate(self) -> bool: + """Whether the eigensolver returns the ground state or only ground state energy.""" + return self._solver.supports_aux_operators() + + def solve(self, + driver: BaseDriver, + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None) \ + -> Union[ElectronicStructureResult, VibronicStructureResult]: + """Compute Ground State properties. + + Args: + driver: a chemistry driver object which defines the chemical problem that is to be + solved by this calculation. + aux_operators: Additional auxiliary operators to evaluate at the ground state. + Depending on whether a fermionic or bosonic system is solved, the type of the + operators must be ``FermionicOperator`` or ``BosonicOperator``, respectively. + + Raises: + NotImplementedError: If an operator in ``aux_operators`` is not of type + ``FermionicOperator``. + + Returns: + An eigenstate result. Depending on the transformation this can be an electronic + structure or bosonic result. + """ + if aux_operators is not None: + if any(not isinstance(op, (WeightedPauliOperator, FermionicOperator)) + for op in aux_operators): + raise NotImplementedError('Currently only fermionic problems are supported.') + + # get the operator and auxiliary operators, and transform the provided auxiliary operators + # note that ``aux_operators`` contains not only the transformed ``aux_operators`` passed + # by the user but also additional ones from the transformation + operator, aux_operators = self.transformation.transform(driver, aux_operators) + + if isinstance(self._solver, MinimumEigensolverFactory): + # this must be called after transformation.transform + solver = self._solver.get_solver(self.transformation) + else: + solver = self._solver + + # if the eigensolver does not support auxiliary operators, reset them + if not solver.supports_aux_operators(): + aux_operators = None + + raw_mes_result = solver.compute_minimum_eigenvalue(operator, aux_operators) + + result = self.transformation.interpret(raw_mes_result) + return result + + def evaluate_operators(self, + state: Union[str, dict, Result, + list, np.ndarray, Statevector, + QuantumCircuit, Instruction, + OperatorBase], + operators: Union[WeightedPauliOperator, OperatorBase, list, dict] + ) -> Union[float, List[float], Dict[str, float]]: + """Evaluates additional operators at the given state. + + Args: + state: any kind of input that can be used to specify a state. See also ``StateFn`` for + more details. + operators: either a single, list or dictionary of ``WeightedPauliOperator``s or any kind + of operator implementing the ``OperatorBase``. + + Returns: + The expectation value of the given operator(s). The return type will be identical to the + format of the provided operators. + """ + # try to get a QuantumInstance from the solver + quantum_instance = getattr(self._solver, 'quantum_instance', None) + + if not isinstance(state, StateFn): + state = StateFn(state) + + # handle all possible formats of operators + # i.e. if a user gives us a dict of operators, we return the results equivalently, etc. + if isinstance(operators, list): + results = [] + for op in operators: + results.append(self._eval_op(state, op, quantum_instance)) + elif isinstance(operators, dict): + results = {} # type: ignore + for name, op in operators.items(): + results[name] = self._eval_op(state, op, quantum_instance) + else: + results = self._eval_op(state, operators, quantum_instance) + + return results + + def _eval_op(self, state, op, quantum_instance): + if isinstance(op, WeightedPauliOperator): + op = op.to_opflow() + + # if the operator is empty we simply return 0 + if op == 0: + # Note, that for some reason the individual results need to be wrapped in lists. + # See also: VQE._eval_aux_ops() + return [0.j] + + exp = ~StateFn(op) @ state # + + if quantum_instance is not None: + try: + sampler = CircuitSampler(quantum_instance) + result = sampler.convert(exp).eval() + except ValueError: + # TODO make this cleaner. The reason for it being here is that some quantum + # instances can lead to non-positive statevectors which the Qiskit circuit + # Initializer is unable to handle. + result = exp.eval() + else: + result = exp.eval() + + # Note, that for some reason the individual results need to be wrapped in lists. + # See also: VQE._eval_aux_ops() + return [result] diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py new file mode 100644 index 0000000000..bde680ac37 --- /dev/null +++ b/qiskit/chemistry/algorithms/ground_state_solvers/ground_state_solver.py @@ -0,0 +1,103 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""The ground state calculation interface.""" + +from abc import ABC, abstractmethod +from typing import Dict, List, Optional, Union + +import numpy as np + +from qiskit import QuantumCircuit +from qiskit.circuit import Instruction +from qiskit.quantum_info import Statevector +from qiskit.result import Result +from qiskit.aqua.operators import OperatorBase, WeightedPauliOperator +from ...fermionic_operator import FermionicOperator +from ...bosonic_operator import BosonicOperator +from ...drivers.base_driver import BaseDriver +from ...results.electronic_structure_result import ElectronicStructureResult +from ...results.vibronic_structure_result import VibronicStructureResult +from ...transformations.transformation import Transformation + + +class GroundStateSolver(ABC): + """The ground state calculation interface""" + + def __init__(self, transformation: Transformation) -> None: + """ + Args: + transformation: transformation from driver to qubit operator (and aux. operators) + """ + self._transformation = transformation + + @property + def transformation(self) -> Transformation: + """Returns the transformation used to obtain a qubit operator from the molecule.""" + return self._transformation + + @transformation.setter + def transformation(self, transformation: Transformation) -> None: + """Sets the transformation used to obtain a qubit operator from the molecule.""" + self._transformation = transformation + + @abstractmethod + def solve(self, + driver: BaseDriver, + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None) \ + -> Union[ElectronicStructureResult, VibronicStructureResult]: + """Compute the ground state energy of the molecule that was supplied via the driver. + + Args: + driver: a chemistry driver object which defines the chemical problem that is to be + solved by this calculation. + aux_operators: Additional auxiliary operators to evaluate. Must be of type + ``FermionicOperator`` if the qubit transformation is fermionic and of type + ``BosonicOperator`` it is bosonic. + + Returns: + An eigenstate result. + """ + raise NotImplementedError + + @abstractmethod + def returns_groundstate(self) -> bool: + """Whether this class returns only the ground state energy or also the ground state itself. + + Returns: + True, if this class also returns the ground state in the results object. + False otherwise. + """ + raise NotImplementedError + + @abstractmethod + def evaluate_operators(self, + state: Union[str, dict, Result, + list, np.ndarray, Statevector, + QuantumCircuit, Instruction, + OperatorBase], + operators: Union[WeightedPauliOperator, OperatorBase, list, dict] + ) -> Union[float, List[float], Dict[str, float]]: + """Evaluates additional operators at the given state. + + Args: + state: any kind of input that can be used to specify a state. See also ``StateFn`` for + more details. + operators: either a single, list or dictionary of ``WeightedPauliOperator``s or any kind + of operator implementing the ``OperatorBase``. + + Returns: + The expectation value of the given operator(s). The return type will be identical to the + format of the provided operators. + """ + raise NotImplementedError diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py new file mode 100644 index 0000000000..7a3086d33c --- /dev/null +++ b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/__init__.py @@ -0,0 +1,22 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Factories that create a minimum eigensolver based on a qubit transformation.""" + +from .minimum_eigensolver_factory import MinimumEigensolverFactory +from .numpy_minimum_eigensolver_factory import NumPyMinimumEigensolverFactory +from .vqe_uccsd_factory import VQEUCCSDFactory + +__all__ = ['MinimumEigensolverFactory', + 'NumPyMinimumEigensolverFactory', + 'VQEUCCSDFactory' + ] diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py new file mode 100644 index 0000000000..cc188303ce --- /dev/null +++ b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/minimum_eigensolver_factory.py @@ -0,0 +1,40 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""The minimum eigensolver factory for ground state calculation algorithms.""" + +from abc import ABC, abstractmethod +from qiskit.aqua.algorithms import MinimumEigensolver +from ....transformations.transformation import Transformation + + +class MinimumEigensolverFactory(ABC): + """A factory to construct a minimum eigensolver based on a qubit operator transformation.""" + + @abstractmethod + def get_solver(self, transformation: Transformation) -> MinimumEigensolver: + """Returns a minimum eigensolver, based on the qubit operator transformation. + + Args: + transformation: The qubit operator transformation. + + Returns: + A minimum eigensolver suitable to compute the ground state of the molecule transformed + by ``transformation``. + """ + raise NotImplementedError + + @abstractmethod + def supports_aux_operators(self) -> bool: + """Returns whether the eigensolver generated by this factory supports auxiliary operators. + """ + raise NotImplementedError diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py new file mode 100644 index 0000000000..994f695744 --- /dev/null +++ b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/numpy_minimum_eigensolver_factory.py @@ -0,0 +1,85 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""The numpy minimum eigensolver factory for ground state calculation algorithms.""" + +from typing import Optional, Union, List, Callable +import numpy as np + +from qiskit.aqua.algorithms import MinimumEigensolver, NumPyMinimumEigensolver +from ....transformations.transformation import Transformation +from .minimum_eigensolver_factory import MinimumEigensolverFactory + + +class NumPyMinimumEigensolverFactory(MinimumEigensolverFactory): + """A factory to construct a NumPyMinimumEigensolver.""" + + def __init__(self, + filter_criterion: Callable[[Union[List, np.ndarray], float, Optional[List[float]]], + bool] = None, + use_default_filter_criterion: bool = False) -> None: + """ + Args: + filter_criterion: callable that allows to filter eigenvalues/eigenstates. The minimum + eigensolver is only searching over feasible states and returns an eigenstate that + has the smallest eigenvalue among feasible states. The callable has the signature + `filter(eigenstate, eigenvalue, aux_values)` and must return a boolean to indicate + whether to consider this value or not. If there is no + feasible element, the result can even be empty. + use_default_filter_criterion: whether to use the transformation's default filter + criterion if ``filter_criterion`` is ``None``. + """ + self._filter_criterion = filter_criterion + self._use_default_filter_criterion = use_default_filter_criterion + + @property + def filter_criterion(self) -> Callable[[Union[List, np.ndarray], float, Optional[List[float]]], + bool]: + """ returns filter criterion """ + return self._filter_criterion + + @filter_criterion.setter + def filter_criterion(self, value: Callable[[Union[List, np.ndarray], float, + Optional[List[float]]], bool]) -> None: + """ sets filter criterion """ + self._filter_criterion = value + + @property + def use_default_filter_criterion(self) -> bool: + """ returns whether to use the default filter criterion """ + return self._use_default_filter_criterion + + @use_default_filter_criterion.setter + def use_default_filter_criterion(self, value: bool) -> None: + """ sets whether to use the default filter criterion """ + self._use_default_filter_criterion = value + + def get_solver(self, transformation: Transformation) -> MinimumEigensolver: + """Returns a NumPyMinimumEigensolver which possibly uses the default filter criterion + provided by the ``transformation``. + + Args: + transformation: a fermionic qubit operator transformation. + + Returns: + A NumPyMinimumEigensolver suitable to compute the ground state of the molecule + transformed by ``transformation``. + """ + filter_criterion = self._filter_criterion + if not filter_criterion and self._use_default_filter_criterion: + filter_criterion = transformation.get_default_filter_criterion() + + npme = NumPyMinimumEigensolver(filter_criterion=filter_criterion) + return npme + + def supports_aux_operators(self): + return NumPyMinimumEigensolver.supports_aux_operators() diff --git a/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uccsd_factory.py b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uccsd_factory.py new file mode 100644 index 0000000000..9284a09873 --- /dev/null +++ b/qiskit/chemistry/algorithms/ground_state_solvers/minimum_eigensolver_factories/vqe_uccsd_factory.py @@ -0,0 +1,149 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""The minimum eigensolver factory for ground state calculation algorithms.""" + +from typing import Optional +import numpy as np + +from qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import MinimumEigensolver, VQE +from qiskit.aqua.operators import ExpectationBase +from qiskit.aqua.components.optimizers import Optimizer +from ....components.variational_forms import UCCSD +from ....transformations.fermionic_transformation import FermionicTransformation +from ....components.initial_states import HartreeFock + +from .minimum_eigensolver_factory import MinimumEigensolverFactory + + +class VQEUCCSDFactory(MinimumEigensolverFactory): + """A factory to construct a VQE minimum eigensolver with UCCSD ansatz wavefunction.""" + + def __init__(self, + quantum_instance: QuantumInstance, + optimizer: Optional[Optimizer] = None, + initial_point: Optional[np.ndarray] = None, + expectation: Optional[ExpectationBase] = None, + include_custom: bool = False) -> None: + """ + Args: + quantum_instance: The quantum instance used in the minimum eigensolver. + 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. + """ + self._quantum_instance = quantum_instance + self._optimizer = optimizer + self._initial_point = initial_point + self._expectation = expectation + self._include_custom = include_custom + + @property + def quantum_instance(self) -> QuantumInstance: + """Getter of the quantum instance.""" + return self._quantum_instance + + @quantum_instance.setter + def quantum_instance(self, q_instance: QuantumInstance) -> None: + """Setter of the quantum instance.""" + self._quantum_instance = q_instance + + @property + def optimizer(self) -> Optimizer: + """Getter of the optimizer.""" + return self._optimizer + + @optimizer.setter + def optimizer(self, optimizer: Optimizer) -> None: + """Setter of the optimizer.""" + self._optimizer = optimizer + + @property + def initial_point(self) -> np.ndarray: + """Getter of the initial point.""" + return self._initial_point + + @initial_point.setter + def initial_point(self, initial_point: np.ndarray) -> None: + """Setter of the initial point.""" + self._initial_point = initial_point + + @property + def expectation(self) -> ExpectationBase: + """Getter of the expectation.""" + return self._expectation + + @expectation.setter + def expectation(self, expectation: ExpectationBase) -> None: + """Setter of the expectation.""" + self._expectation = expectation + + @property + def include_custom(self) -> bool: + """Getter of the ``include_custom`` setting for the ``expectation`` setting.""" + return self._include_custom + + @include_custom.setter + def include_custom(self, include_custom: bool) -> None: + """Setter of the ``include_custom`` setting for the ``expectation`` setting.""" + self._include_custom = include_custom + + def get_solver(self, # type: ignore + transformation: FermionicTransformation) -> MinimumEigensolver: + """Returns a VQE with a UCCSD wavefunction ansatz, based on ``transformation``. + This works only with a ``FermionicTransformation``. + + Args: + transformation: a fermionic qubit operator transformation. + + Returns: + A VQE suitable to compute the ground state of the molecule transformed + by ``transformation``. + """ + num_orbitals = transformation.molecule_info['num_orbitals'] + num_particles = transformation.molecule_info['num_particles'] + qubit_mapping = transformation.qubit_mapping + two_qubit_reduction = transformation.molecule_info['two_qubit_reduction'] + z2_symmetries = transformation.molecule_info['z2_symmetries'] + + initial_state = HartreeFock(num_orbitals, num_particles, qubit_mapping, + two_qubit_reduction, z2_symmetries.sq_list) + var_form = UCCSD(num_orbitals=num_orbitals, + num_particles=num_particles, + initial_state=initial_state, + qubit_mapping=qubit_mapping, + two_qubit_reduction=two_qubit_reduction, + z2_symmetries=z2_symmetries) + vqe = VQE(var_form=var_form, + quantum_instance=self._quantum_instance, + optimizer=self._optimizer, + initial_point=self._initial_point, + expectation=self._expectation, + include_custom=self._include_custom) + return vqe + + def supports_aux_operators(self): + return VQE.supports_aux_operators() diff --git a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py index 0dc3d58fb1..14bc0b318b 100644 --- a/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py +++ b/qiskit/chemistry/algorithms/minimum_eigen_solvers/vqe_adapt.py @@ -36,8 +36,7 @@ class VQEAdapt(VQAlgorithm): - """ - The Adaptive VQE algorithm. + """DEPRECATED. The Adaptive VQE algorithm. See https://arxiv.org/abs/1812.11173 """ @@ -73,6 +72,10 @@ def __init__(self, operator: LegacyBaseOperator, ValueError: if var_form_base is not an instance of UCCSD. See also: qiskit/chemistry/components/variational_forms/uccsd_adapt.py """ + warnings.warn('The qiskit.chemistry.algorithms.minimum_eigen_solvers.VQEAdapt object is ' + 'deprecated as of 0.8.0 and will be removed no sooner than 3 months after the' + ' release. You should use qiskit.chemistry.algorithms.ground_state_solvers.' + 'AdaptVQE instead.', DeprecationWarning, stacklevel=2) validate_min('threshold', threshold, 1e-15) validate_min('delta', delta, 1e-5) super().__init__(var_form=var_form_base, diff --git a/qiskit/chemistry/applications/molecular_ground_state_energy.py b/qiskit/chemistry/applications/molecular_ground_state_energy.py index bc10ab2a19..eecec094e5 100644 --- a/qiskit/chemistry/applications/molecular_ground_state_energy.py +++ b/qiskit/chemistry/applications/molecular_ground_state_energy.py @@ -12,6 +12,7 @@ """ Molecular ground state energy chemistry application """ +import warnings from typing import List, Optional, Callable, Union from qiskit.providers import BaseBackend @@ -59,6 +60,11 @@ def __init__(self, See also :class:`~qiskit.chemistry.core.Hamiltonian` which has the core processing behind this class. """ + + warnings.warn('The MolecularGroundStateEnergy class is deprecated as of Qiskit Aqua 0.8.0 ' + 'and will be removed no earlier than 3 months after the release date. ' + 'Instead, the GroundStateCalculation class can be used.', + DeprecationWarning, stacklevel=2) self._driver = driver self._solver = solver self._transformation = transformation diff --git a/qiskit/chemistry/components/variational_forms/uccsd.py b/qiskit/chemistry/components/variational_forms/uccsd.py index a2cc61d08b..16101937f7 100644 --- a/qiskit/chemistry/components/variational_forms/uccsd.py +++ b/qiskit/chemistry/components/variational_forms/uccsd.py @@ -153,7 +153,7 @@ def __init__(self, excitation_type=self._excitation_type,) self._hopping_ops, self._num_parameters = self._build_hopping_operators() - self._excitation_pool = None + self._excitation_pool = None # type: Optional[List[WeightedPauliOperator]] self._bounds = [(-np.pi, np.pi) for _ in range(self._num_parameters)] self._logging_construct_circuit = True @@ -226,14 +226,15 @@ def double_excitations(self): return self._double_excitations @property - def excitation_pool(self): - """ - Getter of full list of available excitations (called the pool) - Returns: - list[WeightedPauliOperator]: excitation pool - """ + def excitation_pool(self) -> List[WeightedPauliOperator]: + """Returns the full list of available excitations (called the pool).""" return self._excitation_pool + @excitation_pool.setter + def excitation_pool(self, excitation_pool: List[WeightedPauliOperator]) -> None: + """Sets the excitation pool.""" + self._excitation_pool = excitation_pool.copy() + def _build_hopping_operators(self): if logger.isEnabledFor(logging.DEBUG): TextProgressBar(sys.stderr) @@ -328,8 +329,9 @@ def manage_hopping_operators(self): hopping operators in a so called "excitation pool" and clears the previous list to be empty. Furthermore, the depth is asserted to be 1 which is required by the Adaptive VQE algorithm. """ - # store full list of excitations as pool - self._excitation_pool = self._hopping_ops.copy() + if self._excitation_pool is None: + # store full list of excitations as pool + self._excitation_pool = self._hopping_ops.copy() # check depth parameter if self._reps != 1: @@ -339,8 +341,8 @@ def manage_hopping_operators(self): # reset internal excitation list to be empty self._hopping_ops = [] - self._num_parameters = len(self._hopping_ops) * self._reps - self._bounds = [(-np.pi, np.pi) for _ in range(self._num_parameters)] + self._num_parameters = 0 + self._bounds = [] def push_hopping_operator(self, excitation): """ diff --git a/qiskit/chemistry/core/chemistry_operator.py b/qiskit/chemistry/core/chemistry_operator.py index 37008fef1a..07ed9afa3a 100644 --- a/qiskit/chemistry/core/chemistry_operator.py +++ b/qiskit/chemistry/core/chemistry_operator.py @@ -14,9 +14,11 @@ Such an operator takes a QMolecule and produces an input for a quantum algorithm """ + from abc import ABC, abstractmethod +import warnings import logging -from typing import Union, List, Tuple, Optional, cast +from typing import Dict, Union, List, Tuple, Optional, cast import numpy as np from qiskit.aqua.algorithms import MinimumEigensolverResult, EigensolverResult, AlgorithmResult @@ -43,6 +45,10 @@ class ChemistryOperator(ABC): @abstractmethod def __init__(self): + warnings.warn('The ChemistryOperator is deprecated as of Qiskit Aqua 0.8.0 and will be ' + 'removed no earlier than 3 months after the release date. Instead, the ' + 'FermionicTransformation can be used to transform QMolecules and construct ' + 'ground state result objects.', DeprecationWarning, stacklevel=2) self._molecule_info = {} @abstractmethod @@ -105,6 +111,13 @@ class MolecularChemistryResult(AlgorithmResult): Energies are in Hartree and dipole moments in A.U unless otherwise stated. """ + def __init__(self, a_dict: Optional[Dict] = None) -> None: + super().__init__(a_dict) + warnings.warn('The qiskit.chemistry.chemistry_operator.MolecularChemistryResult object is ' + 'deprecated as of 0.8.0 and will be removed no sooner than 3 months after the' + ' release. You should use qiskit.chemistry.algorithms.ground_state_solvers.' + 'FermionicGroundStateResult instead.', DeprecationWarning, stacklevel=2) + @property def algorithm_result(self) -> AlgorithmResult: """ Returns raw algorithm result """ @@ -153,6 +166,14 @@ class MolecularGroundStateResult(MolecularChemistryResult): Energies are in Hartree and dipole moments in A.U unless otherwise stated. """ + def __init__(self, a_dict: Optional[Dict] = None) -> None: + super().__init__(a_dict) + warnings.warn('The qiskit.chemistry.chemistry_operator.MolecularGroundStateResult object ' + 'is deprecated as of 0.8.0 and will be removed no sooner than 3 months after ' + 'the release. You should use qiskit.chemistry.algorithms.' + 'ground_state_solvers.FermionicGroundStateResult instead.', + DeprecationWarning, stacklevel=2) + @property def energy(self) -> Optional[float]: """ Returns ground state energy if nuclear_repulsion_energy is available from driver """ diff --git a/qiskit/chemistry/core/hamiltonian.py b/qiskit/chemistry/core/hamiltonian.py index 89a2e0265e..fab4cb5b0e 100644 --- a/qiskit/chemistry/core/hamiltonian.py +++ b/qiskit/chemistry/core/hamiltonian.py @@ -14,7 +14,7 @@ energy of the electrons and nuclei in a molecule. """ import warnings -from typing import Optional, List, Union, cast +from typing import Optional, List, Union, cast, Tuple import logging from enum import Enum @@ -33,12 +33,22 @@ class TransformationType(Enum): """ Transformation Type enum """ + warnings.warn('The chemistry.core.TransformationType class is deprecated as of Qiskit Aqua ' + '0.8.0 and will be removed no earlier than 3 months after the release date. ' + 'Instead, the ' + 'chemistry.qubit_transformatons.fermionic_transformation.TransformationType can ' + 'be used.', DeprecationWarning, stacklevel=2) FULL = 'full' PARTICLE_HOLE = 'particle_hole' class QubitMappingType(Enum): """ QubitMappingType enum """ + warnings.warn('The chemistry.core.QubitMappingType class is deprecated as of Qiskit Aqua ' + '0.8.0 and will be removed no earlier than 3 months after the release date. ' + 'Instead, the ' + 'chemistry.qubit_transformatons.fermionic_transformation.QubitMappingType can ' + 'be used.', DeprecationWarning, stacklevel=2) JORDAN_WIGNER = 'jordan_wigner' PARITY = 'parity' BRAVYI_KITAEV = 'bravyi_kitaev' @@ -82,6 +92,9 @@ def __init__(self, Raises: QiskitChemistryError: Invalid symmetry reduction """ + warnings.warn('The Hamiltonian class is deprecated as of Qiskit Aqua 0.8.0 and will be ' + 'removed no earlier than 3 months after the release date. Instead, the ' + 'FermionicTransformation can be used.', DeprecationWarning, stacklevel=2) transformation = transformation.value qubit_mapping = qubit_mapping.value orbital_reduction = orbital_reduction if orbital_reduction is not None else [] @@ -114,7 +127,9 @@ def __init__(self, self._ph_y_dipole_shift = 0.0 self._ph_z_dipole_shift = 0.0 - def run(self, qmolecule): + def run(self, qmolecule: QMolecule) -> Tuple[WeightedPauliOperator, + List[WeightedPauliOperator]]: + """ run method""" logger.debug('Processing started...') # Save these values for later combination with the quantum computation result self._hf_energy = qmolecule.hf_energy @@ -155,7 +170,8 @@ def run(self, qmolecule): if orbitals_list: orbitals_list = np.array(orbitals_list) orbitals_list = \ - orbitals_list[(orbitals_list >= 0) & (orbitals_list < qmolecule.num_orbitals)] + orbitals_list[(cast(np.ndarray, orbitals_list) >= 0) & + (orbitals_list < qmolecule.num_orbitals)] freeze_list_alpha = [i for i in orbitals_list if i < num_alpha] freeze_list_beta = [i for i in orbitals_list if i < num_beta] diff --git a/qiskit/chemistry/results/__init__.py b/qiskit/chemistry/results/__init__.py new file mode 100644 index 0000000000..f7661d5b73 --- /dev/null +++ b/qiskit/chemistry/results/__init__.py @@ -0,0 +1,24 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Chemistry results module.""" + +from .electronic_structure_result import DipoleTuple, ElectronicStructureResult +from .vibronic_structure_result import VibronicStructureResult +from .eigenstate_result import EigenstateResult + + +__all__ = ['DipoleTuple', + 'EigenstateResult', + 'ElectronicStructureResult', + 'VibronicStructureResult' + ] diff --git a/qiskit/chemistry/results/eigenstate_result.py b/qiskit/chemistry/results/eigenstate_result.py new file mode 100644 index 0000000000..a891803ea6 --- /dev/null +++ b/qiskit/chemistry/results/eigenstate_result.py @@ -0,0 +1,86 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Eigenstate results module.""" + +from typing import Optional, List, Union + +import numpy as np + +from qiskit import QuantumCircuit +from qiskit.circuit import Instruction +from qiskit.quantum_info import Statevector +from qiskit.result import Result +from qiskit.aqua.algorithms import AlgorithmResult +from qiskit.aqua.operators import OperatorBase + + +class EigenstateResult(AlgorithmResult): + """The eigenstate result interface.""" + + @property + def eigenenergies(self) -> Optional[np.ndarray]: + """ returns eigen energies """ + return self.get('eigenenergies') + + @eigenenergies.setter + def eigenenergies(self, value: np.ndarray) -> None: + """ set eigen energies """ + self.data['eigenenergies'] = value + + @property + def eigenstates(self) -> Optional[List[Union[str, dict, Result, list, np.ndarray, Statevector, + QuantumCircuit, Instruction, OperatorBase]]]: + """ returns eigen states """ + return self.get('eigenstates') + + @eigenstates.setter + def eigenstates(self, value: List[Union[str, dict, Result, list, np.ndarray, Statevector, + QuantumCircuit, Instruction, OperatorBase]]) -> None: + """ set eigen states """ + self.data['eigenstates'] = value + + @property + def groundenergy(self) -> Optional[float]: + """ returns ground energy """ + energies = self.get('eigenenergies') + if energies: + return energies[0].real + return None + + @property + def groundstate(self) -> Optional[Union[str, dict, Result, list, np.ndarray, Statevector, + QuantumCircuit, Instruction, OperatorBase]]: + """ returns ground state """ + states = self.get('eigenstates') + if states: + return states[0] + return None + + @property + def aux_operator_eigenvalues(self) -> Optional[List[float]]: + """ return aux operator eigen values """ + return self.get('aux_operator_eigenvalues') + + @aux_operator_eigenvalues.setter + def aux_operator_eigenvalues(self, value: List[float]) -> None: + """ set aux operator eigen values """ + self.data['aux_operator_eigenvalues'] = value + + @property + def raw_result(self) -> Optional[AlgorithmResult]: + """Returns the raw algorithm result.""" + return self.get('raw_result') + + @raw_result.setter + def raw_result(self, result: AlgorithmResult) -> None: + self.data['raw_result'] = result diff --git a/qiskit/chemistry/results/electronic_structure_result.py b/qiskit/chemistry/results/electronic_structure_result.py new file mode 100644 index 0000000000..06b92fd748 --- /dev/null +++ b/qiskit/chemistry/results/electronic_structure_result.py @@ -0,0 +1,335 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""The electronic structure result.""" + +from typing import List, Optional, Tuple, cast + +import logging +import numpy as np + +from qiskit.chemistry import QMolecule + +from .eigenstate_result import EigenstateResult + +logger = logging.getLogger(__name__) + +# A dipole moment, when present as X, Y and Z components will normally have float values for all +# the components. However when using Z2Symmetries, if the dipole component operator does not +# commute with the symmetry then no evaluation is done and None will be used as the 'value' +# indicating no measurement of the observable took place +DipoleTuple = Tuple[Optional[float], Optional[float], Optional[float]] + + +class ElectronicStructureResult(EigenstateResult): + """The electronic structure result.""" + + @property + def hartree_fock_energy(self) -> float: + """ Returns Hartree-Fock energy """ + return self.get('hartree_fock_energy') + + @hartree_fock_energy.setter + def hartree_fock_energy(self, value: float) -> None: + """ Sets Hartree-Fock energy """ + self.data['hartree_fock_energy'] = value + + @property + def nuclear_repulsion_energy(self) -> Optional[float]: + """ Returns nuclear repulsion energy when available from driver """ + return self.get('nuclear_repulsion_energy') + + @nuclear_repulsion_energy.setter + def nuclear_repulsion_energy(self, value: float) -> None: + """ Sets nuclear repulsion energy """ + self.data['nuclear_repulsion_energy'] = value + + @property + def nuclear_dipole_moment(self) -> Optional[DipoleTuple]: + """ Returns nuclear dipole moment X,Y,Z components in A.U when available from driver """ + return self.get('nuclear_dipole_moment') + + @nuclear_dipole_moment.setter + def nuclear_dipole_moment(self, value: DipoleTuple) -> None: + """ Sets nuclear dipole moment in A.U """ + self.data['nuclear_dipole_moment'] = value + + # TODO we need to be able to extract the statevector or the optimal parameters that can + # construct the circuit of the GS from here (if the algorithm supports this) + + @property + def energy(self) -> Optional[float]: + """ Returns ground state energy if nuclear_repulsion_energy is available from driver """ + # TODO the fact that this property is computed on the fly breaks the `.combine()` + # functionality + nre = self.nuclear_repulsion_energy + return self.electronic_energy + nre if nre is not None else None + + @property + def electronic_energy(self) -> float: + """ Returns electronic part of ground state energy """ + # TODO the fact that this property is computed on the fly breaks the `.combine()` + # functionality + return (self.computed_electronic_energy + + self.ph_extracted_energy + + self.frozen_extracted_energy) + + @property + def computed_electronic_energy(self) -> float: + """ Returns computed electronic part of ground state energy """ + return self.get('computed_electronic_energy') + + @computed_electronic_energy.setter + def computed_electronic_energy(self, value: float) -> None: + """ Sets computed electronic part of ground state energy """ + self.data['computed_electronic_energy'] = value + + @property + def ph_extracted_energy(self) -> float: + """ Returns particle hole extracted part of ground state energy """ + return self.get('ph_extracted_energy') + + @ph_extracted_energy.setter + def ph_extracted_energy(self, value: float) -> None: + """ Sets particle hole extracted part of ground state energy """ + self.data['ph_extracted_energy'] = value + + @property + def frozen_extracted_energy(self) -> float: + """ Returns frozen extracted part of ground state energy """ + return self.get('frozen_extracted_energy') + + @frozen_extracted_energy.setter + def frozen_extracted_energy(self, value: float) -> None: + """ Sets frozen extracted part of ground state energy """ + self.data['frozen_extracted_energy'] = value + + # Dipole moment results. Note dipole moments of tuples of X, Y and Z components. Chemistry + # drivers either support dipole integrals or not. Note that when using Z2 symmetries of + + def has_dipole(self) -> bool: + """ Returns whether dipole moment is present in result or not """ + return self.nuclear_dipole_moment is not None and self.electronic_dipole_moment is not None + + @property + def reverse_dipole_sign(self) -> bool: + """ Returns if electronic dipole moment sign should be reversed when adding to nuclear """ + return self.get('reverse_dipole_sign') + + @reverse_dipole_sign.setter + def reverse_dipole_sign(self, value: bool) -> None: + """ Sets if electronic dipole moment sign should be reversed when adding to nuclear """ + self.data['reverse_dipole_sign'] = value + + @property + def total_dipole_moment(self) -> Optional[float]: + """ Returns total dipole of moment """ + if self.dipole_moment is None: + return None # No dipole at all + if np.any(np.equal(list(self.dipole_moment), None)): + return None # One or more components in the dipole is None + return np.sqrt(np.sum(np.power(list(self.dipole_moment), 2))) + + @property + def total_dipole_moment_in_debye(self) -> Optional[float]: + """ Returns total dipole of moment in Debye """ + tdm = self.total_dipole_moment + return tdm / QMolecule.DEBYE if tdm is not None else None + + @property + def dipole_moment(self) -> Optional[DipoleTuple]: + """ Returns dipole moment """ + edm = self.electronic_dipole_moment + if self.reverse_dipole_sign: + edm = cast(DipoleTuple, tuple(-1 * x if x is not None else None for x in edm)) + return _dipole_tuple_add(edm, self.nuclear_dipole_moment) + + @property + def dipole_moment_in_debye(self) -> Optional[DipoleTuple]: + """ Returns dipole moment in Debye """ + dipm = self.dipole_moment + if dipm is None: + return None + dipmd0 = dipm[0]/QMolecule.DEBYE if dipm[0] is not None else None + dipmd1 = dipm[1]/QMolecule.DEBYE if dipm[1] is not None else None + dipmd2 = dipm[2]/QMolecule.DEBYE if dipm[2] is not None else None + return dipmd0, dipmd1, dipmd2 + + @property + def electronic_dipole_moment(self) -> Optional[DipoleTuple]: + """ Returns electronic dipole moment """ + return _dipole_tuple_add(self.computed_dipole_moment, + _dipole_tuple_add(self.ph_extracted_dipole_moment, + self.frozen_extracted_dipole_moment)) + + @property + def computed_dipole_moment(self) -> Optional[DipoleTuple]: + """ Returns computed electronic part of dipole moment """ + return self.get('computed_dipole_moment') + + @computed_dipole_moment.setter + def computed_dipole_moment(self, value: DipoleTuple) -> None: + """ Sets computed electronic part of dipole moment """ + self.data['computed_dipole_moment'] = value + + @property + def ph_extracted_dipole_moment(self) -> Optional[DipoleTuple]: + """ Returns particle hole extracted part of dipole moment """ + return self.get('ph_extracted_dipole_moment') + + @ph_extracted_dipole_moment.setter + def ph_extracted_dipole_moment(self, value: DipoleTuple) -> None: + """ Sets particle hole extracted part of dipole moment """ + self.data['ph_extracted_dipole_moment'] = value + + @property + def frozen_extracted_dipole_moment(self) -> Optional[DipoleTuple]: + """ Returns frozen extracted part of dipole moment """ + return self.get('frozen_extracted_dipole_moment') + + @frozen_extracted_dipole_moment.setter + def frozen_extracted_dipole_moment(self, value: DipoleTuple) -> None: + """ Sets frozen extracted part of dipole moment """ + self.data['frozen_extracted_dipole_moment'] = value + + # Other measured operators. If these are not evaluated then None will be returned + # instead of any measured value. + + def has_observables(self): + """ Returns whether result has aux op observables such as spin, num particles """ + return self.total_angular_momentum is not None \ + or self.num_particles is not None \ + or self.magnetization is not None + + @property + def total_angular_momentum(self) -> Optional[float]: + """ Returns total angular momentum (S^2) """ + return self.get('total_angular_momentum') + + @total_angular_momentum.setter + def total_angular_momentum(self, value: float) -> None: + """ Sets total angular momentum """ + self.data['total_angular_momentum'] = value + + @property + def spin(self) -> Optional[float]: + """ Returns computed spin """ + if self.total_angular_momentum is None: + return None + return (-1.0 + np.sqrt(1 + 4 * self.total_angular_momentum)) / 2 + + @property + def num_particles(self) -> Optional[float]: + """ Returns measured number of particles """ + return self.get('num_particles') + + @num_particles.setter + def num_particles(self, value: float) -> None: + """ Sets measured number of particles """ + self.data['num_particles'] = value + + @property + def magnetization(self) -> Optional[float]: + """ Returns measured magnetization """ + return self.get('magnetization') + + @magnetization.setter + def magnetization(self, value: float) -> None: + """ Sets measured magnetization """ + self.data['magnetization'] = value + + def __str__(self) -> str: + """ Printable formatted result """ + return '\n'.join(self.formatted) + + @property + def formatted(self) -> List[str]: + """ Formatted result as a list of strings """ + lines = [] + lines.append('=== GROUND STATE ENERGY ===') + lines.append(' ') + lines.append('* Electronic ground state energy (Hartree): {}'. + format(round(self.electronic_energy, 12))) + lines.append(' - computed part: {}'. + format(round(self.computed_electronic_energy, 12))) + lines.append(' - frozen energy part: {}'. + format(round(self.frozen_extracted_energy, 12))) + lines.append(' - particle hole part: {}' + .format(round(self.ph_extracted_energy, 12))) + if self.nuclear_repulsion_energy is not None: + lines.append('~ Nuclear repulsion energy (Hartree): {}'. + format(round(self.nuclear_repulsion_energy, 12))) + lines.append('> Total ground state energy (Hartree): {}'. + format(round(self.energy, 12))) + if self.has_observables(): + line = ' Measured::' + if self.num_particles is not None: + line += ' # Particles: {:.3f}'.format(self.num_particles) + if self.spin is not None: + line += ' S: {:.3f}'.format(self.spin) + if self.total_angular_momentum is not None: + line += ' S^2: {:.3f}'.format(self.total_angular_momentum) + if self.magnetization is not None: + line += ' M: {:.5f}'.format(self.magnetization) + lines.append(line) + + if self.has_dipole(): + lines.append(' ') + lines.append('=== DIPOLE MOMENT ===') + lines.append(' ') + lines.append('* Electronic dipole moment (a.u.): {}' + .format(_dipole_to_string(self.electronic_dipole_moment))) + lines.append(' - computed part: {}' + .format(_dipole_to_string(self.computed_dipole_moment))) + lines.append(' - frozen energy part: {}' + .format(_dipole_to_string(self.frozen_extracted_dipole_moment))) + lines.append(' - particle hole part: {}' + .format(_dipole_to_string(self.ph_extracted_dipole_moment))) + if self.nuclear_dipole_moment is not None: + lines.append('~ Nuclear dipole moment (a.u.): {}' + .format(_dipole_to_string(self.nuclear_dipole_moment))) + lines.append('> Dipole moment (a.u.): {} Total: {}' + .format(_dipole_to_string(self.dipole_moment), + _float_to_string(self.total_dipole_moment))) + lines.append(' (debye): {} Total: {}' + .format(_dipole_to_string(self.dipole_moment_in_debye), + _float_to_string(self.total_dipole_moment_in_debye))) + return lines + + +def _dipole_tuple_add(x: Optional[DipoleTuple], + y: Optional[DipoleTuple]) -> Optional[DipoleTuple]: + """ Utility to add two dipole tuples element-wise for dipole additions """ + if x is None or y is None: + return None + return _element_add(x[0], y[0]), _element_add(x[1], y[1]), _element_add(x[2], y[2]) + + +def _element_add(x: Optional[float], y: Optional[float]): + """ Add dipole elements where a value may be None then None is returned """ + return x + y if x is not None and y is not None else None + + +def _dipole_to_string(dipole: DipoleTuple): + dips = [round(x, 8) if x is not None else x for x in dipole] + value = '[' + for i, _ in enumerate(dips): + value += _float_to_string(dips[i]) if dips[i] is not None else 'None' + value += ' ' if i < len(dips)-1 else ']' + return value + + +def _float_to_string(value: Optional[float], precision: int = 8) -> str: + if value is None: + return 'None' + else: + return '0.0' if value == 0 else ('{:.' + str(precision) + 'f}').format(value).rstrip('0') diff --git a/qiskit/chemistry/results/vibronic_structure_result.py b/qiskit/chemistry/results/vibronic_structure_result.py new file mode 100644 index 0000000000..8a40550472 --- /dev/null +++ b/qiskit/chemistry/results/vibronic_structure_result.py @@ -0,0 +1,24 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""The vibronic structure result.""" + +import logging + +from .eigenstate_result import EigenstateResult + +logger = logging.getLogger(__name__) + + +class VibronicStructureResult(EigenstateResult): + """The vibronic structure result.""" + # TODO: just a placeholder to pass lint, needs to be defined diff --git a/qiskit/chemistry/transformations/__init__.py b/qiskit/chemistry/transformations/__init__.py new file mode 100644 index 0000000000..88a613bb9a --- /dev/null +++ b/qiskit/chemistry/transformations/__init__.py @@ -0,0 +1,19 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Qubit operator transformation module.""" + +from .fermionic_transformation import FermionicTransformation +from .transformation import Transformation + +__all__ = ['FermionicTransformation', + 'Transformation'] diff --git a/qiskit/chemistry/transformations/fermionic_transformation.py b/qiskit/chemistry/transformations/fermionic_transformation.py new file mode 100644 index 0000000000..9677cc1e29 --- /dev/null +++ b/qiskit/chemistry/transformations/fermionic_transformation.py @@ -0,0 +1,622 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""This module implements a transformation from a fermionic problem to a qubit operator. + +The problem is described in a driver. +""" + +from functools import partial +from typing import Optional, List, Union, cast, Tuple, Dict, Any, Callable +import logging +from enum import Enum + +import numpy as np +from qiskit.aqua.algorithms import EigensolverResult, MinimumEigensolverResult +from qiskit.aqua.operators import Z2Symmetries, WeightedPauliOperator, OperatorBase +from qiskit.chemistry import QiskitChemistryError, QMolecule +from qiskit.chemistry.fermionic_operator import FermionicOperator +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.results import DipoleTuple, EigenstateResult, ElectronicStructureResult + +from .transformation import Transformation +from ..components.initial_states import HartreeFock + +logger = logging.getLogger(__name__) + + +class TransformationType(Enum): + """ Transformation Type enum """ + FULL = 'full' + PARTICLE_HOLE = 'particle_hole' + + +class QubitMappingType(Enum): + """ QubitMappingType enum """ + JORDAN_WIGNER = 'jordan_wigner' + PARITY = 'parity' + BRAVYI_KITAEV = 'bravyi_kitaev' + + +class FermionicTransformation(Transformation): + """A transformation from a fermionic problem, represented by a driver, to a qubit operator.""" + + def __init__(self, + transformation: TransformationType = TransformationType.FULL, + qubit_mapping: QubitMappingType = QubitMappingType.PARITY, + two_qubit_reduction: bool = True, + freeze_core: bool = False, + orbital_reduction: Optional[List[int]] = None, + z2symmetry_reduction: Optional[Union[str, List[int]]] = None) -> None: + """ + Args: + transformation: full or particle_hole + qubit_mapping: 'jordan_wigner', 'parity' or 'bravyi_kitaev' + two_qubit_reduction: Whether two qubit reduction should be used, + when parity mapping only + freeze_core: Whether to freeze core orbitals when possible + orbital_reduction: Orbital list to be frozen or removed + z2symmetry_reduction: If z2 symmetry reduction should be applied to resulting + qubit operators that are computed. For each symmetry detected the operator will be + split in two where each requires one qubit less for computation. So for example + 3 symmetries will split in the original operator into 8 new operators each + requiring 3 less qubits. Now only one of these operators will have the ground state + and be the correct symmetry sector needed for the ground state. Setting 'auto' will + use an automatic computation of the correct sector. If from other experiments, with + the z2symmetry logic, the sector is known, then the tapering values of that sector + can be provided (a list of int of values -1, and 1). The default is None + meaning no symmetry reduction is done. Note that dipole and other operators + such as spin, num particles etc are also symmetry reduced according to the + symmetries found in the main operator if this operator commutes with the main + operator symmetry. If it does not then the operator will be discarded since no + meaningful measurement can take place. + Raises: + QiskitChemistryError: Invalid symmetry reduction + """ + transformation = transformation.value + orbital_reduction = orbital_reduction if orbital_reduction is not None else [] + super().__init__() + self._transformation = transformation + self._qubit_mapping = qubit_mapping.value + self._two_qubit_reduction = two_qubit_reduction + self._freeze_core = freeze_core + self._orbital_reduction = orbital_reduction + if z2symmetry_reduction is not None: + if isinstance(z2symmetry_reduction, str): + if z2symmetry_reduction != 'auto': + raise QiskitChemistryError('Invalid z2symmetry_reduction value') + self._z2symmetry_reduction = z2symmetry_reduction + self._has_dipole_moments = False + + # Store values that are computed by the classical logic in order + # that later they may be combined with the quantum result + self._hf_energy = None + self._nuclear_repulsion_energy = None + self._nuclear_dipole_moment = None + self._reverse_dipole_sign = None + # The following shifts are from freezing orbitals under orbital reduction + self._energy_shift = 0.0 + self._x_dipole_shift = 0.0 + self._y_dipole_shift = 0.0 + self._z_dipole_shift = 0.0 + # The following shifts are from particle_hole transformation + self._ph_energy_shift = 0.0 + self._ph_x_dipole_shift = 0.0 + self._ph_y_dipole_shift = 0.0 + self._ph_z_dipole_shift = 0.0 + + self._molecule_info: Dict[str, Any] = {} + + @property + def molecule_info(self) -> Dict[str, Any]: + """Getter of the molecule information.""" + return self._molecule_info + + @property + def qubit_mapping(self) -> str: + """Getter of the qubit mapping.""" + return self._qubit_mapping + + def transform(self, driver: BaseDriver, + aux_operators: Optional[List[FermionicOperator]] = None + ) -> Tuple[OperatorBase, List[OperatorBase]]: + """Transformation from the ``driver`` to a qubit operator. + + Args: + driver: A driver encoding the molecule information. + aux_operators: Additional auxiliary ``FermionicOperator``s to evaluate. + + Returns: + A qubit operator and a dictionary of auxiliary operators. + """ + q_molecule = driver.run() + ops, aux_ops = self._do_transform(q_molecule, aux_operators) + + # the internal method may still return legacy operators which is why we make sure to convert + # all of the operator to the operator flow + ops = ops.to_opflow() if isinstance(ops, WeightedPauliOperator) else ops + aux_ops = [a.to_opflow() if isinstance(a, WeightedPauliOperator) else a for a in aux_ops] + + return ops, aux_ops + + def _do_transform(self, qmolecule: QMolecule, + aux_operators: Optional[List[FermionicOperator]] = None + ) -> Tuple[WeightedPauliOperator, List[WeightedPauliOperator]]: + """ + Args: + qmolecule: qmolecule + aux_operators: Additional ``FermionicOperator``s to map to a qubit operator. + + Returns: + (qubit operator, auxiliary operators) + + """ + logger.debug('Processing started...') + # Save these values for later combination with the quantum computation result + self._hf_energy = qmolecule.hf_energy + self._nuclear_repulsion_energy = qmolecule.nuclear_repulsion_energy + self._nuclear_dipole_moment = qmolecule.nuclear_dipole_moment + self._reverse_dipole_sign = qmolecule.reverse_dipole_sign + + core_list = qmolecule.core_orbitals if self._freeze_core else [] + reduce_list = self._orbital_reduction + + if self._freeze_core: + logger.info("Freeze_core specified. Core orbitals to be frozen: %s", core_list) + if reduce_list: + logger.info("Configured orbital reduction list: %s", reduce_list) + reduce_list = [x + qmolecule.num_orbitals if x < 0 else x for x in reduce_list] + + freeze_list = [] + remove_list = [] + + # Orbitals are specified by their index from 0 to n-1, where n is the number of orbitals the + # molecule has. The combined list of the core orbitals, when freeze_core is true, with any + # user supplied orbitals is what will be used. Negative numbers may be used to indicate the + # upper virtual orbitals, so -1 is the highest, then -2 etc. and these will + # be converted to the + # positive 0-based index for computation. + # In the combined list any orbitals that are occupied are added to a freeze list and an + # energy is stored from these orbitals to be added later. + # Unoccupied orbitals are just discarded. + # Because freeze and eliminate is done in separate steps, + # with freeze first, we have to re-base + # the indexes for elimination according to how many orbitals were removed when freezing. + # + orbitals_list = list(set(core_list + reduce_list)) + num_alpha = qmolecule.num_alpha + num_beta = qmolecule.num_beta + new_num_alpha = num_alpha + new_num_beta = num_beta + if orbitals_list: + orbitals_list = np.array(orbitals_list) + orbitals_list = orbitals_list[(cast(np.ndarray, orbitals_list) >= 0) & + (orbitals_list < qmolecule.num_orbitals)] + + freeze_list_alpha = [i for i in orbitals_list if i < num_alpha] + freeze_list_beta = [i for i in orbitals_list if i < num_beta] + freeze_list = np.append(freeze_list_alpha, + [i + qmolecule.num_orbitals for i in freeze_list_beta]) + + remove_list_alpha = [i for i in orbitals_list if i >= num_alpha] + remove_list_beta = [i for i in orbitals_list if i >= num_beta] + rla_adjust = -len(freeze_list_alpha) + rlb_adjust = -len(freeze_list_alpha) - len(freeze_list_beta) + qmolecule.num_orbitals + remove_list = np.append([i + rla_adjust for i in remove_list_alpha], + [i + rlb_adjust for i in remove_list_beta]) + + logger.info("Combined orbital reduction list: %s", orbitals_list) + logger.info(" converting to spin orbital reduction list: %s", + np.append(np.array(orbitals_list), + np.array(orbitals_list) + qmolecule.num_orbitals)) + logger.info(" => freezing spin orbitals: %s", freeze_list) + logger.info(" => removing spin orbitals: %s (indexes accounting for freeze %s)", + np.append(remove_list_alpha, + np.array(remove_list_beta) + qmolecule.num_orbitals), remove_list) + + new_num_alpha -= len(freeze_list_alpha) + new_num_beta -= len(freeze_list_beta) + + new_nel = [new_num_alpha, new_num_beta] + + # construct the fermionic operator + fer_op = FermionicOperator(h1=qmolecule.one_body_integrals, h2=qmolecule.two_body_integrals) + + # try to reduce it according to the freeze and remove list + fer_op, self._energy_shift, did_shift = \ + FermionicTransformation._try_reduce_fermionic_operator(fer_op, freeze_list, remove_list) + # apply same transformation for the aux operators + if aux_operators is not None: + aux_operators = [ + FermionicTransformation._try_reduce_fermionic_operator( + op, freeze_list, remove_list)[0] + for op in aux_operators + ] + + if did_shift: + logger.info("Frozen orbital energy shift: %s", self._energy_shift) + + # apply particle hole transformation, if specified + if self._transformation == TransformationType.PARTICLE_HOLE.value: + fer_op, ph_shift = fer_op.particle_hole_transformation(new_nel) + self._ph_energy_shift = -ph_shift + logger.info("Particle hole energy shift: %s", self._ph_energy_shift) + + # apply the same transformation for the aux operators + if aux_operators is not None: + aux_operators = [ + op.particle_hole_transformation(new_nel)[0] + for op in aux_operators + ] + + logger.debug('Converting to qubit using %s mapping', self._qubit_mapping) + qubit_op = FermionicTransformation._map_fermionic_operator_to_qubit( + fer_op, self._qubit_mapping, new_nel, self._two_qubit_reduction + ) + qubit_op.name = 'Fermionic Operator' + + logger.debug(' num paulis: %s, num qubits: %s', len(qubit_op.paulis), qubit_op.num_qubits) + + aux_ops = [] # list of the aux operators + + def _add_aux_op(aux_op: FermionicOperator, name: str) -> None: + """ + Add auxiliary operators + + Args: + aux_op: auxiliary operators + name: name + + """ + aux_qop = FermionicTransformation._map_fermionic_operator_to_qubit( + aux_op, self._qubit_mapping, new_nel, self._two_qubit_reduction + ) + aux_qop.name = name + + aux_ops.append(aux_qop) + logger.debug(' num paulis: %s', aux_qop.paulis) + + # the first three operators are hardcoded to number of particles, angular momentum + # and magnetization in this order + logger.debug('Creating aux op for Number of Particles') + _add_aux_op(fer_op.total_particle_number(), 'Number of Particles') + logger.debug('Creating aux op for S^2') + _add_aux_op(fer_op.total_angular_momentum(), 'S^2') + logger.debug('Creating aux op for Magnetization') + _add_aux_op(fer_op.total_magnetization(), 'Magnetization') + + # the next three are dipole moments, if supported by the qmolecule + if qmolecule.has_dipole_integrals(): + self._has_dipole_moments = True + + def _dipole_op(dipole_integrals: np.ndarray, axis: str) \ + -> Tuple[WeightedPauliOperator, float, float]: + """ + Dipole operators + + Args: + dipole_integrals: dipole integrals + axis: axis for dipole moment calculation + + Returns: + (qubit_op_, shift, ph_shift_) + """ + logger.debug('Creating aux op for dipole %s', axis) + fer_op_ = FermionicOperator(h1=dipole_integrals) + fer_op_, shift, did_shift_ = self._try_reduce_fermionic_operator(fer_op_, + freeze_list, + remove_list) + if did_shift_: + logger.info("Frozen orbital %s dipole shift: %s", axis, shift) + ph_shift_ = 0.0 + if self._transformation == TransformationType.PARTICLE_HOLE.value: + fer_op_, ph_shift_ = fer_op_.particle_hole_transformation(new_nel) + ph_shift_ = -ph_shift_ + logger.info("Particle hole %s dipole shift: %s", axis, ph_shift_) + qubit_op_ = self._map_fermionic_operator_to_qubit(fer_op_, + self._qubit_mapping, + new_nel, + self._two_qubit_reduction) + qubit_op_.name = 'Dipole ' + axis + logger.debug(' num paulis: %s', len(qubit_op_.paulis)) + return qubit_op_, shift, ph_shift_ + + op_dipole_x, self._x_dipole_shift, self._ph_x_dipole_shift = \ + _dipole_op(qmolecule.x_dipole_integrals, 'x') + op_dipole_y, self._y_dipole_shift, self._ph_y_dipole_shift = \ + _dipole_op(qmolecule.y_dipole_integrals, 'y') + op_dipole_z, self._z_dipole_shift, self._ph_z_dipole_shift = \ + _dipole_op(qmolecule.z_dipole_integrals, 'z') + + aux_ops += [op_dipole_x, op_dipole_y, op_dipole_z] + + # add user specified auxiliary operators + if aux_operators is not None: + for aux_op in aux_operators: + if hasattr(aux_op, 'name'): + name = aux_op.name + else: + name = '' + _add_aux_op(aux_op, name) + + logger.info('Molecule num electrons: %s, remaining for processing: %s', + [num_alpha, num_beta], new_nel) + nspinorbs = qmolecule.num_orbitals * 2 + new_nspinorbs = nspinorbs - len(freeze_list) - len(remove_list) + logger.info('Molecule num spin orbitals: %s, remaining for processing: %s', + nspinorbs, new_nspinorbs) + + self._molecule_info['num_particles'] = [new_num_alpha, new_num_beta] + self._molecule_info['num_orbitals'] = new_nspinorbs + reduction = self._two_qubit_reduction if self._qubit_mapping == 'parity' else False + self._molecule_info['two_qubit_reduction'] = reduction + + z2symmetries = Z2Symmetries([], [], [], None) + if self._z2symmetry_reduction is not None: + logger.debug('Processing z2 symmetries') + qubit_op, aux_ops, z2symmetries = self._process_z2symmetry_reduction(qubit_op, aux_ops) + self._molecule_info['z2_symmetries'] = z2symmetries + + logger.debug('Processing complete ready to run algorithm') + return qubit_op, aux_ops + + def _process_z2symmetry_reduction(self, + qubit_op: WeightedPauliOperator, + aux_ops: List[WeightedPauliOperator]) -> Tuple: + """ + Implement z2 symmetries in the qubit operator + + Args: + qubit_op : qubit operator + aux_ops: auxiliary operators + + Returns: + (z2_qubit_op, z2_aux_ops, z2_symmetries) + + Raises: + QiskitChemistryError: Invalid input + """ + z2_symmetries = Z2Symmetries.find_Z2_symmetries(qubit_op) + if z2_symmetries.is_empty(): + logger.debug('No Z2 symmetries found') + z2_qubit_op = qubit_op + z2_aux_ops = aux_ops + z2_symmetries = Z2Symmetries([], [], [], None) + else: + logger.debug('%s Z2 symmetries found: %s', len(z2_symmetries.symmetries), + ','.join([symm.to_label() for symm in z2_symmetries.symmetries])) + + # Check auxiliary operators commute with main operator's symmetry + logger.debug('Checking operators commute with symmetry:') + symmetry_ops = [] + for symmetry in z2_symmetries.symmetries: + symmetry_ops.append(WeightedPauliOperator(paulis=[[1.0, symmetry]])) + commutes = FermionicTransformation._check_commutes(symmetry_ops, qubit_op) + if not commutes: + raise QiskitChemistryError('Z2 symmetry failure main operator must commute ' + 'with symmetries found from it') + for i, aux_op in enumerate(aux_ops): + commutes = FermionicTransformation._check_commutes(symmetry_ops, aux_op) + if not commutes: + aux_ops[i] = None # Discard since no meaningful measurement can be done + + if self._z2symmetry_reduction == 'auto': + hf_state = HartreeFock(num_orbitals=self._molecule_info['num_orbitals'], + qubit_mapping=self._qubit_mapping, + two_qubit_reduction=self._two_qubit_reduction, + num_particles=self._molecule_info['num_particles']) + z2_symmetries = FermionicTransformation._pick_sector(z2_symmetries, hf_state.bitstr) + else: + if len(self._z2symmetry_reduction) != len(z2_symmetries.symmetries): + raise QiskitChemistryError('z2symmetry_reduction tapering values list has ' + 'invalid length {} should be {}'. + format(len(self._z2symmetry_reduction), + len(z2_symmetries.symmetries))) + valid = np.all(np.isin(self._z2symmetry_reduction, [-1, 1])) + if not valid: + raise QiskitChemistryError('z2symmetry_reduction tapering values list must ' + 'contain -1\'s and/or 1\'s only was {}'. + format(self._z2symmetry_reduction,)) + z2_symmetries.tapering_values = self._z2symmetry_reduction + + logger.debug('Apply symmetry with tapering values %s', z2_symmetries.tapering_values) + chop_to = 0.00000001 # Use same threshold as qubit mapping to chop tapered operator + z2_qubit_op = z2_symmetries.taper(qubit_op).chop(chop_to) + z2_aux_ops = [] + for aux_op in aux_ops: + if aux_op is None: + z2_aux_ops += [None] + else: + z2_aux_ops += [z2_symmetries.taper(aux_op).chop(chop_to)] + + return z2_qubit_op, z2_aux_ops, z2_symmetries + + @staticmethod + def _check_commutes(cliffords: List[WeightedPauliOperator], + operator: WeightedPauliOperator) -> bool: + """ + Check commutations + + Args: + cliffords : cliffords + operator: qubit operator + + Returns: + Boolean: does_commute + """ + commutes = [] + for clifford in cliffords: + commutes.append(operator.commute_with(clifford)) + does_commute = np.all(commutes) + logger.debug(' \'%s\' commutes: %s, %s', operator.name, does_commute, commutes) + return does_commute + + def get_default_filter_criterion(self) -> Optional[Callable[[Union[List, np.ndarray], float, + Optional[List[float]]], bool]]: + """Returns a default filter criterion method to filter the eigenvalues computed by the + eigen solver. For more information see also + aqua.algorithms.eigen_solvers.NumPyEigensolver.filter_criterion. + + In the fermionic case the default filter ensures that the number of particles is being + preserved. + """ + + # pylint: disable=unused-argument + def filter_criterion(self, eigenstate, eigenvalue, aux_values): + # the first aux_value is the evaluated number of particles + num_particles_aux = aux_values[0][0] + return np.isclose(sum(self.molecule_info['num_particles']), num_particles_aux) + + return partial(filter_criterion, self) + + @staticmethod + def _pick_sector(z2_symmetries: Z2Symmetries, hf_str: np.ndarray) -> Z2Symmetries: + """ + Based on Hartree-Fock bit string and found symmetries to determine the sector. + The input z2 symmetries will be mutated with the determined tapering values. + + Args: + z2_symmetries: the z2 symmetries object. + hf_str: Hartree-Fock bit string (the last index is for qubit 0). + + Returns: + the original z2 symmetries filled with the correct tapering values. + """ + # Finding all the symmetries using the find_Z2_symmetries: + taper_coef = [] + for sym in z2_symmetries.symmetries: + # pylint: disable=no-member + coef = -1 if np.logical_xor.reduce(np.logical_and(sym.z[::-1], hf_str)) else 1 + taper_coef.append(coef) + z2_symmetries.tapering_values = taper_coef + return z2_symmetries + + def interpret(self, raw_result: Union[EigenstateResult, EigensolverResult, + MinimumEigensolverResult]) -> ElectronicStructureResult: + """Interprets an EigenstateResult in the context of this transformation. + + Args: + raw_result: an eigenstate result object. + + Returns: + An electronic structure result. + """ + eigenstate_result = None + if isinstance(raw_result, EigenstateResult): + eigenstate_result = raw_result + elif isinstance(raw_result, EigensolverResult): + eigenstate_result = EigenstateResult() + eigenstate_result.raw_result = raw_result + eigenstate_result.eigenenergies = raw_result.eigenvalues + eigenstate_result.eigenstates = raw_result.eigenstates + eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues + elif isinstance(raw_result, MinimumEigensolverResult): + eigenstate_result = EigenstateResult() + eigenstate_result.raw_result = raw_result + eigenstate_result.eigenenergies = np.asarray([raw_result.eigenvalue]) + eigenstate_result.eigenstates = [raw_result.eigenstate] + eigenstate_result.aux_operator_eigenvalues = raw_result.aux_operator_eigenvalues + + result = ElectronicStructureResult(eigenstate_result.data) + result.computed_electronic_energy = eigenstate_result.groundenergy + result.hartree_fock_energy = self._hf_energy + result.nuclear_repulsion_energy = self._nuclear_repulsion_energy + if self._nuclear_dipole_moment is not None: + result.nuclear_dipole_moment = tuple(x for x in self._nuclear_dipole_moment) + result.ph_extracted_energy = self._ph_energy_shift + result.frozen_extracted_energy = self._energy_shift + if result.aux_operator_eigenvalues is not None: + # the first three values are hardcoded to number of particles, angular momentum + # and magnetization in this order + if result.aux_operator_eigenvalues[0] is not None: + result.num_particles = result.aux_operator_eigenvalues[0][0].real + else: + result.num_particles = None + + if result.aux_operator_eigenvalues[1] is not None: + result.total_angular_momentum = result.aux_operator_eigenvalues[1][0].real + else: + result.total_angular_momentum = None + + if result.aux_operator_eigenvalues[2] is not None: + result.magnetization = result.aux_operator_eigenvalues[2][0].real + else: + result.magnetization = None + + # the next three are hardcoded to Dipole moments, if they are set + if len(result.aux_operator_eigenvalues) >= 6 and self._has_dipole_moments: + # check if the names match + # extract dipole moment in each axis + dipole_moment = [] + for moment in result.aux_operator_eigenvalues[3:6]: + if moment is not None: + dipole_moment += [moment[0].real] + else: + dipole_moment += [None] + + result.reverse_dipole_sign = self._reverse_dipole_sign + result.computed_dipole_moment = cast(DipoleTuple, tuple(dipole_moment)) + result.ph_extracted_dipole_moment = (self._ph_x_dipole_shift, + self._ph_y_dipole_shift, + self._ph_z_dipole_shift) + result.frozen_extracted_dipole_moment = (self._x_dipole_shift, + self._y_dipole_shift, + self._z_dipole_shift) + + return result + + @staticmethod + def _try_reduce_fermionic_operator(fer_op: FermionicOperator, + freeze_list: List, + remove_list: List) -> Tuple: + """ + Trying to reduce the fermionic operator w.r.t to freeze and remove list if provided + + Args: + fer_op: fermionic operator + freeze_list: freeze list of orbitals + remove_list: remove list of orbitals + + Returns: + (fermionic_operator, energy_shift, did_shift) + """ + # pylint: disable=len-as-condition + did_shift = False + energy_shift = 0.0 + if len(freeze_list) > 0: + fer_op, energy_shift = fer_op.fermion_mode_freezing(freeze_list) + did_shift = True + if len(remove_list) > 0: + fer_op = fer_op.fermion_mode_elimination(remove_list) + return fer_op, energy_shift, did_shift + + @staticmethod + def _map_fermionic_operator_to_qubit(fer_op: FermionicOperator, + qubit_mapping: str, + num_particles: List[int], + two_qubit_reduction: bool) -> WeightedPauliOperator: + """ + + Args: + fer_op: Fermionic Operator + qubit_mapping: fermionic to qubit mapping + num_particles: number of particles + two_qubit_reduction: two qubit reduction + + Returns: + qubit operator + """ + + qubit_op = fer_op.mapping(map_type=qubit_mapping, threshold=0.00000001) + if qubit_mapping == 'parity' and two_qubit_reduction: + qubit_op = Z2Symmetries.two_qubit_reduction(qubit_op, num_particles) + return qubit_op diff --git a/qiskit/chemistry/transformations/transformation.py b/qiskit/chemistry/transformations/transformation.py new file mode 100644 index 0000000000..6348257606 --- /dev/null +++ b/qiskit/chemistry/transformations/transformation.py @@ -0,0 +1,67 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +"""Base class for transformation to qubit operators for chemistry problems""" + +from abc import ABC, abstractmethod +from typing import Tuple, List, Optional, Union, Callable + +import numpy as np + +from qiskit.aqua.algorithms import EigensolverResult, MinimumEigensolverResult +from qiskit.aqua.operators import OperatorBase +from qiskit.chemistry import FermionicOperator, BosonicOperator +from qiskit.chemistry.drivers import BaseDriver +from qiskit.chemistry.results import EigenstateResult + + +class Transformation(ABC): + """Base class for transformation to qubit operators for chemistry problems""" + + @abstractmethod + def transform(self, driver: BaseDriver, + aux_operators: Optional[Union[List[FermionicOperator], + List[BosonicOperator]]] = None + ) -> Tuple[OperatorBase, List[OperatorBase]]: + """Transformation from the ``driver`` to a qubit operator. + + Args: + driver: A driver encoding the molecule information. + aux_operators: Additional auxiliary operators to evaluate. Must be of type + ``FermionicOperator`` if the qubit transformation is fermionic and of type + ``BosonicOperator`` it is bosonic. + + Returns: + A qubit operator and a dictionary of auxiliary operators. + """ + raise NotImplementedError + + def get_default_filter_criterion(self) -> Optional[Callable[[Union[List, np.ndarray], float, + Optional[List[float]]], bool]]: + """Returns a default filter criterion method to filter the eigenvalues computed by the + eigen solver. For more information see also + aqua.algorithms.eigen_solvers.NumPyEigensolver.filter_criterion. + """ + return None + + @abstractmethod + def interpret(self, raw_result: Union[EigenstateResult, EigensolverResult, + MinimumEigensolverResult]) -> EigenstateResult: + """Interprets an EigenstateResult in the context of this transformation. + + Args: + raw_result: an eigenstate result object. + + Returns: + An "interpreted" eigenstate result. + """ + raise NotImplementedError diff --git a/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml new file mode 100644 index 0000000000..d4862f9cc9 --- /dev/null +++ b/releasenotes/notes/ground_state_interface-42576cb6658a46e0.yaml @@ -0,0 +1,21 @@ +--- +features: + - | + Introduces ``transformations`` for the fermionic and bosonic transformation of a problem + instance. Transforms the fermionic operator to qubit operator. Respective class for the + transformation is ``fermionic_transformation`` + Introduces in algorithms ``ground_state_solvers`` for the calculation of ground state + properties. The calculation can be done either using an ``MinimumEigensolver`` or using + ``AdaptVQE`` + Introduces ``chemistry/results`` where the eigenstate_result and the + electronic_structure_result are also used for the algorithms. + Introduces Minimum Eigensolver factories ``minimum_eigensolver_factories`` where chemistry specific + minimum eigensolvers can be initialised + Introduces orbital optimization vqe ``oovqe`` as a ground state solver for chemistry applications +deprecations: + - | + ``Core Hamiltonian`` class is deprecated in favor of the ``FermionicTransformation`` + ``Chemistry Operator`` class is deprecated in facor of the ``tranformations`` + ``minimum_eigen_solvers/vqe_adapt`` is also deprecate and moved as an implementation + of the ground_state_solver interface + ``applications/molecular_ground_state_energy`` is deprecated in favor of ``ground_state_solver`` \ No newline at end of file diff --git a/test/chemistry/test_adapt_vqe.py b/test/chemistry/test_adapt_vqe.py new file mode 100644 index 0000000000..573dd2bdea --- /dev/null +++ b/test/chemistry/test_adapt_vqe.py @@ -0,0 +1,141 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test of the Adaptive VQE ground state calculations """ +import unittest +from test.chemistry import QiskitChemistryTestCase + +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.providers.basicaer import BasicAer +from qiskit.aqua import QuantumInstance +from qiskit.aqua.algorithms import VQE +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.components.variational_forms import UCCSD +from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.aqua.components.optimizers import L_BFGS_B +from qiskit.chemistry.algorithms.ground_state_solvers import AdaptVQE, VQEUCCSDFactory +from qiskit.chemistry.transformations import FermionicTransformation + + +class TestAdaptVQE(QiskitChemistryTestCase): + """ Test Adaptive VQE Ground State Calculation """ + + def setUp(self): + super().setUp() + + try: + self.driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.735', + unit=UnitsType.ANGSTROM, + basis='sto3g') + except QiskitChemistryError: + self.skipTest('PYSCF driver does not appear to be installed') + return + + self.expected = -1.85727503 + + self.transformation = FermionicTransformation() + + def test_default(self): + """ Default execution """ + solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + calc = AdaptVQE(self.transformation, solver) + res = calc.solve(self.driver) + self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) + + def test_custom_minimum_eigensolver(self): + """ Test custom MES """ + # Note: the VQEUCCSDFactory actually allows to specify an optimizer through its constructor. + # Thus, this example is quite far fetched but for a proof-of-principle test it still works. + class CustomFactory(VQEUCCSDFactory): + """A custom MESFactory""" + + def get_solver(self, transformation): + num_orbitals = transformation.molecule_info['num_orbitals'] + num_particles = transformation.molecule_info['num_particles'] + qubit_mapping = transformation.qubit_mapping + two_qubit_reduction = transformation.molecule_info['two_qubit_reduction'] + z2_symmetries = transformation.molecule_info['z2_symmetries'] + initial_state = HartreeFock(num_orbitals, num_particles, qubit_mapping, + two_qubit_reduction, z2_symmetries.sq_list) + var_form = UCCSD(num_orbitals=num_orbitals, + num_particles=num_particles, + initial_state=initial_state, + qubit_mapping=qubit_mapping, + two_qubit_reduction=two_qubit_reduction, + z2_symmetries=z2_symmetries) + vqe = VQE(var_form=var_form, quantum_instance=self._quantum_instance, + optimizer=L_BFGS_B()) + return vqe + + solver = CustomFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + + calc = AdaptVQE(self.transformation, solver) + res = calc.solve(self.driver) + self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) + + def test_custom_excitation_pool(self): + """ Test custom excitation pool """ + + class CustomFactory(VQEUCCSDFactory): + """A custom MES factory.""" + + def get_solver(self, transformation): + solver = super().get_solver(transformation) + # Here, we can create essentially any custom excitation pool. + # For testing purposes only, we simply select some hopping operator already + # available in the variational form object. + # pylint: disable=no-member + custom_excitation_pool = [solver.var_form._hopping_ops[2]] + solver.var_form.excitation_pool = custom_excitation_pool + return solver + + solver = CustomFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + calc = AdaptVQE(self.transformation, solver) + res = calc.solve(self.driver) + self.assertAlmostEqual(res.electronic_energy, self.expected, places=6) + + def test_vqe_adapt_check_cyclicity(self): + """ VQEAdapt index cycle detection """ + param_list = [ + ([1, 1], True), + ([1, 11], False), + ([11, 1], False), + ([1, 12], False), + ([12, 2], False), + ([1, 1, 1], True), + ([1, 2, 1], False), + ([1, 2, 2], True), + ([1, 2, 21], False), + ([1, 12, 2], False), + ([11, 1, 2], False), + ([1, 2, 1, 1], True), + ([1, 2, 1, 2], True), + ([1, 2, 1, 21], False), + ([11, 2, 1, 2], False), + ([1, 11, 1, 111], False), + ([11, 1, 111, 1], False), + ([1, 2, 3, 1, 2, 3], True), + ([1, 2, 3, 4, 1, 2, 3], False), + ([11, 2, 3, 1, 2, 3], False), + ([1, 2, 3, 1, 2, 31], False), + ([1, 2, 3, 4, 1, 2, 3, 4], True), + ([11, 2, 3, 4, 1, 2, 3, 4], False), + ([1, 2, 3, 4, 1, 2, 3, 41], False), + ([1, 2, 3, 4, 5, 1, 2, 3, 4], False), + ] + for seq, is_cycle in param_list: + with self.subTest(msg="Checking index cyclicity in:", seq=seq): + self.assertEqual(is_cycle, AdaptVQE._check_cyclicity(seq)) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_app_mgse.py b/test/chemistry/test_app_mgse.py index 40ad190274..ed6823210f 100644 --- a/test/chemistry/test_app_mgse.py +++ b/test/chemistry/test_app_mgse.py @@ -12,6 +12,7 @@ """ Test molecular ground state energy application """ +import warnings import unittest from test.chemistry import QiskitChemistryTestCase @@ -53,6 +54,7 @@ def setUp(self): def test_mgse_npme(self): """ Test Molecular Ground State Energy NumPy classical solver """ + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(self.driver, self.npme) result = mgse.compute_energy() self.assertAlmostEqual(result.energy, self.reference_energy, places=5) @@ -71,15 +73,19 @@ def test_mgse_npme(self): self.assertEqual(formatted[14], ' - frozen energy part: [0.0 0.0 0.0]') self.assertEqual(formatted[15], ' - particle hole part: [0.0 0.0 0.0]') self.assertEqual(formatted[18], ' (debye): [0.0 0.0 0.0] Total: 0.') + warnings.filterwarnings('always', category=DeprecationWarning) def test_mgse_vqe(self): """ Test Molecular Ground State Energy VQE solver """ + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(self.driver, self.vqe) result = mgse.compute_energy() self.assertAlmostEqual(result.energy, self.reference_energy, places=5) + warnings.filterwarnings('always', category=DeprecationWarning) def test_mgse_solver(self): """ Test Molecular Ground State Energy setting solver """ + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(self.driver) with self.assertRaises(QiskitChemistryError): _ = mgse.compute_energy() @@ -91,6 +97,7 @@ def test_mgse_solver(self): mgse.solver = self.vqe result = mgse.compute_energy() self.assertAlmostEqual(result.energy, self.reference_energy, places=5) + warnings.filterwarnings('always', category=DeprecationWarning) def test_mgse_callback_ipqe(self): """ Callback test setting up Hartree Fock with IQPE """ @@ -106,9 +113,11 @@ def cb_create_solver(num_particles, num_orbitals, shots=100) return iqpe + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(self.driver) result = mgse.compute_energy(cb_create_solver) np.testing.assert_approx_equal(result.energy, self.reference_energy, significant=2) + warnings.filterwarnings('always', category=DeprecationWarning) def test_mgse_callback_vqe_uccsd(self): """ Callback test setting up Hartree Fock with UCCSD and VQE """ @@ -127,12 +136,15 @@ def cb_create_solver(num_particles, num_orbitals, vqe.quantum_instance = BasicAer.get_backend('statevector_simulator') return vqe + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(self.driver) result = mgse.compute_energy(cb_create_solver) self.assertAlmostEqual(result.energy, self.reference_energy, places=5) + warnings.filterwarnings('always', category=DeprecationWarning) def test_mgse_callback(self): """ Callback testing """ + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(self.driver) result = mgse.compute_energy(lambda *args: NumPyMinimumEigensolver()) @@ -140,9 +152,11 @@ def test_mgse_callback(self): result = mgse.compute_energy(lambda *args: self.vqe) self.assertAlmostEqual(result.energy, self.reference_energy, places=5) + warnings.filterwarnings('always', category=DeprecationWarning) def test_mgse_default_solver(self): """ Callback testing using default solver """ + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(self.driver) result = mgse.compute_energy(mgse.get_default_solver( @@ -152,6 +166,7 @@ def test_mgse_default_solver(self): q_inst = QuantumInstance(BasicAer.get_backend('statevector_simulator')) result = mgse.compute_energy(mgse.get_default_solver(q_inst)) self.assertAlmostEqual(result.energy, self.reference_energy, places=5) + warnings.filterwarnings('always', category=DeprecationWarning) def test_mgse_callback_vqe_uccsd_z2(self): """ Callback test setting up Hartree Fock with UCCSD and VQE, plus z2 symmetries """ @@ -171,11 +186,13 @@ def cb_create_solver(num_particles, num_orbitals, return vqe driver = PySCFDriver(atom='Li .0 .0 -0.8; H .0 .0 0.8') + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(driver, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=True, z2symmetry_reduction='auto') result = mgse.compute_energy(cb_create_solver) self.assertAlmostEqual(result.energy, -7.882, places=3) + warnings.filterwarnings('always', category=DeprecationWarning) def test_mgse_callback_vqe_uccsd_z2_nosymm(self): """ This time we reduce the operator so it has symmetries left. Whether z2 symmetry @@ -193,6 +210,7 @@ def cb_create_solver(num_particles, num_orbitals, return NumPyMinimumEigensolver() driver = PySCFDriver(atom='Li .0 .0 -0.8; H .0 .0 0.8') + warnings.filterwarnings('ignore', category=DeprecationWarning) mgse = MolecularGroundStateEnergy(driver, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=True, orbital_reduction=[-3, -2], @@ -215,6 +233,7 @@ def cb_create_solver(num_particles, num_orbitals, self.assertEqual(z2_symm.is_empty(), True) self.assertEqual(str(result), str(result1)) # Compare string form of results + warnings.filterwarnings('always', category=DeprecationWarning) if __name__ == '__main__': diff --git a/test/chemistry/test_chc_vscf.py b/test/chemistry/test_chc_vscf.py index 9939432aef..7bdfa9f421 100644 --- a/test/chemistry/test_chc_vscf.py +++ b/test/chemistry/test_chc_vscf.py @@ -12,6 +12,8 @@ """ Test of CHC and VSCF Aqua extensions """ +import unittest + from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer @@ -78,3 +80,7 @@ def test_chc_vscf(self): energy = vqe_result['optimal_value'] self.assertAlmostEqual(energy, self.reference_energy, places=4) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_core_hamiltonian.py b/test/chemistry/test_core_hamiltonian.py index 73498c7987..227ffa3f9c 100644 --- a/test/chemistry/test_core_hamiltonian.py +++ b/test/chemistry/test_core_hamiltonian.py @@ -12,6 +12,7 @@ """ Test Core Hamiltonian """ +import warnings import unittest from test.chemistry import QiskitChemistryTestCase @@ -58,11 +59,13 @@ def _validate_input_object(self, qubit_op, num_qubits=4, num_paulis=15): def test_output(self): """ output test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) self._validate_info(core, actual_two_qubit_reduction=True) @@ -70,11 +73,13 @@ def test_output(self): def test_jordan_wigner(self): """ jordan wigner test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) self._validate_info(core) @@ -82,11 +87,13 @@ def test_jordan_wigner(self): def test_jordan_wigner_2q(self): """ jordan wigner 2q test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=True, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) # Reported effective 2 qubit reduction should be false @@ -95,11 +102,13 @@ def test_jordan_wigner_2q(self): def test_parity(self): """ parity test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) self._validate_info(core) @@ -107,11 +116,13 @@ def test_parity(self): def test_bravyi_kitaev(self): """ bravyi kitaev test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.BRAVYI_KITAEV, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) self._validate_info(core) @@ -119,11 +130,13 @@ def test_bravyi_kitaev(self): def test_particle_hole(self): """ particle hole test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.PARTICLE_HOLE, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core, ph_energy_shift=-1.83696799) self._validate_info(core) @@ -131,11 +144,13 @@ def test_particle_hole(self): def test_freeze_core(self): """ freeze core test -- Should be in effect a no-op for H2 """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=True, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) self._validate_info(core) @@ -145,11 +160,13 @@ def test_orbital_reduction(self): """ orbital reduction test --- Remove virtual orbital just for test purposes (not sensible!) """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[-1]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) self._validate_info(core, num_orbitals=2) diff --git a/test/chemistry/test_core_hamiltonian_orb_reduce.py b/test/chemistry/test_core_hamiltonian_orb_reduce.py index 7254b40361..8573c43d61 100644 --- a/test/chemistry/test_core_hamiltonian_orb_reduce.py +++ b/test/chemistry/test_core_hamiltonian_orb_reduce.py @@ -12,6 +12,7 @@ """ Test Core Hamiltonian Orb Reduce """ +import warnings import unittest from test.chemistry import QiskitChemistryTestCase @@ -58,11 +59,13 @@ def _validate_input_object(self, qubit_op, num_qubits=12, num_paulis=631): def test_output(self): """ output test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) self._validate_info(core) @@ -70,11 +73,13 @@ def test_output(self): def test_parity(self): """ parity test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core) self._validate_info(core, actual_two_qubit_reduction=True) @@ -82,11 +87,13 @@ def test_parity(self): def test_freeze_core(self): """ freeze core test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=False, freeze_core=True, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core, energy_shift=-7.7962196) self._validate_info(core, num_particles=[1, 1], num_orbitals=10) @@ -94,11 +101,13 @@ def test_freeze_core(self): def test_freeze_core_orb_reduction(self): """ freeze core orb reduction test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=False, freeze_core=True, orbital_reduction=[-3, -2]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core, energy_shift=-7.7962196) self._validate_info(core, num_particles=[1, 1], num_orbitals=6) @@ -106,11 +115,13 @@ def test_freeze_core_orb_reduction(self): def test_freeze_core_all_reduction(self): """ freeze core all reduction test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=True, orbital_reduction=[-3, -2]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core, energy_shift=-7.7962196) self._validate_info(core, num_particles=[1, 1], num_orbitals=6, @@ -119,11 +130,13 @@ def test_freeze_core_all_reduction(self): def test_freeze_core_all_reduction_ph(self): """ freeze core all reduction ph test """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.PARTICLE_HOLE, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=True, orbital_reduction=[-2, -1]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.qmolecule) self._validate_vars(core, energy_shift=-7.7962196, ph_energy_shift=-1.05785247) self._validate_info(core, num_particles=[1, 1], num_orbitals=6, diff --git a/test/chemistry/test_core_hamiltonian_symmetries.py b/test/chemistry/test_core_hamiltonian_symmetries.py index a6b355321c..ef56f1af30 100644 --- a/test/chemistry/test_core_hamiltonian_symmetries.py +++ b/test/chemistry/test_core_hamiltonian_symmetries.py @@ -12,6 +12,7 @@ """ Test Core Hamiltonian Symmetry Reduction """ +import warnings import unittest from test.chemistry import QiskitChemistryTestCase import numpy as np @@ -53,138 +54,172 @@ def _validate_result(self, result, symm=True): def test_no_symmetry(self): """ No symmetry reduction """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=None, z2symmetry_reduction=None) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, aux_ops = core.run(self.qmolecule) self.assertEqual(qubit_op.num_qubits, 12) npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) self._validate_result(result, False) def test_auto_symmetry(self): """ Auto symmetry reduction """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=None, z2symmetry_reduction='auto') + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, aux_ops = core.run(self.qmolecule) self.assertEqual(qubit_op.num_qubits, 8) npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) self._validate_result(result) self.assertEqual(core.molecule_info[core.INFO_Z2SYMMETRIES].tapering_values, [1, 1, 1, 1]) def test_given_symmetry(self): """ Supplied symmetry reduction """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=None, z2symmetry_reduction=[1, 1, 1, 1]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, aux_ops = core.run(self.qmolecule) self.assertEqual(qubit_op.num_qubits, 8) npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) self._validate_result(result) self.assertEqual(core.molecule_info[core.INFO_Z2SYMMETRIES].tapering_values, [1, 1, 1, 1]) def test_given_symmetry_fail_len(self): """ Supplied symmetry reduction invalid len """ with self.assertRaises(QiskitChemistryError): + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=None, z2symmetry_reduction=[1, 1, 1]) + warnings.filterwarnings('always', category=DeprecationWarning) _, _ = core.run(self.qmolecule) def test_given_symmetry_fail_values(self): """ Supplied symmetry reduction invalid values """ with self.assertRaises(QiskitChemistryError): + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=False, orbital_reduction=None, z2symmetry_reduction=[1, 0, 1, 1]) + warnings.filterwarnings('always', category=DeprecationWarning) _, _ = core.run(self.qmolecule) def test_auto_symmetry_freeze_core(self): """ Auto symmetry reduction, with freeze core """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=True, orbital_reduction=None, z2symmetry_reduction='auto') + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, aux_ops = core.run(self.qmolecule) self.assertEqual(qubit_op.num_qubits, 6) npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) self._validate_result(result) self.assertEqual(core.molecule_info[core.INFO_Z2SYMMETRIES].tapering_values, [-1, 1, 1, -1]) def test_auto_freeze_core_parity(self): """ Auto symmetry reduction, with freeze core and parity mapping """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=False, freeze_core=True, orbital_reduction=None, z2symmetry_reduction='auto') + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, aux_ops = core.run(self.qmolecule) self.assertEqual(qubit_op.num_qubits, 6) npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) self._validate_result(result) self.assertEqual(core.molecule_info[core.INFO_Z2SYMMETRIES].tapering_values, [-1, 1, 1, 1]) def test_auto_freeze_core_parity_2(self): """ Auto symmetry reduction, with freeze core, parity and two q reduction """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=True, orbital_reduction=None, z2symmetry_reduction='auto') + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, aux_ops = core.run(self.qmolecule) self.assertEqual(qubit_op.num_qubits, 6) npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) self._validate_result(result) self.assertEqual(core.molecule_info[core.INFO_Z2SYMMETRIES].tapering_values, [1, 1]) def test_auto_ph_freeze_core_parity_2(self): """ Auto symmetry reduction, with freeze core, parity and two q reduction """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.PARTICLE_HOLE, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=True, orbital_reduction=None, z2symmetry_reduction='auto') + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, aux_ops = core.run(self.qmolecule) self.assertEqual(qubit_op.num_qubits, 6) npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) + warnings.filterwarnings('ignore', category=DeprecationWarning) result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) self._validate_result(result) self.assertEqual(core.molecule_info[core.INFO_Z2SYMMETRIES].tapering_values, [1, 1]) def test_vqe_auto_symmetry_freeze_core(self): """ Auto symmetry reduction, with freeze core using VQE """ + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=True, orbital_reduction=None, z2symmetry_reduction='auto') + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, aux_ops = core.run(self.qmolecule) self.assertEqual(qubit_op.num_qubits, 6) num_orbitals = core.molecule_info[core.INFO_NUM_ORBITALS] @@ -202,7 +237,9 @@ def test_vqe_auto_symmetry_freeze_core(self): z2_symmetries=z2_symmetries) vqe = VQE(qubit_op, var_form=var_form, optimizer=SLSQP(maxiter=500), aux_operators=aux_ops) vqe.quantum_instance = BasicAer.get_backend('statevector_simulator') + warnings.filterwarnings('ignore', category=DeprecationWarning) result = core.process_algorithm_result(vqe.compute_minimum_eigenvalue()) + warnings.filterwarnings('always', category=DeprecationWarning) self._validate_result(result) self.assertEqual(core.molecule_info[core.INFO_Z2SYMMETRIES].tapering_values, [-1, 1, 1, -1]) diff --git a/test/chemistry/test_driver_methods_fcidump.py b/test/chemistry/test_driver_methods_fcidump.py index cb82532237..f2451bdb3b 100644 --- a/test/chemistry/test_driver_methods_fcidump.py +++ b/test/chemistry/test_driver_methods_fcidump.py @@ -12,8 +12,10 @@ """ Test Driver Methods FCIDump """ +import unittest + from test.chemistry import QiskitChemistryTestCase -from test.chemistry.test_driver_methods import TestDriverMethods +from test.chemistry.test_driver_methods_gsc import TestDriverMethods from qiskit.chemistry.drivers import FCIDumpDriver @@ -82,3 +84,7 @@ def test_qmolecule_log_with_atoms(self): atoms=['H', 'H']).run() with self.assertLogs('qiskit.chemistry', level='DEBUG') as _: qmolecule.log() + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_driver_methods_gaussian.py b/test/chemistry/test_driver_methods_gaussian.py index 87baeb4176..167710e8d3 100644 --- a/test/chemistry/test_driver_methods_gaussian.py +++ b/test/chemistry/test_driver_methods_gaussian.py @@ -12,7 +12,9 @@ """ Test Driver Methods Gaussian """ -from test.chemistry.test_driver_methods import TestDriverMethods +import unittest + +from test.chemistry.test_driver_methods_gsc import TestDriverMethods from qiskit.chemistry.drivers import GaussianDriver from qiskit.chemistry import QiskitChemistryError @@ -78,3 +80,7 @@ def test_oh_uhf(self): driver = GaussianDriver(config=self.g16_oh_config.format('uhf')) result = self._run_driver(driver) self._assert_energy_and_dipole(result, 'oh') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_driver_methods.py b/test/chemistry/test_driver_methods_gsc.py similarity index 66% rename from test/chemistry/test_driver_methods.py rename to test/chemistry/test_driver_methods_gsc.py index 907736babd..f10dfd9657 100644 --- a/test/chemistry/test_driver_methods.py +++ b/test/chemistry/test_driver_methods_gsc.py @@ -12,8 +12,12 @@ """ Test Driver Methods """ +import unittest + from test.chemistry import QiskitChemistryTestCase -from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.transformations import FermionicTransformation +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver from qiskit.aqua.algorithms import NumPyMinimumEigensolver @@ -37,18 +41,19 @@ def setUp(self): def _run_driver(driver, transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=False, freeze_core=True): - qmolecule = driver.run() - core = Hamiltonian(transformation=transformation, - qubit_mapping=qubit_mapping, - two_qubit_reduction=two_qubit_reduction, - freeze_core=freeze_core, - orbital_reduction=[]) + fermionic_transformation = \ + FermionicTransformation(transformation=transformation, + qubit_mapping=qubit_mapping, + two_qubit_reduction=two_qubit_reduction, + freeze_core=freeze_core, + orbital_reduction=[]) + + solver = NumPyMinimumEigensolver() - qubit_op, aux_ops = core.run(qmolecule) + gsc = GroundStateEigensolver(fermionic_transformation, solver) - npme = NumPyMinimumEigensolver(qubit_op, aux_operators=aux_ops) - result = core.process_algorithm_result(npme.compute_minimum_eigenvalue()) + result = gsc.solve(driver) return result def _assert_energy(self, result, mol): @@ -57,3 +62,7 @@ def _assert_energy(self, result, mol): def _assert_energy_and_dipole(self, result, mol): self._assert_energy(result, mol) self.assertAlmostEqual(self.ref_dipoles[mol], result.total_dipole_moment, places=3) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_driver_methods_psi4.py b/test/chemistry/test_driver_methods_psi4.py index ab638f1b1a..db52bb7e50 100644 --- a/test/chemistry/test_driver_methods_psi4.py +++ b/test/chemistry/test_driver_methods_psi4.py @@ -12,7 +12,9 @@ """ Test Driver Methods PSI4 """ -from test.chemistry.test_driver_methods import TestDriverMethods +import unittest + +from test.chemistry.test_driver_methods_gsc import TestDriverMethods from qiskit.chemistry.drivers import PSI4Driver from qiskit.chemistry import QiskitChemistryError @@ -84,3 +86,7 @@ def test_oh_uhf(self): driver = PSI4Driver(config=self.psi4_oh_config.format('uhf')) result = self._run_driver(driver) self._assert_energy_and_dipole(result, 'oh') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_driver_methods_pyquante.py b/test/chemistry/test_driver_methods_pyquante.py index 932613ffb7..d7dde77b0e 100644 --- a/test/chemistry/test_driver_methods_pyquante.py +++ b/test/chemistry/test_driver_methods_pyquante.py @@ -12,7 +12,9 @@ """ Test Driver Methods Pyquante """ -from test.chemistry.test_driver_methods import TestDriverMethods +import unittest + +from test.chemistry.test_driver_methods_gsc import TestDriverMethods from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.drivers import PyQuanteDriver, UnitsType, BasisType, HFMethodType @@ -66,3 +68,7 @@ def test_oh_uhf(self): hf_method=HFMethodType.UHF) result = self._run_driver(driver) self._assert_energy(result, 'oh') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_driver_methods_pyscf.py b/test/chemistry/test_driver_methods_pyscf.py index c0d980f9d0..5535b29ccc 100644 --- a/test/chemistry/test_driver_methods_pyscf.py +++ b/test/chemistry/test_driver_methods_pyscf.py @@ -12,7 +12,9 @@ """ Test Driver Methods PySCF """ -from test.chemistry.test_driver_methods import TestDriverMethods +import unittest + +from test.chemistry.test_driver_methods_gsc import TestDriverMethods from qiskit.chemistry.drivers import PySCFDriver, UnitsType, HFMethodType from qiskit.chemistry.core import TransformationType, QubitMappingType from qiskit.chemistry import QiskitChemistryError @@ -148,3 +150,7 @@ def test_oh_uhf_bk(self): result = self._run_driver(driver, transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.BRAVYI_KITAEV) self._assert_energy_and_dipole(result, 'oh') + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_end2end_with_vqe.py b/test/chemistry/test_end2end_with_vqe.py index 81dd39f20e..13cdae6a9a 100644 --- a/test/chemistry/test_end2end_with_vqe.py +++ b/test/chemistry/test_end2end_with_vqe.py @@ -35,11 +35,13 @@ def setUp(self): driver = HDF5Driver(hdf5_input=self.get_resource_path('test_driver_hdf5.hdf5')) self.qmolecule = driver.run() + warnings.filterwarnings('ignore', category=DeprecationWarning) self.core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) self.qubit_op, self.aux_ops = self.core.run(self.qmolecule) self.reference_energy = -1.857275027031588 @@ -65,7 +67,6 @@ def test_end2end_h2(self, name, optimizer, backend, shots): quantum_instance = QuantumInstance(backend, shots=shots) result = vqe.run(quantum_instance) self.assertAlmostEqual(result.eigenvalue.real, self.reference_energy, places=4) - # TODO test aux_ops properly def test_deprecated_algo_result(self): """ Test processing a deprecated dictionary result from algorithm """ diff --git a/test/chemistry/test_fermionic_transformation.py b/test/chemistry/test_fermionic_transformation.py new file mode 100644 index 0000000000..0c5345f826 --- /dev/null +++ b/test/chemistry/test_fermionic_transformation.py @@ -0,0 +1,187 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test Fermionic Transformation """ + +import unittest + +from test.chemistry import QiskitChemistryTestCase +from qiskit.aqua.operators import OperatorBase, I, Z +from qiskit.chemistry import QiskitChemistryError, FermionicOperator +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.chemistry.transformations import FermionicTransformation + + +class TestFermionicTransformation(QiskitChemistryTestCase): + """Fermionic Transformation tests.""" + + def setUp(self): + super().setUp() + try: + driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.735', + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='sto3g') + except QiskitChemistryError: + self.skipTest('PYSCF driver does not appear to be installed') + self.driver = driver + + def _validate_vars(self, fermionic_transformation, energy_shift=0.0, ph_energy_shift=0.0): + self.assertAlmostEqual(fermionic_transformation._hf_energy, -1.117, places=3) + self.assertAlmostEqual(fermionic_transformation._energy_shift, energy_shift) + self.assertAlmostEqual(fermionic_transformation._ph_energy_shift, ph_energy_shift) + + def _validate_info(self, fermionic_transformation, num_particles=None, + num_orbitals=4, actual_two_qubit_reduction=False): + num_particles = num_particles if num_particles is not None else [1, 1] + z2symmetries = fermionic_transformation.molecule_info.pop('z2_symmetries') + self.assertEqual(z2symmetries.is_empty(), True) + self.assertEqual(fermionic_transformation.molecule_info, + {'num_particles': num_particles, + 'num_orbitals': num_orbitals, + 'two_qubit_reduction': actual_two_qubit_reduction}) + + def _validate_input_object(self, qubit_op, num_qubits=4, num_paulis=15): + self.assertTrue(isinstance(qubit_op, OperatorBase)) + self.assertIsNotNone(qubit_op) + self.assertEqual(qubit_op.num_qubits, num_qubits) + self.assertEqual(len(qubit_op.oplist), num_paulis) + + def test_output(self): + """ output test """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation) + self._validate_info(fermionic_transformation, actual_two_qubit_reduction=True) + self._validate_input_object(qubit_op, num_qubits=2, num_paulis=5) + + def test_jordan_wigner(self): + """ jordan wigner test """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation) + self._validate_info(fermionic_transformation) + self._validate_input_object(qubit_op) + + def test_jordan_wigner_2q(self): + """ jordan wigner 2q test """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=True, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation) + # Reported effective 2 qubit reduction should be false + self._validate_info(fermionic_transformation, actual_two_qubit_reduction=False) + self._validate_input_object(qubit_op) + + def test_parity(self): + """ parity test """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation) + self._validate_info(fermionic_transformation) + self._validate_input_object(qubit_op) + + def test_bravyi_kitaev(self): + """ bravyi kitaev test """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.BRAVYI_KITAEV, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation) + self._validate_info(fermionic_transformation) + self._validate_input_object(qubit_op) + + def test_particle_hole(self): + """ particle hole test """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.PARTICLE_HOLE, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation, ph_energy_shift=-1.83696799) + self._validate_info(fermionic_transformation) + self._validate_input_object(qubit_op) + + def test_freeze_core(self): + """ freeze core test -- Should be in effect a no-op for H2 """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation) + self._validate_info(fermionic_transformation) + self._validate_input_object(qubit_op) + + def test_orbital_reduction(self): + """ orbital reduction test --- Remove virtual orbital just + for test purposes (not sensible!) + """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[-1]) + + # get dummy aux operator + qmolecule = self.driver.run() + fer_op = FermionicOperator(h1=qmolecule.one_body_integrals, h2=qmolecule.two_body_integrals) + dummy = fer_op.total_particle_number() + expected = (I ^ I) - 0.5 * (I ^ Z) - 0.5 * (Z ^ I) + + qubit_op, aux_ops = fermionic_transformation.transform(self.driver, [dummy]) + self._validate_vars(fermionic_transformation) + self._validate_info(fermionic_transformation, num_orbitals=2) + self._validate_input_object(qubit_op, num_qubits=2, num_paulis=4) + + # the first six aux_ops are added automatically, ours is the 7th one + self.assertEqual(aux_ops[6], expected) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_fermionic_transformation_orb_reduce.py b/test/chemistry/test_fermionic_transformation_orb_reduce.py new file mode 100644 index 0000000000..8a95edea1f --- /dev/null +++ b/test/chemistry/test_fermionic_transformation_orb_reduce.py @@ -0,0 +1,149 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2018, 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test Core Hamiltonian Orb Reduce """ + +import unittest + +from test.chemistry import QiskitChemistryTestCase +from qiskit.aqua.operators import OperatorBase +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.transformations import FermionicTransformation + + +class TestFermionicTransformationOrbReduce(QiskitChemistryTestCase): + """core/hamiltonian Driver tests.""" + + def setUp(self): + super().setUp() + try: + self.driver = PySCFDriver(atom='Li .0 .0 -0.8; H .0 .0 0.8', + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='sto3g') + except QiskitChemistryError: + self.skipTest('PYSCF driver does not appear to be installed') + + def _validate_vars(self, fermionic_transformation, energy_shift=0.0, ph_energy_shift=0.0): + self.assertAlmostEqual(fermionic_transformation._hf_energy, -7.862, places=3) + self.assertAlmostEqual(fermionic_transformation._energy_shift, energy_shift) + self.assertAlmostEqual(fermionic_transformation._ph_energy_shift, ph_energy_shift) + + def _validate_info(self, fermionic_transformation, num_particles=None, + num_orbitals=12, actual_two_qubit_reduction=False): + num_particles = num_particles if num_particles is not None else [2, 2] + z2symmetries = fermionic_transformation.molecule_info.pop('z2_symmetries') + self.assertEqual(z2symmetries.is_empty(), True) + self.assertEqual(fermionic_transformation.molecule_info, + {'num_particles': num_particles, + 'num_orbitals': num_orbitals, + 'two_qubit_reduction': actual_two_qubit_reduction}) + + def _validate_input_object(self, qubit_op, num_qubits=12, num_paulis=631): + self.assertTrue(isinstance(qubit_op, OperatorBase)) + self.assertIsNotNone(qubit_op) + self.assertEqual(qubit_op.num_qubits, num_qubits) + self.assertEqual(len(qubit_op.oplist), num_paulis) + + def test_output(self): + """ output test """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.JORDAN_WIGNER, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation) + self._validate_info(fermionic_transformation) + self._validate_input_object(qubit_op) + + def test_parity(self): + """ parity test """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation) + self._validate_info(fermionic_transformation, actual_two_qubit_reduction=True) + self._validate_input_object(qubit_op, num_qubits=10) + + def test_freeze_core(self): + """ freeze core test """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation, energy_shift=-7.7962196) + self._validate_info(fermionic_transformation, num_particles=[1, 1], num_orbitals=10) + self._validate_input_object(qubit_op, num_qubits=10, num_paulis=276) + + def test_freeze_core_orb_reduction(self): + """ freeze core orb reduction test """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False, + freeze_core=True, + orbital_reduction=[-3, -2]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation, energy_shift=-7.7962196) + self._validate_info(fermionic_transformation, num_particles=[1, 1], num_orbitals=6) + self._validate_input_object(qubit_op, num_qubits=6, num_paulis=118) + + def test_freeze_core_all_reduction(self): + """ freeze core all reduction test """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=[-3, -2]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation, energy_shift=-7.7962196) + self._validate_info(fermionic_transformation, num_particles=[1, 1], num_orbitals=6, + actual_two_qubit_reduction=True) + self._validate_input_object(qubit_op, num_qubits=4, num_paulis=100) + + def test_freeze_core_all_reduction_ph(self): + """ freeze core all reduction ph test """ + fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.PARTICLE_HOLE, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=[-2, -1]) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + self._validate_vars(fermionic_transformation, energy_shift=-7.7962196, + ph_energy_shift=-1.05785247) + self._validate_info(fermionic_transformation, num_particles=[1, 1], num_orbitals=6, + actual_two_qubit_reduction=True) + self._validate_input_object(qubit_op, num_qubits=4, num_paulis=52) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_initial_state_hartree_fock.py b/test/chemistry/test_initial_state_hartree_fock.py index 055b634466..72b59dfaeb 100644 --- a/test/chemistry/test_initial_state_hartree_fock.py +++ b/test/chemistry/test_initial_state_hartree_fock.py @@ -14,13 +14,15 @@ import unittest from test.chemistry import QiskitChemistryTestCase + import numpy as np from ddt import ddt, idata, unpack from qiskit.chemistry.components.initial_states import HartreeFock -from qiskit.aqua.operators.legacy import op_converter +from qiskit.aqua.operators import StateFn from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.drivers import PySCFDriver, UnitsType -from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.transformations import FermionicTransformation @ddt @@ -93,21 +95,25 @@ def test_hf_value(self, mapping): basis='sto3g') except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') - qmolecule = driver.run() - core = Hamiltonian(transformation=TransformationType.FULL, - qubit_mapping=mapping, - two_qubit_reduction=False, - freeze_core=False, - orbital_reduction=[]) - - qubit_op, _ = core.run(qmolecule) - qubit_op = op_converter.to_matrix_operator(qubit_op) - hrfo = HartreeFock(core.molecule_info['num_orbitals'], - core.molecule_info['num_particles'], mapping.value, False) + + fermionic_transformation = FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=mapping, + two_qubit_reduction=False, + freeze_core=False, + orbital_reduction=[]) + + qubit_op, _ = fermionic_transformation.transform(driver) + + hrfo = HartreeFock(fermionic_transformation.molecule_info['num_orbitals'], + fermionic_transformation.molecule_info['num_particles'], + mapping.value, + two_qubit_reduction=False) qc = hrfo.construct_circuit('vector') - hf_energy = qubit_op.evaluate_with_statevector(qc)[0].real + core._nuclear_repulsion_energy + exp = ~StateFn(qubit_op) @ StateFn(qc) + hf_energy = exp.eval().real \ + + fermionic_transformation._nuclear_repulsion_energy - self.assertAlmostEqual(qmolecule.hf_energy, hf_energy, places=8) + self.assertAlmostEqual(fermionic_transformation._hf_energy, hf_energy, places=6) if __name__ == '__main__': diff --git a/test/chemistry/test_mes_gsc_calculation.py b/test/chemistry/test_mes_gsc_calculation.py new file mode 100644 index 0000000000..ec64b030fd --- /dev/null +++ b/test/chemistry/test_mes_gsc_calculation.py @@ -0,0 +1,119 @@ +# This code is part of Qiskit. +# +# (C) Copyright IBM 2020. +# +# This code is licensed under the Apache License, Version 2.0. You may +# obtain a copy of this license in the LICENSE.txt file in the root directory +# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. +# +# Any modifications or derivative works of this code must retain this +# copyright notice, and modified files need to carry a notice indicating +# that they have been altered from the originals. + +""" Test MinimumEigensovler ground state calculation """ + +import unittest + +from test.chemistry import QiskitChemistryTestCase + +from qiskit import BasicAer +from qiskit.aqua import QuantumInstance +from qiskit.chemistry import QiskitChemistryError +from qiskit.chemistry.drivers import PySCFDriver, UnitsType +from qiskit.chemistry.transformations import FermionicTransformation +from qiskit.chemistry.transformations.fermionic_transformation import QubitMappingType +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver +from qiskit.chemistry.algorithms.ground_state_solvers.minimum_eigensolver_factories import \ + (VQEUCCSDFactory, NumPyMinimumEigensolverFactory) + + +class TestMESGSCCalculation(QiskitChemistryTestCase): + """ Test MinimumEigensovler ground state calculation """ + + def setUp(self): + super().setUp() + try: + self.driver = PySCFDriver(atom='H .0 .0 .0; H .0 .0 0.735', + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='sto3g') + except QiskitChemistryError: + self.skipTest('PYSCF driver does not appear to be installed') + + self.reference_energy = -1.137306 + + self.transformation = FermionicTransformation(qubit_mapping=QubitMappingType.JORDAN_WIGNER) + + def test_npme(self): + """ Test NumPyMinimumEigensolver """ + solver = NumPyMinimumEigensolverFactory() + calc = GroundStateEigensolver(self.transformation, solver) + res = calc.solve(self.driver) + self.assertAlmostEqual(res.energy, self.reference_energy, places=6) + + def test_vqe_uccsd(self): + """ Test VQE UCCSD case """ + solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + calc = GroundStateEigensolver(self.transformation, solver) + res = calc.solve(self.driver) + self.assertAlmostEqual(res.energy, self.reference_energy, places=6) + + def _setup_evaluation_operators(self): + # first we run a ground state calculation + solver = VQEUCCSDFactory(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) + calc = GroundStateEigensolver(self.transformation, solver) + res = calc.solve(self.driver) + + # now we decide that we want to evaluate another operator + # for testing simplicity, we just use some pre-constructed auxiliary operators + _, aux_ops = self.transformation.transform(self.driver) + return calc, res, aux_ops + + def test_eval_op_single(self): + """ Test evaluating a single additional operator """ + calc, res, aux_ops = self._setup_evaluation_operators() + # we filter the list because in this test we test a single operator evaluation + add_aux_op = aux_ops[0][0] + + # now we have the ground state calculation evaluate it + add_aux_op_res = calc.evaluate_operators(res.raw_result.eigenstate, add_aux_op) + self.assertIsInstance(add_aux_op_res[0], complex) + self.assertAlmostEqual(add_aux_op_res[0].real, 2, places=6) + + def test_eval_op_list(self): + """ Test evaluating a list of additional operator """ + calc, res, aux_ops = self._setup_evaluation_operators() + # we filter the list because of simplicity + expected_results = {'number of particles': 2, + 's^2': 0, + 'magnetization': 0} + add_aux_op = aux_ops[0:3] + + # now we have the ground state calculation evaluate them + add_aux_op_res = calc.evaluate_operators(res.raw_result.eigenstate, add_aux_op) + self.assertIsInstance(add_aux_op_res, list) + # in this list we require that the order of the results remains unchanged + for idx, expected in enumerate(expected_results.values()): + self.assertAlmostEqual(add_aux_op_res[idx][0].real, expected, places=6) + + def test_eval_op_dict(self): + """ Test evaluating a dict of additional operator """ + calc, res, aux_ops = self._setup_evaluation_operators() + # we filter the list because of simplicity + expected_results = {'number of particles': 2, + 's^2': 0, + 'magnetization': 0} + add_aux_op = aux_ops[0:3] + # now we convert it into a dictionary + add_aux_op = dict(zip(expected_results.keys(), add_aux_op)) + + # now we have the ground state calculation evaluate them + add_aux_op_res = calc.evaluate_operators(res.raw_result.eigenstate, add_aux_op) + self.assertIsInstance(add_aux_op_res, dict) + for name, expected in expected_results.items(): + self.assertAlmostEqual(add_aux_op_res[name][0].real, expected, places=6) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_particle_hole.py b/test/chemistry/test_particle_hole.py index 911d1c4f51..03f11d5687 100644 --- a/test/chemistry/test_particle_hole.py +++ b/test/chemistry/test_particle_hole.py @@ -12,6 +12,8 @@ """ Test Particle Hole """ +import unittest + from test.chemistry import QiskitChemistryTestCase from ddt import ddt, idata, unpack from qiskit.aqua.algorithms import NumPyMinimumEigensolver @@ -77,3 +79,7 @@ def test_particle_hole(self, atom, charge=0, spin=0, basis='sto3g', hf_method=HF self.assertAlmostEqual(result.eigenvalue.real, ph_result.eigenvalue.real - ph_shift, msg=config) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_qeom_ee.py b/test/chemistry/test_qeom_ee.py index 3d7a1de41d..1d6a813369 100644 --- a/test/chemistry/test_qeom_ee.py +++ b/test/chemistry/test_qeom_ee.py @@ -12,6 +12,7 @@ """ Test of Eom EE.""" +import warnings import unittest from test.aqua import QiskitAquaTestCase @@ -35,11 +36,13 @@ def setUp(self): pyscf_driver = PySCFDriver(atom=atom, unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') self.molecule = pyscf_driver.run() + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) exact_eigensolver = NumPyEigensolver(qubit_op, k=2 ** qubit_op.num_qubits) result = exact_eigensolver.run() @@ -51,11 +54,13 @@ def test_h2_four_qubits(self): """Test H2 with jordan wigner.""" two_qubit_reduction = False qubit_mapping = 'jordan_wigner' + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] @@ -71,11 +76,13 @@ def test_h2_two_qubits(self): two_qubit_reduction = True qubit_mapping = 'parity' + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] @@ -91,11 +98,13 @@ def test_h2_one_qubit(self): """Test H2 with tapering.""" two_qubit_reduction = False qubit_mapping = 'jordan_wigner' + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.JORDAN_WIGNER, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] diff --git a/test/chemistry/test_qeom_vqe.py b/test/chemistry/test_qeom_vqe.py index 31989122a1..d17cfc147a 100644 --- a/test/chemistry/test_qeom_vqe.py +++ b/test/chemistry/test_qeom_vqe.py @@ -12,6 +12,7 @@ """ Test of Eom VQE.""" +import warnings import unittest from test.aqua import QiskitAquaTestCase @@ -43,11 +44,13 @@ def setUp(self): pyscf_driver = PySCFDriver(atom=atom, unit=UnitsType.ANGSTROM, charge=0, spin=0, basis='sto3g') self.molecule = pyscf_driver.run() + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=True, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) exact_eigensolver = NumPyEigensolver(qubit_op, k=2 ** qubit_op.num_qubits) result = exact_eigensolver.run() @@ -59,11 +62,13 @@ def test_h2_two_qubits_statevector(self): """Test H2 with parity mapping and statevector backend.""" two_qubit_reduction = True qubit_mapping = 'parity' + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] @@ -91,11 +96,13 @@ def test_h2_one_qubit_statevector(self): """Test H2 with tapering and statevector backend.""" two_qubit_reduction = True qubit_mapping = 'parity' + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] @@ -130,11 +137,13 @@ def test_h2_one_qubit_qasm(self): """Test H2 with tapering and qasm backend""" two_qubit_reduction = True qubit_mapping = 'parity' + warnings.filterwarnings('ignore', category=DeprecationWarning) core = Hamiltonian(transformation=TransformationType.FULL, qubit_mapping=QubitMappingType.PARITY, two_qubit_reduction=two_qubit_reduction, freeze_core=False, orbital_reduction=[]) + warnings.filterwarnings('always', category=DeprecationWarning) qubit_op, _ = core.run(self.molecule) num_orbitals = core.molecule_info['num_orbitals'] diff --git a/test/chemistry/test_swaprz.py b/test/chemistry/test_swaprz.py index 55b0a078e3..4cf2419a0b 100644 --- a/test/chemistry/test_swaprz.py +++ b/test/chemistry/test_swaprz.py @@ -12,15 +12,19 @@ """Test of ExcitationPreserving from the circuit library.""" +import unittest from test.chemistry import QiskitChemistryTestCase + from qiskit import BasicAer -from qiskit.circuit.library import ExcitationPreserving from qiskit.aqua import QuantumInstance, aqua_globals from qiskit.aqua.algorithms import VQE from qiskit.aqua.components.optimizers import SLSQP from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.chemistry.core import QubitMappingType from qiskit.chemistry.drivers import HDF5Driver -from qiskit.chemistry.core import Hamiltonian, QubitMappingType +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver +from qiskit.chemistry.transformations import FermionicTransformation +from qiskit.circuit.library import ExcitationPreserving class TestExcitationPreserving(QiskitChemistryTestCase): @@ -41,22 +45,31 @@ def test_excitation_preserving(self): """Test the excitation preserving wavefunction on a chemistry example.""" driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) - qmolecule = driver.run() - operator = Hamiltonian(qubit_mapping=QubitMappingType.JORDAN_WIGNER, - two_qubit_reduction=False) - qubit_op, _ = operator.run(qmolecule) + fermionic_transformation = FermionicTransformation(qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False) + + qubit_op, _ = fermionic_transformation.transform(driver) optimizer = SLSQP(maxiter=100) - initial_state = HartreeFock(operator.molecule_info['num_orbitals'], - operator.molecule_info['num_particles'], - qubit_mapping=operator._qubit_mapping, - two_qubit_reduction=operator._two_qubit_reduction) + initial_state = HartreeFock( + fermionic_transformation.molecule_info['num_orbitals'], + fermionic_transformation.molecule_info['num_particles'], + qubit_mapping=fermionic_transformation._qubit_mapping, + two_qubit_reduction=fermionic_transformation._two_qubit_reduction) wavefunction = ExcitationPreserving(qubit_op.num_qubits, initial_state=initial_state) - algo = VQE(qubit_op, wavefunction, optimizer) - result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'), - seed_simulator=aqua_globals.random_seed, - seed_transpiler=aqua_globals.random_seed)) - result = operator.process_algorithm_result(result) - self.assertAlmostEqual(result.energy, self.reference_energy, places=6) + solver = VQE(var_form=wavefunction, optimizer=optimizer, + quantum_instance=QuantumInstance(BasicAer.get_backend('statevector_simulator'), + seed_simulator=aqua_globals.random_seed, + seed_transpiler=aqua_globals.random_seed)) + + gsc = GroundStateEigensolver(fermionic_transformation, solver) + + result = gsc.solve(driver) + + self.assertAlmostEqual(result.energy, self.reference_energy, places=4) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_symmetries.py b/test/chemistry/test_symmetries.py index 6fa6eb6c80..38a5a62aa9 100644 --- a/test/chemistry/test_symmetries.py +++ b/test/chemistry/test_symmetries.py @@ -12,17 +12,20 @@ """ Test of Symmetry UCCSD processing """ +import unittest + from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.aqua import QuantumInstance -from qiskit.aqua.operators import Z2Symmetries from qiskit.aqua.algorithms import VQE from qiskit.aqua.components.optimizers import SLSQP from qiskit.chemistry import QiskitChemistryError -from qiskit.chemistry.core import Hamiltonian, TransformationType, QubitMappingType from qiskit.chemistry.drivers import PySCFDriver, UnitsType from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.components.initial_states import HartreeFock +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.transformations import FermionicTransformation class TestSymmetries(QiskitChemistryTestCase): @@ -31,21 +34,25 @@ class TestSymmetries(QiskitChemistryTestCase): def setUp(self): super().setUp() try: - driver = PySCFDriver(atom='Li .0 .0 .0; H .0 .0 1.6', - unit=UnitsType.ANGSTROM, - charge=0, - spin=0, - basis='sto3g') + self.driver = PySCFDriver(atom='Li .0 .0 .0; H .0 .0 1.6', + unit=UnitsType.ANGSTROM, + charge=0, + spin=0, + basis='sto3g') except QiskitChemistryError: self.skipTest('PYSCF driver does not appear to be installed') - self.qmolecule = driver.run() - self.core = Hamiltonian(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=[]) - self.qubit_op, _ = self.core.run(self.qmolecule) - self.z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) + + self.fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=[], + z2symmetry_reduction='auto') + + self.qubit_op, _ = self.fermionic_transformation.transform(self.driver) + + self.z2_symmetries = self.fermionic_transformation.molecule_info.pop('z2_symmetries') self.reference_energy = -7.882096489442 @@ -69,34 +76,36 @@ def test_sq_list(self): def test_tapered_op(self): """ tapered op test """ - tapered_ops = self.z2_symmetries.taper(self.qubit_op) - smallest_idx = 0 # Prior knowledge of which tapered_op has ground state - the_tapered_op = tapered_ops[smallest_idx] optimizer = SLSQP(maxiter=1000) + init_state = HartreeFock( + num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, + num_particles=self.fermionic_transformation.molecule_info['num_particles'], + sq_list=self.z2_symmetries.sq_list) + + var_form = UCCSD( + num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], + num_particles=self.fermionic_transformation.molecule_info['num_particles'], + active_occupied=None, + active_unoccupied=None, + initial_state=init_state, + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, + num_time_slices=1, + z2_symmetries=self.z2_symmetries) + + solver = VQE(var_form=var_form, optimizer=optimizer, + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) - init_state = HartreeFock(num_orbitals=self.core._molecule_info['num_orbitals'], - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_particles=self.core._molecule_info['num_particles'], - sq_list=the_tapered_op.z2_symmetries.sq_list) - - var_form = UCCSD(num_orbitals=self.core._molecule_info['num_orbitals'], - num_particles=self.core._molecule_info['num_particles'], - active_occupied=None, active_unoccupied=None, - initial_state=init_state, - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_time_slices=1, - z2_symmetries=the_tapered_op.z2_symmetries) - - algo = VQE(the_tapered_op, var_form, optimizer) - - backend = BasicAer.get_backend('statevector_simulator') - quantum_instance = QuantumInstance(backend=backend) - - algo_result = algo.run(quantum_instance) + self.assertAlmostEqual(result.energy, self.reference_energy, places=6) - result = self.core.process_algorithm_result(algo_result) - self.assertAlmostEqual(result.energy, self.reference_energy, places=6) +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_uccsd_advanced.py b/test/chemistry/test_uccsd_advanced.py index a7ad73d483..6e13d0a8d8 100644 --- a/test/chemistry/test_uccsd_advanced.py +++ b/test/chemistry/test_uccsd_advanced.py @@ -12,17 +12,21 @@ """ Test of UCCSD and HartreeFock Aqua extensions """ +import unittest + from test.chemistry import QiskitChemistryTestCase from qiskit import BasicAer from qiskit.aqua import QuantumInstance -from qiskit.aqua.operators import Z2Symmetries -from qiskit.aqua.algorithms import VQE, NumPyMinimumEigensolver +from qiskit.aqua.algorithms import VQE from qiskit.aqua.components.optimizers import SLSQP from qiskit.chemistry import QiskitChemistryError from qiskit.chemistry.components.initial_states import HartreeFock from qiskit.chemistry.components.variational_forms import UCCSD from qiskit.chemistry.drivers import PySCFDriver, UnitsType -from qiskit.chemistry.core import Hamiltonian, QubitMappingType, TransformationType +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver +from qiskit.chemistry.core import TransformationType, QubitMappingType +from qiskit.chemistry.transformations import FermionicTransformation + # pylint: disable=invalid-name @@ -39,25 +43,13 @@ def setUp(self): charge=0, spin=0, basis='631g') - self.qmolecule = self.driver.run() - self.core = Hamiltonian(transformation=TransformationType.FULL, - qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True, - freeze_core=True, - orbital_reduction=[]) - self.qubit_op, _ = self.core.run(self.qmolecule) - - z2_symmetries = Z2Symmetries.find_Z2_symmetries(self.qubit_op) - tapered_ops = z2_symmetries.taper(self.qubit_op) - smallest_eig_value = 99999999999999 - smallest_idx = -1 - for idx, _ in enumerate(tapered_ops): - ee = NumPyMinimumEigensolver(tapered_ops[idx]) - curr_value = ee.compute_minimum_eigenvalue().eigenvalue.real - if curr_value < smallest_eig_value: - smallest_eig_value = curr_value - smallest_idx = idx - self.the_tapered_op = tapered_ops[smallest_idx] + self.fermionic_transformation = \ + FermionicTransformation(transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=[]) + self.qubit_op, _ = self.fermionic_transformation.transform(self.driver) self.reference_energy_pUCCD = -1.1434447924298028 self.reference_energy_UCCD0 = -1.1476045878481704 @@ -80,52 +72,67 @@ def test_uccsd_hf_qpUCCD(self): """ paired uccd test """ optimizer = SLSQP(maxiter=100) - initial_state = HartreeFock(self.core.molecule_info['num_orbitals'], - self.core.molecule_info['num_particles'], - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction) - - var_form = UCCSD(num_orbitals=self.core._molecule_info['num_orbitals'], - num_particles=self.core._molecule_info['num_particles'], - active_occupied=None, active_unoccupied=None, - initial_state=initial_state, - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_time_slices=1, - shallow_circuit_concat=False, - method_doubles='pucc', - excitation_type='d' - ) - - algo = VQE(self.qubit_op, var_form, optimizer) - result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) - result = self.core.process_algorithm_result(result) + + initial_state = HartreeFock( + self.fermionic_transformation.molecule_info['num_orbitals'], + self.fermionic_transformation.molecule_info['num_particles'], + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction) + + var_form = UCCSD( + num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], + num_particles=self.fermionic_transformation.molecule_info['num_particles'], + active_occupied=None, active_unoccupied=None, + initial_state=initial_state, + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, + num_time_slices=1, + shallow_circuit_concat=False, + method_doubles='pucc', + excitation_type='d' + ) + + solver = VQE(var_form=var_form, optimizer=optimizer, + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) + self.assertAlmostEqual(result.energy, self.reference_energy_pUCCD, places=6) def test_uccsd_hf_qUCCD0(self): """ singlet uccd test """ optimizer = SLSQP(maxiter=100) - initial_state = HartreeFock(self.core.molecule_info['num_orbitals'], - self.core.molecule_info['num_particles'], - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction) - - var_form = UCCSD(num_orbitals=self.core._molecule_info['num_orbitals'], - num_particles=self.core._molecule_info['num_particles'], - active_occupied=None, active_unoccupied=None, - initial_state=initial_state, - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_time_slices=1, - shallow_circuit_concat=False, - method_doubles='succ', - excitation_type='d' - ) - - algo = VQE(self.qubit_op, var_form, optimizer) - result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) - result = self.core.process_algorithm_result(result) + initial_state = HartreeFock( + self.fermionic_transformation.molecule_info['num_orbitals'], + self.fermionic_transformation.molecule_info['num_particles'], + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction) + + var_form = UCCSD( + num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], + num_particles=self.fermionic_transformation.molecule_info['num_particles'], + active_occupied=None, active_unoccupied=None, + initial_state=initial_state, + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, + num_time_slices=1, + shallow_circuit_concat=False, + method_doubles='succ', + excitation_type='d' + ) + + solver = VQE(var_form=var_form, optimizer=optimizer, + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) + self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0, places=6) def test_uccsd_hf_qUCCD0full(self): @@ -133,83 +140,108 @@ def test_uccsd_hf_qUCCD0full(self): optimizer = SLSQP(maxiter=100) - initial_state = HartreeFock(self.core.molecule_info['num_orbitals'], - self.core.molecule_info['num_particles'], - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction) - - var_form = UCCSD(num_orbitals=self.core._molecule_info['num_orbitals'], - num_particles=self.core._molecule_info['num_particles'], - active_occupied=None, active_unoccupied=None, - initial_state=initial_state, - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_time_slices=1, - shallow_circuit_concat=False, - method_doubles='succ_full', - excitation_type='d' - ) - - algo = VQE(self.qubit_op, var_form, optimizer) - result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) - result = self.core.process_algorithm_result(result) + initial_state = HartreeFock( + self.fermionic_transformation.molecule_info['num_orbitals'], + self.fermionic_transformation.molecule_info['num_particles'], + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction) + + var_form = UCCSD( + num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], + num_particles=self.fermionic_transformation.molecule_info['num_particles'], + active_occupied=None, active_unoccupied=None, + initial_state=initial_state, + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, + num_time_slices=1, + shallow_circuit_concat=False, + method_doubles='succ_full', + excitation_type='d' + ) + + solver = VQE(var_form=var_form, optimizer=optimizer, + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) + self.assertAlmostEqual(result.energy, self.reference_energy_UCCD0full, places=6) def test_uccsd_hf_qUCCSD(self): """ uccsd tapering test using all double excitations """ + fermionic_transformation = FermionicTransformation( + transformation=TransformationType.FULL, + qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=True, + freeze_core=True, + orbital_reduction=[], + z2symmetry_reduction='auto' + ) + + qubit_op, _ = fermionic_transformation.transform(self.driver) + # optimizer optimizer = SLSQP(maxiter=100) # initial state - init_state = HartreeFock(num_orbitals=self.core._molecule_info['num_orbitals'], - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_particles=self.core._molecule_info['num_particles'], - sq_list=self.the_tapered_op.z2_symmetries.sq_list) - - var_form = UCCSD(num_orbitals=self.core._molecule_info['num_orbitals'], - num_particles=self.core._molecule_info['num_particles'], - active_occupied=None, active_unoccupied=None, - initial_state=init_state, - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_time_slices=1, - z2_symmetries=self.the_tapered_op.z2_symmetries, - shallow_circuit_concat=False, - method_doubles='ucc', - excitation_type='sd', - skip_commute_test=True) - - algo = VQE(self.the_tapered_op, var_form, optimizer) - - result = algo.run(QuantumInstance(BasicAer.get_backend('statevector_simulator'))) - result = self.core.process_algorithm_result(result) + init_state = HartreeFock( + num_orbitals=fermionic_transformation.molecule_info['num_orbitals'], + qubit_mapping=fermionic_transformation._qubit_mapping, + two_qubit_reduction=fermionic_transformation._two_qubit_reduction, + num_particles=fermionic_transformation.molecule_info['num_particles'], + sq_list=fermionic_transformation.molecule_info['z2_symmetries'].sq_list) + + var_form = UCCSD( + num_orbitals=fermionic_transformation.molecule_info['num_orbitals'], + num_particles=fermionic_transformation.molecule_info['num_particles'], + active_occupied=None, active_unoccupied=None, + initial_state=init_state, + qubit_mapping=fermionic_transformation._qubit_mapping, + two_qubit_reduction=fermionic_transformation._two_qubit_reduction, + num_time_slices=1, + z2_symmetries=fermionic_transformation.molecule_info['z2_symmetries'], + shallow_circuit_concat=False, + method_doubles='ucc', + excitation_type='sd', + skip_commute_test=True) + + solver = VQE(var_form=var_form, optimizer=optimizer, + quantum_instance=QuantumInstance( + backend=BasicAer.get_backend('statevector_simulator'))) + + raw_result = solver.compute_minimum_eigenvalue(qubit_op, None) + result = fermionic_transformation.interpret(raw_result) + self.assertAlmostEqual(result.energy, self.reference_energy_UCCSD, places=6) def test_uccsd_hf_excitations(self): """ uccsd tapering test using all double excitations """ # initial state - init_state = HartreeFock(num_orbitals=self.core._molecule_info['num_orbitals'], - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_particles=self.core._molecule_info['num_particles'], - sq_list=self.the_tapered_op.z2_symmetries.sq_list) + init_state = HartreeFock( + num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, + num_particles=self.fermionic_transformation.molecule_info['num_particles'], + sq_list=self.fermionic_transformation.molecule_info['z2_symmetries'].sq_list) # check singlet excitations - var_form = UCCSD(num_orbitals=self.core._molecule_info['num_orbitals'], - num_particles=self.core._molecule_info['num_particles'], - active_occupied=None, active_unoccupied=None, - initial_state=init_state, - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_time_slices=1, - z2_symmetries=self.the_tapered_op.z2_symmetries, - shallow_circuit_concat=False, - method_doubles='succ', - excitation_type='d', - skip_commute_test=True) + var_form = UCCSD( + num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], + num_particles=self.fermionic_transformation.molecule_info['num_particles'], + active_occupied=None, active_unoccupied=None, + initial_state=init_state, + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, + num_time_slices=1, + z2_symmetries=self.fermionic_transformation.molecule_info['z2_symmetries'], + shallow_circuit_concat=False, + method_doubles='succ', + excitation_type='d', + skip_commute_test=True) double_excitations_singlet = var_form._double_excitations res = TestUCCSDHartreeFock.excitation_lists_comparator( @@ -217,18 +249,19 @@ def test_uccsd_hf_excitations(self): self.assertEqual(res, True) # check grouped singlet excitations - var_form = UCCSD(num_orbitals=self.core._molecule_info['num_orbitals'], - num_particles=self.core._molecule_info['num_particles'], - active_occupied=None, active_unoccupied=None, - initial_state=init_state, - qubit_mapping=self.core._qubit_mapping, - two_qubit_reduction=self.core._two_qubit_reduction, - num_time_slices=1, - z2_symmetries=self.the_tapered_op.z2_symmetries, - shallow_circuit_concat=False, - method_doubles='succ_full', - excitation_type='d', - skip_commute_test=True) + var_form = UCCSD( + num_orbitals=self.fermionic_transformation.molecule_info['num_orbitals'], + num_particles=self.fermionic_transformation.molecule_info['num_particles'], + active_occupied=None, active_unoccupied=None, + initial_state=init_state, + qubit_mapping=self.fermionic_transformation._qubit_mapping, + two_qubit_reduction=self.fermionic_transformation._two_qubit_reduction, + num_time_slices=1, + z2_symmetries=self.fermionic_transformation.molecule_info['z2_symmetries'], + shallow_circuit_concat=False, + method_doubles='succ_full', + excitation_type='d', + skip_commute_test=True) double_excitations_singlet_grouped = var_form._double_excitations_grouped res_groups = TestUCCSDHartreeFock.group_excitation_lists_comparator( @@ -312,3 +345,7 @@ def group_excitation_lists_comparator(glist1, glist2): counter += 1 return bool(counter == number_groups) + + +if __name__ == '__main__': + unittest.main() diff --git a/test/chemistry/test_uccsd_hartree_fock.py b/test/chemistry/test_uccsd_hartree_fock.py index f01b271c3a..5a94152f9e 100644 --- a/test/chemistry/test_uccsd_hartree_fock.py +++ b/test/chemistry/test_uccsd_hartree_fock.py @@ -11,10 +11,9 @@ # that they have been altered from the originals. """ Test of UCCSD and HartreeFock Aqua extensions """ -from test.chemistry import QiskitChemistryTestCase +from test.chemistry import QiskitChemistryTestCase from ddt import ddt, idata, unpack - from qiskit import BasicAer from qiskit.aqua import QuantumInstance, aqua_globals from qiskit.aqua.algorithms import VQE @@ -22,8 +21,10 @@ from qiskit.aqua.operators import AerPauliExpectation, PauliExpectation from qiskit.chemistry.components.initial_states import HartreeFock from qiskit.chemistry.components.variational_forms import UCCSD +from qiskit.chemistry.core import QubitMappingType from qiskit.chemistry.drivers import HDF5Driver -from qiskit.chemistry.core import Hamiltonian, QubitMappingType +from qiskit.chemistry.algorithms.ground_state_solvers import GroundStateEigensolver +from qiskit.chemistry.transformations import FermionicTransformation @ddt @@ -37,41 +38,52 @@ def setUp(self): self.seed = 700 aqua_globals.random_seed = self.seed - driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) - qmolecule = driver.run() - core = Hamiltonian(qubit_mapping=QubitMappingType.PARITY, - two_qubit_reduction=True) - self.qubit_op, _ = core.run(qmolecule) - self.core = core + self.driver = HDF5Driver(self.get_resource_path('test_driver_hdf5.hdf5')) + fermionic_transformation = FermionicTransformation(qubit_mapping=QubitMappingType.PARITY, + two_qubit_reduction=False) + + self.qubit_op, _ = fermionic_transformation.transform(self.driver) + self.fermionic_transformation = fermionic_transformation self.optimizer = SLSQP(maxiter=100) - initial_state = HartreeFock(core.molecule_info['num_orbitals'], - core.molecule_info['num_particles'], - qubit_mapping=core._qubit_mapping, - two_qubit_reduction=core._two_qubit_reduction) - self.var_form = UCCSD(num_orbitals=core.molecule_info['num_orbitals'], - num_particles=core.molecule_info['num_particles'], - initial_state=initial_state, - qubit_mapping=core._qubit_mapping, - two_qubit_reduction=core._two_qubit_reduction) + initial_state = HartreeFock( + fermionic_transformation.molecule_info['num_orbitals'], + fermionic_transformation.molecule_info['num_particles'], + qubit_mapping=fermionic_transformation._qubit_mapping, + two_qubit_reduction=fermionic_transformation._two_qubit_reduction) + self.var_form = UCCSD( + num_orbitals=fermionic_transformation.molecule_info['num_orbitals'], + num_particles=fermionic_transformation.molecule_info['num_particles'], + initial_state=initial_state, + qubit_mapping=fermionic_transformation._qubit_mapping, + two_qubit_reduction=fermionic_transformation._two_qubit_reduction) def test_uccsd_hf(self): """ uccsd hf test """ backend = BasicAer.get_backend('statevector_simulator') - algo = VQE(self.qubit_op, self.var_form, self.optimizer) - result = algo.run(QuantumInstance(backend)) - result = self.core.process_algorithm_result(result) + solver = VQE(var_form=self.var_form, optimizer=self.optimizer, + quantum_instance=QuantumInstance(backend=backend)) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) + self.assertAlmostEqual(result.energy, self.reference_energy, places=6) def test_uccsd_hf_qasm(self): """ uccsd hf test with qasm_simulator. """ backend = BasicAer.get_backend('qasm_simulator') optimizer = SPSA(maxiter=200, last_avg=5) - algo = VQE(self.qubit_op, self.var_form, optimizer, expectation=PauliExpectation()) - result = algo.run(QuantumInstance(backend, - seed_simulator=aqua_globals.random_seed, - seed_transpiler=aqua_globals.random_seed)) - result = self.core.process_algorithm_result(result) + solver = VQE(var_form=self.var_form, optimizer=optimizer, + expectation=PauliExpectation(), + quantum_instance=QuantumInstance(backend=backend, + seed_simulator=aqua_globals.random_seed, + seed_transpiler=aqua_globals.random_seed)) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) + self.assertAlmostEqual(result.energy, -1.138, places=2) def test_uccsd_hf_aer_statevector(self): @@ -83,9 +95,13 @@ def test_uccsd_hf_aer_statevector(self): self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return backend = Aer.get_backend('statevector_simulator') - algo = VQE(self.qubit_op, self.var_form, self.optimizer) - result = algo.run(QuantumInstance(backend)) - result = self.core.process_algorithm_result(result) + solver = VQE(var_form=self.var_form, optimizer=self.optimizer, + quantum_instance=QuantumInstance(backend=backend)) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) + self.assertAlmostEqual(result.energy, self.reference_energy, places=6) def test_uccsd_hf_aer_qasm(self): @@ -98,11 +114,16 @@ def test_uccsd_hf_aer_qasm(self): return backend = Aer.get_backend('qasm_simulator') optimizer = SPSA(maxiter=200, last_avg=5) - algo = VQE(self.qubit_op, self.var_form, optimizer, expectation=PauliExpectation()) - result = algo.run(QuantumInstance(backend, - seed_simulator=aqua_globals.random_seed, - seed_transpiler=aqua_globals.random_seed)) - result = self.core.process_algorithm_result(result) + solver = VQE(var_form=self.var_form, optimizer=optimizer, + expectation=PauliExpectation(), + quantum_instance=QuantumInstance(backend=backend, + seed_simulator=aqua_globals.random_seed, + seed_transpiler=aqua_globals.random_seed)) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) + self.assertAlmostEqual(result.energy, -1.138, places=2) def test_uccsd_hf_aer_qasm_snapshot(self): @@ -114,10 +135,15 @@ def test_uccsd_hf_aer_qasm_snapshot(self): self.skipTest("Aer doesn't appear to be installed. Error: '{}'".format(str(ex))) return backend = Aer.get_backend('qasm_simulator') - algo = VQE(self.qubit_op, self.var_form, self.optimizer, expectation=AerPauliExpectation()) - result = algo.run(QuantumInstance(backend)) - result = self.core.process_algorithm_result(result) - self.assertAlmostEqual(result.energy, self.reference_energy, places=6) + optimizer = SPSA(maxiter=200, last_avg=5) + solver = VQE(var_form=self.var_form, optimizer=optimizer, + expectation=AerPauliExpectation(), + quantum_instance=QuantumInstance(backend=backend)) + + gsc = GroundStateEigensolver(self.fermionic_transformation, solver) + + result = gsc.solve(self.driver) + self.assertAlmostEqual(result.energy, self.reference_energy, places=3) EXCITATION_RESULTS = \ [[[[0, 1], [0, 2], [3, 4], [3, 5]], diff --git a/test/chemistry/test_vqe_uccsd_adapt.py b/test/chemistry/test_vqe_uccsd_adapt.py index 7df98b7e82..af2eee5387 100644 --- a/test/chemistry/test_vqe_uccsd_adapt.py +++ b/test/chemistry/test_vqe_uccsd_adapt.py @@ -12,6 +12,7 @@ """ Test of the Adaptive VQE implementation with the adaptive UCCSD variational form """ +import warnings import unittest from test.chemistry import QiskitChemistryTestCase @@ -29,6 +30,7 @@ class TestVQEAdaptUCCSD(QiskitChemistryTestCase): """ Test Adaptive VQE with UCCSD""" + def setUp(self): super().setUp() # np.random.seed(50) @@ -77,14 +79,19 @@ def test_vqe_adapt(self): self.num_particles, initial_state=self.init_state) backend = Aer.get_backend('statevector_simulator') optimizer = L_BFGS_B() + + warnings.filterwarnings('ignore', category=DeprecationWarning) algorithm = VQEAdapt(self.qubit_op, self.var_form_base, optimizer, threshold=0.00001, delta=0.1, max_iterations=1) + warnings.filterwarnings('always', category=DeprecationWarning) result = algorithm.run(backend) self.assertEqual(result.num_iterations, 1) self.assertEqual(result.finishing_criterion, 'Maximum number of iterations reached') + warnings.filterwarnings('ignore', category=DeprecationWarning) algorithm = VQEAdapt(self.qubit_op, self.var_form_base, optimizer, threshold=0.00001, delta=0.1) + warnings.filterwarnings('always', category=DeprecationWarning) result = algorithm.run(backend) self.assertAlmostEqual(result.eigenvalue.real, -1.85727503, places=2) self.assertEqual(result.num_iterations, 2)